22.UDP通信

发布时间 2023-11-21 16:30:43作者: CodeMagicianT

22.UDP通信

TCP:传输控制协议,面向连接的,稳定的,可靠的,安全的数据流传递

稳定和可靠: 丢包重传

数据有序: 序号和确认序号

流量控制: 滑动窗口

UDP:用户数据报协议

面向无连接的,不稳定,不可靠,不安全的数据报传递---更像是收发短信

UDP传输不需要建立连接,传输效率更高,在稳定的局域网内环境相对可靠

UDP通信相关函数介绍:

ssize_t recvfrom(int sockfd, void* buf, size_t len, int flags, struct sockaddr* src_addr, socklen_t* addrlen);

函数说明: 接收消息

参数说明:

▶sockfd 套接字

▶buf 要接受的缓冲区

▶len 缓冲区的长度

▶flags 标志位 一般填0

▶src_addr 原地址 传出参数

▶addrlen 发送方地址长度

▶返回值

成功: 返回读到的字节数

失败: 返回 -1 设置errno

调用该函数相当于TCP通信的recv+accept函数

函数说明: 发送数据

参数说明:

▶sockfd 套接字

▶dest_addr 目的地址

▶addrlen 目的地址长度

▶返回值

成功: 返回写入的字节数

失败: 返回-1,设置errno

UDP的服务器编码流程:

▶创建套接字socket,type=SOCK_DGRAM

udp_socket = socket(AF_INET, SOCK_DGRAM, 0);
SOCK_DGRAM      Supports  datagrams  (connectionless, unreliable messages of a fixedmaximum length).

▶绑定ip和端口 -bind

▶while循环收发数据

while (1)
{
	//读取消息
	recvfrom(cfd, buf, sizeof(buf), 0, (struct sockaddr*)&client, &len);
	
	//发送数据
	sendto(cfd, buf, n, 0, (struct sockaddr *)&client, len);
}

▶关闭套接字--close

close(cfd);

UDP客户端流程:

▶创建套接字socket,type=SOCK_DGRAM

udp_socket = socket(AF_INET, SOCK_DGRAM, 0);
SOCK_DGRAM      Supports  datagrams  (connectionless, unreliable messages of a fixedmaximum length).

▶while循环收发数据

while (1)
{
        //发送数据
	sendto(cfd, buf, n, 0, (struct sockaddr *)&client, len);
    
	//读取消息
	n = recvfrom(cfd, buf, sizeof(buf), 0, (struct sockaddr*)&client, &len);
}

▶关闭套接字--close

close(cfd);

编写udp代码并进行测试

测试:

多开器几个客户端经过测试表明:udp天然支持多客户端,这点和TCP不同,TCP需要维护连接。

使用nc命令进行测试: nc -u 127.1 8888

服务端

//udp服务端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <ctype.h>

int main()
{
	//创建socket
	int cfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(cfd<0)
	{
		perror("socket error");
		return -1;
	}

	//绑定
	struct sockaddr_in serv;
	struct sockaddr_in client;
	bzero(&serv, sizeof(serv));
	serv.sin_family = AF_INET;
	serv.sin_port = htons(8888);
	serv.sin_addr.s_addr = htonl(INADDR_ANY);
	bind(cfd, (struct sockaddr *)&serv, sizeof(serv));

	int i;
	int n;
	socklen_t len;
	char buf[1024];
	while(1)
	{
		//读取数据
		memset(buf, 0x00, sizeof(buf));
		len = sizeof(client);
		n = recvfrom(cfd, buf, sizeof(buf), 0, (struct sockaddr *)&client, &len);

		//将大写转换为小写
		for(i=0; i<n; i++)
		{
			buf[i] = toupper(buf[i]);
		}
		//printf("[%d]:n==[%d], buf==[%s]\n", ntohs(client.sin_port), n, buf);
		//发送数据
		sendto(cfd, buf, n, 0, (struct sockaddr *)&client, len);
	}

	//关闭套接字
	close(cfd);

	return 0;
}

终端1


终端2

cmt@cmt-VMware-Virtual-Platform:~/C_Program/day12$ nc -u 127.1 8888
nihao
NIHAO

终端3

cmt@cmt-VMware-Virtual-Platform:~/C_Program/day12$ netstat -anp | grep 8888
(并非所有进程都能被检测到,所有非本用户的进程信息将不会显示,如果想看到所有信息,则必须切换到 root 用户)
udp        0      0 127.0.0.1:36759         127.0.0.1:8888          ESTABLISHED 2991/nc             
udp        0      0 0.0.0.0:8888            0.0.0.0:*                           2990/./01-udp_server 

终端4

cmt@cmt-VMware-Virtual-Platform:~/C_Program/day12$ nc -u 127.1 8888
shenme
SHENME

可以接受两个客户端。

两个客户端给服务端发送数据,收到谁的就发给谁。不会出现客户端A发给服务端同时服务端发给客户端B。

下面代码去掉注释

printf("[%d]:n==[%d], buf==[%s]\n", ntohs(client.sin_port), n, buf);

终端1

cmt@cmt-VMware-Virtual-Platform:~/C_Program/day12$ ./01-udp_server
[60231]:n==[4], buf==[SFS
]
[49824]:n==[12], buf==[SDFWSADFASD
]

终端2

cmt@cmt-VMware-Virtual-Platform:~/C_Program/day12$ nc -u 127.1 8888
sfs
SFS

终端3

cmt@cmt-VMware-Virtual-Platform:~/C_Program/day12$ nc -u 127.1 8888
sdfwsadfasd
SDFWSADFASD

客户端

//udp客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <ctype.h>

int main()
{
	//创建socket
	int cfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(cfd<0)
	{
		perror("socket error");
		return -1;
	}

	int n;
	char buf[1024];
	struct sockaddr_in serv;
	serv.sin_family = AF_INET;
	serv.sin_port = htons(8888);
	inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);

	while(1)
	{
		//读标准输入数据
		memset(buf, 0x00, sizeof(buf));
		n = read(STDIN_FILENO, buf, sizeof(buf));

		//发送数据
		sendto(cfd, buf, n, 0, (struct sockaddr *)&serv, sizeof(serv));

		//读取数据
		memset(buf, 0x00, sizeof(buf));
		n = recvfrom(cfd, buf, sizeof(buf), 0, NULL, NULL);
		printf("n==[%d], buf==[%s]\n", n, buf);
	}

	//关闭套接字
	close(cfd);

	return 0;
}

终端1服务端

cmt@cmt-VMware-Virtual-Platform:~/C_Program/day12$ make 01-udp_server
cc     01-udp_server.c   -o 01-udp_server
cmt@cmt-VMware-Virtual-Platform:~/C_Program/day12$ ./01-udp_server 
[38049]:n==[6], buf==[NIHAO
]

终端2客户端1

cmt@cmt-VMware-Virtual-Platform:~/C_Program/day12$ ./02-udp-client 
nihao
^C
cmt@cmt-VMware-Virtual-Platform:~/C_Program/day12$ ./02-udp-client 
nihao
n==[6], buf==[NIHAO
]