• UDP网络编程


    UDP网络编程

    1.UDP协议简介

      UDP协议采用无连接的方式,不管发送的数据包是否到达目的主机,数据包是否出错。收到数据包的主机也不会告诉发送方是否正确收到了数据,它的可靠性是由上层协议来保障的。
      UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection,开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETF RFC 768 [1] 是UDP的正式规范。UDP在IP报文的协议号是17。
      UDP是无连接的服务。在无连接服务的情况下,两个实体之间的通信不需先建立好一个连接,因此其下层的有关资源不需要事先进行预定保留。这些资源将在数据传输时动态地进行分配。无连接服务的另一特征就是它不需要通信的两个实体同时是活跃的(即处于激活态)。当发送端的实体正在进行发送时,它才必须是活跃的。优点是灵活方便和比较迅速,但不能防止报文的丢失、重复或失序,特别适合于传送少量零星的报文。
      UDP报文没有可靠性保证、顺序保证和流量控制字段等,可靠性较差。但是正因为UDP协议的控制选项较少,在数据传输过程中延迟小、数据传输效率高,适合对可靠性要求不高的应用程序,或者可以保障可靠性的应用程序,如DNS、TFTP、SNMP等。
    在这里插入图片描述
      UDP和TCP协议的主要区别是两者在如何实现信息的可靠传递方面不同。TCP协议中包含了专门的传递保证机制,当数据接收方收到发送方传来的信息时,会自动向发送方发出确认消息;发送方只有在接收到该确认消息之后才继续传送其它信息,否则将一直等待直到收到确认信息为止。与TCP不同,UDP协议并不提供数据传送的保证机制。如果在从发送方到接收方的传递过程中出现数据包的丢失,协议本身并不能做出任何检测或提示。因此,通常人们把UDP协议称为不可靠的传输协议。

    2.UDP通讯流程

    在这里插入图片描述
      一般在UDP通讯中我们不太区分服务端和客户端,由于UDP通讯不需要建立连接,因此UDP通讯中主要称为发送方和接收方。

    • 发送方创建过程:

      1.创建网络套接字socket
      2.发送数据sendto

    • 接收方创建过程:

      1.创建网络套接字socket
      2.绑定端口号
      3.接收数据recvfrom

    2.1 函数接口

    #include
    #include
    发送数据
    ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
    形参: sockfd --套接字,socket函数返回值
       buf – 要发送是内容
       len --要发送的数据长度
       flags --一般填0即可
       dest_addr、addrlen —和connect后两个参数类似
       dest_addr —对方网络结构体信息
        addrlen --dest_addr结构体大小
    返回值: 成功返回发送字节数,失败返回-1
    接收数据
    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
    形参: sockfd --套接字,socket函数返回值
        buf – 读取内容存放地址
        len --要读取的数据长度
        flags --一般填0即可
        src_addr、addrlen —和accept后两个参数类似
        src_addr —保存发送者的IP和端口号
        addrlen —src_addr结构体大小
    返回值: 成功返回读取到的字节数,失败返回-1;

    • 发送方示例
    #include           /* See NOTES */
    #include 
    #include 
    #include 
    #include  /* superset of previous */
    #include 
    #include 
    #include 
    int main(int argc,char *argv[])
    {
    	if(argc!=3)
    	{
    		printf("格式:./a.out <端口号> \n");
    		return 0;
    	}
    	int sockfd=socket(AF_INET,SOCK_DGRAM,0);	
    	if(sockfd==-1)
    	{
    		printf("创建网络套接字失败\n");
    		return 0;
    	}
    	struct sockaddr_in s_addr=
    	{
    		.sin_family=AF_INET,
    		.sin_port=htons(atoi(argv[1])),
    		.sin_addr.s_addr=inet_addr(argv[2]),//本地所有IP
    	};
    	char buff[]="UDP发送数据测试!";
    	ssize_t size;
    	while(1)
    	{
    		size=sendto(sockfd,buff,sizeof(buff),0,( const struct sockaddr * )&s_addr,sizeof(s_addr));
    		printf("发送数据成功size=%ld\n",size);
    		sleep(1);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 接收方示例
    #include 
    #include           /* See NOTES */
    #include 
    #include 
    #include  /* superset of previous */
    #include 
    #include 
    #include 
    int main(int argc,char *argv[])
    {
    	if(argc!=2)
    	{
    		printf("./a.out <端口号>\n");
    		return 0;
    	}
    	/*1.创建网络套接字*/
    	int sockfd=socket(AF_INET,SOCK_DGRAM, 0);
    	if(sockfd==-1)
    	{
    		printf("创建UDP网络套接字失败\n");
    		return 0;
    	}
    	/*2.绑定端口号*/
    	struct sockaddr_in addr=
    	{
    		.sin_family=AF_INET,
    		.sin_port=htons(atoi(argv[1])),//发送的端口号
    		.sin_addr.s_addr=INADDR_ANY,//本地所有IP
    	};
    	if(bind(sockfd,(const struct sockaddr *)&addr,sizeof(struct sockaddr)))
    	{
    		printf("绑定端口号失败\n");
    		return 0;
    	}
    	/*开始接收数据*/
    	char buff[256];
    	struct sockaddr_in c_addr;
    	socklen_t addrlen=sizeof(struct sockaddr_in);
    	ssize_t size;
    	while(1)
    	{
    		size=recvfrom(sockfd,buff,sizeof(buff)-1,0,(struct sockaddr *)&c_addr,&addrlen);
    		if(size<=0)
    		{
    			printf("接收数据失败\n");
    			continue;
    		}
    		buff[size]='\0';
    		printf("[%s:%d] %s,len=%ld byte\n",inet_ntoa(c_addr.sin_addr),ntohs(c_addr.sin_port),buff,size);
    	}
    	close(sockfd);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    在这里插入图片描述

    2.2 设置UDP广播特性

      默认情况下UDP通讯是不支持广播特性,需要广播特性则需要设置UDP套接字属性。

    //设置该套接字为广播类型,
    	int nb = 0;
    	nb = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (char *)&opt, sizeof(opt));
    	if(nb == -1)
    	{
    		printf("设置广播类型错误.\n");
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 广播发送方示例
    #include           /* See NOTES */
    #include 
    #include 
    #include 
    #include  /* superset of previous */
    #include 
    #include 
    #include 
    int main(int argc,char *argv[])
    {
    	if(argc!=3)
    	{
    		printf("格式:./a.out <端口号> \n");
    		return 0;
    	}
    	int sockfd=socket(AF_INET,SOCK_DGRAM,0);	
    	if(sockfd==-1)
    	{
    		printf("创建网络套接字失败\n");
    		return 0;
    	}
    	//设置该套接字为广播类型,
    	const int opt = 1;
    	int nb = 0;
    	nb = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (char *)&opt, sizeof(opt));
    	if(nb == -1)
    	{
    		printf("设置广播类型错误.\n");
    	}
    	struct sockaddr_in s_addr=
    	{
    		.sin_family=AF_INET,
    		.sin_port=htons(atoi(argv[1])),
    		.sin_addr.s_addr=inet_addr(argv[2]),//本地所有IP
    	};
    	char buff[]="UDP send data test,hello,world!";
    	ssize_t size;
    	while(1)
    	{
    		size=sendto(sockfd,buff,sizeof(buff),0,( const struct sockaddr * )&s_addr,sizeof(s_addr));
    		printf("发送数据成功size=%ld\n",size);
    		sleep(1);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    在这里插入图片描述

  • 相关阅读:
    【COSBench系列】1. COSBench认识、使用、结果分析
    水利部加快推进小型水库除险加固,大坝安全监测是重点
    合同法重点笔记经典版
    JavaScript常用函数
    以激励为导向的配电网分布式电压调节
    Elastic 8.15:更好的语义搜索、新的 OTel 分布、SIEM 数据导入
    【Linux】su、sudo 等 “切换用户” 命令
    彻底搞懂MySQL主从复制工作原理:2个日志 | 3种存储格式 |3个线程|4种工作模式
    基于UDP的服务器端和客户端
    Allegro在测量时如何同时显示双单位
  • 原文地址:https://blog.csdn.net/weixin_44453694/article/details/126481347