UDP协议在Windows上使用示例

news2024/10/7 12:25:57

      UDP(User Datagram Protocol,用户数据报协议)是无连接的,因此在两个进程通信前没有握手过程UDP协议提供一种不可靠数据传送服务,也就是说,当进程将一个报文发送进UDP套接字时,UDP协议并不保证该报文将到达接收进程。不仅如此,到达接收进程的报文也可能是乱序到达的。从一个端系统向另一个端系统发送独立的数据分组,不对交付提供任何保证。运行在不同机器上的进程彼此通过向套接字(socket)发送报文(message)来进行通信。UDP是一种不提供不必要服务的轻量级运输协议,它仅提供最小服务。
      UDP没有包括拥塞控制机制,所以UDP的发送端可以用它选定的任何速率向其下层(网络层)注入数据。(然而,值得注意的是实际端到端吞吐量可能小于该速率,这可能是因为中间链路的带宽受限或因为拥塞而造成的。)
      使用UDP套接字的两个通信进程之间的交互:在发送进程能够将数据分组推出套接字之门之前,当使用UDP时,必须先将目的地址附在该分组之上。在该分组传过发送方的套接字之后,因特网将使用该目的地址通过因特网为该分组选路到接收进程的套接字。当分组到达接收套接字时,接收进程将通过该套接字取回分组,然后检查分组的内容并采取适当的动作。附在分组上的目的地址包含了什么?目的主机的IP地址是目的地址的一部分。通过在分组中包括目的地的IP地址,因特网中的路由器将能够通过因特网分组选路到目的主机。但是因为一台主机可能运行许多网络应用进程,每个进程具有一个或多个套接字,所以在目的主机指定特定的套接字也是必要的。当生成一个套接字时,就为它分配一个称为端口号(port number)的标识符。因此,分组的目的地址也包括该套接字的端口号。总的来说,发送进程为分组附上目的地址,该目的地址是由目的主机的IP地址和目的地套接字的端口号组成的。此外,发送方的源地址也是由源主机的IP地址和源套接字的端口号组成,该源地址也要附在分组之上。然而,将源地址附在分组之上通常并不是由UDP应用程序代码所为,而是由底层操作系统自动完成的。
      使用UDP的客户----服务器应用程序如下图所示:在客户发送其报文之前,服务器必须作为一个进程正在运行。TCP在开始数据传输之前要经过三次握手,UDP却不需要任何准备即可进行数据传输。因此UDP不会引入建立连接的时延。

      注:以上内容来自《计算机网络自顶向下方法(原书第7版)》

      以下是测试代码:参考:https://www.binarytides.com/udp-socket-programming-in-winsock/

     服务器端:

int test_socket_udp_server()
{
#ifdef _MSC_VER
	// Initialise winsock
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
		fprintf(stderr, "Failed. Error Code : %d", WSAGetLastError());
		return -1;
	}

	// Create a socket
	SOCKET s;
	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
		fprintf(stderr, "Could not create socket : %d", WSAGetLastError());
		return -1;
	}

	// Prepare the sockaddr_in structure
	struct sockaddr_in server, si_other;
	server.sin_family = AF_INET;
	server.sin_addr.s_addr = INADDR_ANY;
	server.sin_port = htons(SERVER_PORT);

	// Bind
	if (bind(s, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) {
		fprintf(stderr, "Bind failed with error code : %d", WSAGetLastError());
		return -1;
	}

	// keep listening for data
	int slen = sizeof(si_other);
	int recv_len;
	char buf[BUFFER_MAX_LEN];

	while (1) {
		fprintf(stdout, "Waiting for data...");
		fflush(stdout);

		// clear the buffer by filling null, it might have previously received data
		memset(buf, '\0', BUFFER_MAX_LEN);

		//try to receive some data, this is a blocking call
		if ((recv_len = recvfrom(s, buf, BUFFER_MAX_LEN, 0, (struct sockaddr*)&si_other, &slen)) == SOCKET_ERROR) {
			fprintf(stderr, "recvfrom failed with error code : %d", WSAGetLastError());
			return -1;
		}

		// print details of the client/peer and the data received
		fprintf(stdout, "Received packet from %s:%d\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port));
		fprintf(stdout, "Data: %s\n", buf);

		int i = 0;
		while (buf[i]) {
			buf[i] = toupper(buf[i]);
			++i;
		}

		// now reply the client with the same data
		if (sendto(s, buf, recv_len, 0, (struct sockaddr*)&si_other, slen) == SOCKET_ERROR) {
			fprintf(stderr, "sendto failed with error code : %d", WSAGetLastError());
			return -1;
		}
	}

	closesocket(s);
	WSACleanup();
	return 0;
#else
	fprintf(stderr, "the Linux platform is not yet implemented\n");
	return -1;
#endif
}

      客户端:

int test_socket_udp_client()
{
#ifdef _MSC_VER
	// Initialise winsock
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
		fprintf(stderr, "Failed. Error Code : %d", WSAGetLastError());
		return -1;
	}

	// create socket
	SOCKET s;
	if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == SOCKET_ERROR) {
		fprintf(stderr, "socket failed with error code : %d", WSAGetLastError());
		return -1;
	}

	// setup address structure
	struct sockaddr_in si_other;
	int slen = sizeof(si_other);
	memset((char*)&si_other, 0, sizeof(si_other));
	si_other.sin_family = AF_INET;
	si_other.sin_port = htons(SERVER_PORT);
	si_other.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);

	char buf[BUFFER_MAX_LEN];
	char message[BUFFER_MAX_LEN];

	// start communication
	while (1) {
		fprintf(stdout, "Enter message : ");
		gets_s(message);

		// send the message
		if (sendto(s, message, strlen(message), 0, (struct sockaddr*)&si_other, slen) == SOCKET_ERROR) {
			fprintf(stderr, "sendto failed with error code : %d", WSAGetLastError());
			return -1;
		}

		// receive a reply and print it
		// clear the buffer by filling null, it might have previously received data
		memset(buf, '\0', BUFFER_MAX_LEN);
		// try to receive some data, this is a blocking call
		if (recvfrom(s, buf, BUFFER_MAX_LEN, 0, (struct sockaddr*)&si_other, &slen) == SOCKET_ERROR) {
			fprintf(stderr, "recvfrom failed with error code : %d", WSAGetLastError());
			return -1;
		}

		puts(buf);
	}

	closesocket(s);
	WSACleanup();
	return 0;
#else
	fprintf(stderr, "the Linux platform is not yet implemented\n");
	return -1;
#endif
}

      服务器端IP地址和端口号的设置如下:

#define SERVER_IP "127.0.0.1" // ip address of udp server
#define BUFFER_MAX_LEN 512 // Max length of buffer
#define SERVER_PORT 12345 // The port on which to listen for incoming data

      recvfrom函数声明如下:用于接收数据报(datagram)

int recvfrom(
  [in]                SOCKET   s,
  [out]               char     *buf,
  [in]                int      len,
  [in]                int      flags,
  [out]               sockaddr *from,
  [in, out, optional] int      *fromlen
);

      参数介绍:
      (1).s:绑定socket的描述符(descriptor)。
      (2).buf:传入数据的buffer.
      (3).len:buf参数指向的buffer的长度(以字节为单位).
      (4).flags:用于修改函数调用的行为。
      (5).from:指向sockaddr结构中的buffer的指针。
      (6).fromlen:from参数指向的buffer的大小(以字节为单位).
      如果没有发生错误,recvfrom函数返回接收的字节数。如果连接已正常关闭,则返回值为0.否则将返回SOCKET_ERROR,并且可以通过调用WSAGetLastError函数获取错误码。
      recvfrom函数读取已连接和未连接socket上的传入数据(incoming data),并捕获发送数据的地址。此函数通常用于无连接socket.必须知道socket的本地地址(local address)。对于server应用程序,这通常通过bind函数显式地完成。client应用程序不建议使用显式绑定。对于使用此函数的client应用程序,socket可以通过sendto, WSASendTo或WSAJoinLeaf隐式绑定到本地地址。
      对于UDP,如果接收到的数据包(packet)不包含数据(空),则recvfrom函数将返回0.
      如果from参数非0,并且socket不是面向连接的(例如类型为SOCK_DGRAM),则发送数据的对等方的网络地址(network address of the peer)将复制到相应的sockaddr结构中。fromlen所指向的值被初始化为该结构的大小,并在返回时被修改,以指示存储在sockaddr结构中的地址的实际大小。
      如果socket上没有可用的传入数据,则recvfrom函数将根据为WSARecv定义的阻塞规则阻塞并等待数据到达,除非socket是非阻塞的,否则不设置MSG_PARTIAL标志。

      sendto函数声明如下:用于发送数据

int sendto(
  [in] SOCKET         s,
  [in] const char     *buf,
  [in] int            len,
  [in] int            flags,
  [in] const sockaddr *to,
  [in] int            tolen
);

      参数介绍:
      (1).s:socket的描述符。
      (2).buf:要传输数据的buffer的指针。
      (3).len:buf参数指向的数据的长度(以字节为单位)。
      (4).flags:用于指定调用的方式.
      (5).to:指向scokaddr结构的指针,该结构包含目标socket的地址。
      (6).tolen:to参数指向的地址的大小(以字节为单位)。
      如果没有发生错误,sendto将返回发送的总字节数,这可能小于len所指示的值。否则,将返回SOCKET_ERROR,并且可以通过调用WSAGetLastError函数获取错误码。
      to参数可以是socket地址族中的任何有效地址,包括广播地址和组播地址(broadcast or any multicast address)。要发送到广播地址,应用程序必须使用启用了SO_BROADCAST的setsockopt。否则,sendto将失败,错误码为WSAEACCES。
      如果打开socket,进行setsockopt调用,然后进行sendto调用,则Windows Sockets将执行隐式绑定函数调用。
      如果socket未绑定(unbound),则系统将为本地关联分配唯一值,然后将socket标记为绑定。
      sendto的成功完成并不表示数据已成功传递。
      sendto函数通常用于无连接socket,将数据报发送到由to参数标识的特定对等方socket。即使无连接socket以前已连接到特定地址,to参数也仅覆盖该特定数据报的目标地址。

      以上测试代码实现的功能是,客户端发送字符串,服务器端接收后将其转换为大写字母后再发送,执行结果如下图所示:先运行服务器端程序,左半图,release模式;右半图为debug下的客户端程序

      GitHub: https://github.com/fengbingchun/OpenSSL_Test

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/114548.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

过孔基础常识

过孔,一个绝大多数硬件工程师都听说过,但又并非真正了解的名词。了解的都知道,其在PCB板中其着至关重要的的作用。没有过孔的存在,很难画出一块完美的PCB板。所以呢,小编今日就带大家了解了解什么是过孔。 什么是过孔…

FCN代码及效果展示

1. 代码获取 代码地址: https://github.com/Le0v1n/ml_code/tree/main/Segmentation/FCN 2. 从头开始训练 2.1 测试平台 GPU:NVIDIA RTX 3070CPU: Intel I5-10400FRAM: 16GBOS: Windows 11Dataset: VOC2012Class num: 21(201)Batch size: 4Learning Rate: 0.1Ep…

嘉兴经开区第四届创新创业大赛总决赛成功举办

12月21日至12月22日,嘉兴经济技术开发区第四届创新创业大赛总决赛成功举办,经过激烈角逐最后共有10家企业分别获得大赛初创组和成长组的一二三等奖。 总决赛现场 嘉兴经开区第四届中国创新创业大赛于6月正式启动,陆续在嘉兴、成都、北京、西…

【详细学习SpringBoot源码之内嵌Tomcat启动原理分析编译部署Tomcat源码过程解析-9】

一.知识回顾 【0.SpringBoot专栏的相关文章都在这里哟,后续更多的文章内容可以点击查看】 【1.SpringBoot初识之Spring注解发展流程以及常用的Spring和SpringBoot注解】 【2.SpringBoot自动装配之SPI机制&SPI案例实操学习&SPI机制核心源码学习】 【3.详细学…

12-RabbitMq概述与工作模式深度剖析

MQ概述 MQ全称 Message Queue(消息队列),是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。 MQ 的优势 应用解耦:提高系统容错性和可维护性 异步提速:提升用户体验和系统吞吐量 削峰填谷&#xff1…

unity中使用代码接绘制三维模型

一 模型的构成 在三维世界中,绘制一个模型并不是什么很复杂的问题。只要知道了基本原理一切需求便迎刃而解。 如下图所示,任何模型都是由点线面构成的,而面的最小单位是三角形。 任何一个多边形的面,都是由多个三角形构成的。比…

Web前端105天-day64-HTML5_CORE

HTML5CORE04 目录 前言 一、复习 二、WebSocket 三、服务器搭建 四、聊天室 五、defineProperty 5.1.初识defineProperty 5.2.配置多个属性 5.3.可配置 5.4.赋值监听 5.5.练习 5.6.计算属性 总结 前言 HTML5CORE04学习开始 一、复习 SVG: 利用HTML的 DOM 来绘制图…

PCB贴片机如何送料?

1.常见的贴片机供料器四种形式 http://www.sz-bjzn.com/1547.html 2.模块化设计SMT贴片机送料器的操作方法 3.淘宝 https://item.taobao.com/item.htm?spma230r.1.14.98.33e41823OZ1zzn&id579043582781&ns1&abbucket20#detail 不错:https://item.tao…

distinct与group by 去重

distinct与group by 去重distinct 特点:group by 特点:总结:mysql中常用去重复数据的方法是使用 distinct 或者group by ,以上2种均能实现,但也有不同的地方。distinct 特点: 1、distinct 只能放在查询字段…

重新更新anaconda

更新anaconda问题阐述问题分析打开Anaconda Nvaigator打开文件所在位置复制文件所在路径找到此电脑或者打开设置找到高级系统设置环境变量添加环境变量打开scripts文件修改成功再一次启动感谢观看今天手贱,不小心删掉的anaconda,我想一不做二不休,直接重新重装了,就找到了anaco…

经典SQL语句大全(基础、提升、技巧、数据开发、基本函数)

目录 前言 正文 第一章:基础 第二章:提升 第三章:技巧 第四章:数据开发-经典​​​​​​​ 第五章:SQL Server基本函数 第六章:常识 第七章:SQLServer2000 同步复制技术实现步骤 总结…

juc-4-synchronized原理

目录 1、synchronized 作用于静态方法 总结 ​编辑 案例 静态成员变量 (synchronized锁非静态方法) 案例 静态成员变量 (synchronized锁静态方法 或 直接锁类) 2、监视器锁(monitor) 2.1 synchronized怎么实现的线程安全呢? 3、JDK6 synchronized 的优化 3.1 C…

互联网技术不再是统领,当下正在发生着一场深刻的变革

拥抱实体经济,绝对是当下互联网玩家们的首要选择。无论是头部的互联网企业来讲,还是新生的互联网玩家而言,它们都不约而同地将关注的焦点聚焦在了这样一个方向上。   透过这一点,我们可以非常明显地感受到,一个全新的…

圣诞节,记录前行中跨过的2022

2022年,我人生的第二十四年,是我大学生活的最后一年,是我职场生涯的第一年,这一年从学生到打工人,从实习生到职场员工,变化了许多,做了许多,收获了许多,同时也成长了许多…

m自适应FSK解调系统误码率matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 FSK信号的解调也有非相干和相干两种,FSK信号可以看作是用两个频率源交替传输得到的,所以FSK的接收机由两个并联的ASK接收机组成。 (1)相干解调 相干解调是利用乘法器&…

Minecraft(我的世界) Fabric 1.19.3 服务器搭建教程

Debian系统使用MCSManager9面板搭建MC Java版MOD服务器的教程,本教程用的Fabric1.19.3服务端,用其他服务端的也可以参考一下。 视频教程:https://www.bilibili.com/video/BV1Zd4y1h7zG/ 我的世界(MC) Fabric 1.19.3 开服教程,新手…

IDEA Git 选项栏各项功能详解

IDEA Git 选项栏各项功能详解 如图所示 Copy Revision Number 顾名思义 拷贝当前版本号 到剪切板 a8e4b86ce9ca01968629504a6e19b4b99d76a853 Create Patch 在 git 日志中选择要创建补丁的commit,右键选择Create Patch...同一个文件在多次commit中都存在&#xff…

DSP-离散时间系统

目录 概念: 累加器(Accumulator): N点滑动滤波器: 离散时间系统的分类 : 线性系统 Linear System 移不变系统 Shift-Invariant Systems 因果系统 Causal System 稳定系统 Stable System 无源(passive&#x…

蚂蚁感冒(第五届蓝桥杯省赛C++A/B组)

目录 题目详细:​编辑 题目思路: 两种情况: 代码详解: 题目详细: 题目思路: 这个题目的关键在于对蚂蚁相遇 的时候情况的看待 两个蚂蚁相遇时候的情况 我们可以看作 他们相互穿过了彼此 假设相遇…

基于CNN的MINIST手写数字识别项目代码以及原理详解

文章目录项目简介项目开发软件环境项目开发硬件环境前言一、数据加载的作用二、Pytorch进行数据加载所需工具2.1 Dataset2.2 Dataloader2.3 Torchvision2.4 Torchtext2.5 加载项目需要使用的库三、加载MINIST数据集3.1 数据集简介3.2 数据预处理3.3 加载数据集四、模型构建五、…