• UDP通信


    1、UDP

    /*
    udp传输层协议,和tcp是一样的
    特点:
    	面向无连接的,不安全的,报式传输协议
    	1.无连接:ldp通信的时候不需要connect
    		1) 通信不需要建立连接
    		2) 如果想给对方发送数据,只需要指定对方的IP和端口
    	2. udp会丢包
    		1) 数据丢失了就没有了,没有数据校验机制
    		2) udp不会丢失一部分数据,丢就是全丢,不丢就是一点不不丢
    	3.报式:
    		发送端发送多少数据,接牧端接收多少数据|
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2、UDP通信流程

    udp通信过程中,服务器和客户端做的操作几乎是一样的
    在这里插入图片描述

    1. 服务器端
    // 1. 创建一个通信的套接字   AF_INET使用IPv4 
    int cfd = socket(AF_INET,SOCK_DGRAM,0); //通信使用udp
    // 2.通信的套接字和本地的IP和端口绑定
    // 绑定的目的:程序启动之后不主动发送数据,先接收数据,就需要绑定端口
    // 如果不手动绑定端口,就会自动绑定端口,主动发送数据,可以自动绑定端口
    struct sockaddr_in addr ;
    bind(cfd, (struct sockaddr* )&addr, sizeof(addr) );
    //  3.通信
    接收数据:recvfrom( ) ;
    发送数据:sendto();
    // 4. 关闭通信的文件描述符
    close(); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 客户端
    // 1. 创建一个通信的套接字  
    int cfd = socket(AF_INET,SOCK_DGRAM,0); //通信使用udp
    // 2.通信的套接字和本地的IP和端口绑定
    // 绑定的目的:程序启动之后不主动发送数据,先接收数据,就需要绑定端口
    // 如果不手动绑定端口,就会自动绑定端口
    struct sockaddr_in addr ;
    bind(cfd, (struct sockaddr* )&addr, sizeof(addr) );
    //  3.通信
    接收数据:recvfrom( ) ;
    发送数据:sendto();
    // 4. 关闭通信的文件描述符
    close();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 操作函数
    //接收数据
    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    
    //接收和发送数据的函数默认是阻塞的
    //接收数据
    ssize_t recvfrom( int sockfd, void *buf, size_t len, int flags,
    					struct sockaddr *src_addr, socklen_t *addrlen) ;
    参数∶
    	- sockfd:通信的文件描述符
    	- buf:指向一块有效内存地址,存储接收的数据
    	- len:参数buf指向的内存大小
    	- flags:使用默认属性,指定为0即可
    	- src_addr:传出参数,保存发送端的地址信息(IP和端口)->大端(网络字节序)
    		- 对发送端的地址不感兴趣,可以指定为NULL
    	- addrlen:传入传出参数,类似于accept()最后一个参数
    		- src_addr为NULL,该参数也指定为NULL即可
    返回值:
    	>0:接收的字节数
    	-1:失败
    	
    //发送数据函数
    ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
    				const struct sockaddr *dest_addr, socklen_t addrlen);
    参数:
    	- sockfd:通信的文件描述符
    	- buf:指向一块有效内存地址,内存中存储了待发送的数据
    	- len:参数buf指向的内存中待发送的数据长度
    	- flags:使用默认属性,指定为0即可
    	- dest_addr:传入参数,保存接收端的地址信息(IP和端口)->大端(网络字节序)
    	- addrlen:传入参数,dest_addr参数指向的内存大小
    返回值:
    	>0: 发送的字节数
    	-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
    • 通信流程总结:
      • 服务器
    // 1.创建通信的套接字
    int fd = socket(af_inet, sock_dgram, 0);
    // 1.5 如果服务器只接受数据,需要绑定端口
    bind();
    // 2.通信
    recvfrom();
    sendto();
    // 3.关闭套接宇
    close():
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    * 客户端
    
    • 1
    // 1.创建通信的套接字
    int fd = socket(af_inet, sock_dgram, 0) ;
    // 1.5 如果客户端是主动发送发送数据,绑定端口可以不写(随机绑定)
    // bind();
    // 2.通信
    recvfrom();
    sendto();
    // 3.关闭套接字
    close():
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3、UDP服务器代码

    #include 
    #include 
    #incIude 
    #incIude 
    #include 
    //服务器端启动之后不发送数据,先接收数据
    //需要手动绑定端口
    int main()
    {
    	// 1.创建通信的套接字
    	int fd = socket(AF_INET,SOCK_DGRAM,0);
    	if(fd == -1)
    	{
    	      perror(exit(0));
    	      exit(0);
        }
    	// 接收数据需要绑定固定的端口
    	struct sockaddr_in addr;
    	addr.sin_family = AF_INET;
    	addr.sin_port= htons(8989);
    	addr.sin_addr.s_addr = INADDR ANY;
    	int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr));
    	if(ret == -1)
    	{
    		perror("bind");
    		exit(0);
    	}
    	//通信
    	char ip[24];
    	char buf[1024];
    	struct sockaddr_in cliaddr;
    	int clilen = sizeof(cliaddr);
    	while(1)
    	{
    		//接收数据
    		int len= recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&cliaddr,&clilen);
    		if(len==-1)
    		{
    			break;
    		}
    		printf("client ip: %s, port: %d\n",
    				inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ip, sizeof(ip)), 
    				ntohs(cliaddr.sin_port));
    		printf("client say: %s\n", buf);
    		//回复数据
    		sendto(fd, buf, strlen(len)+1, 0, (struct sockaddr*) &cliaddr, clilen);
    		close(fd);
    		return 0;
    	}
    
    	return 0;
    }
    
    • 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

    4、UDP客户端代码

    #include 
    #include 
    #incIude 
    #incIude 
    #include 
    //客户端启动之后主动发送数据
    //自动随机绑定端口
    int main()
    {
    
    	// 1.创建通信的套接字
    	int fd = socket(AF_INET,SOCK_DGRAM,0);
    	if(fd == -1)
    	{
    	      perror(exit(0));
    	      exit(0);
        }
        #if 0
    	// 接收数据需要绑定固定的端口
    	struct sockaddr_in addr;
    	addr.sin_family = AF_INET;
    	addr.sin_port= htons(8989);
    	addr.sin_addr.s_addr = INADDR ANY;
    	int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr));
    	if(ret == -1)
    	{
    		perror("bind");
    		exit(0);
    	}
    	#endif
    	//通信
    	char ip[24];
    	char buf[1024];
    	struct sockaddr_in cliaddr;
    	int clilen = sizeof(cliaddr);
    	//服务器地址
    	struct sockaddr_in seraddr;
    	seraddr.sin_family = AF_INET;
    	seraddr.sin_port = htons(8989);
    	inet_pton(AF_INET, "192.168.233.121",&seraddr.sin_addr.s_sddr);
    	while(1)
    	{
    		sprintf(buf, "hello, world, %d...\n",num++);
    		//发送数据,发送给服务器
    		sendto(fd, buf, strlen(buf)+1, 0, (struct sockaddr*) &seraddr, sizeof(seraddr));
    		memset(buf,0,sizeof(buf));
    		int len= recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&cliaddr,&clilen);
    		if(len==-1)
    		{
    			break;
    		}
    		printf("server say: %s\n", buf);
    		close(fd);
    		return 0;
    	}
    
    	return 0;
    }
    
    • 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
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    5、应用场景

    • 数据容易丢,用在对数据没有那么敏感的场景下。
      • 用户名密码->不行
      • 文件传输->不行
      • 语言通信->可以
      • 视频聊天,视频会议->可以。
      • 屏幕共享->可以
    • 效率高
      • 不需要建立连接
      • 数据校验机制
    • 一般用于局域网,数据丢包比较少,广域网,网络环境不好容易丢包
      • qq处理文件传输使用的是tcp,其他都是udp
      • 比较大的公司,有一个人力和财力,会在应用层对udp通信的数据包进行校验
        • 应用层->检验udp数据,让rdp类似于tcp,但是效率高
        • 传输层> udp
        • 网络层
        • 网络接口层
  • 相关阅读:
    【C++】入门(上)
    React组件应用于Spring MVC工程
    6.3 ASP.NET Core Web API技术选择
    前端性能优化:页面加载速度慢怎么办?
    MYSQL | 数据库到底是怎么来的?
    Word处理控件Aspose.Words功能演示:使用C#或VB.NET在Word文档中进行邮件合并
    十六、RabbitMQ快速入门
    第十四届蓝桥杯模拟赛第一期试题与题解Java
    Web信息收集,互联网上的裸奔者
    常用类以及接口
  • 原文地址:https://blog.csdn.net/qq_28539917/article/details/132892020