UDP 内部原理
IP 的作用是让离开主机 B 的UDP数据准确传递到主机 A,UDP 最重要的作用是根据端口号把传到主机的数据最终交付给 UDP 套接字
TCP 比 UDP 慢的原因
- 收发数据前后进行的连接设置和清除过程
- 收发数据过程中为保证可靠性进行的流控制
基于 UDP 的 server 端和客户端
- 没有连接,只有创建套接字和数据交换过程
- server 和 client 只需一个套接字
UDP 套接字的 I/O 函数
发送函数
ssize_t sendto(int sock, const void *buff, size_t nbytes,
int flags, const struct sockaddr *to, socklen_t addrlen)
成功时返回传输的字节数,失败时返回-1
sock: 套接字描述符
buff: 保存待传输数据的缓冲地址值
nbytes: 待传输数据的长度,以字节为单位
flags: 可选参数,若无则传0
to: 存有目标地址信息的sockaddr结构体变量的地址值
addrlen: 传递给参数to的地址值接头体变量长度
memset(&your_adr, 0, sizeof(your_adr));
your_adr.sin_family = AF_INET;
your_adr.sin_addr.s_addr = inet_addr(argv[1]);
your_adr.sin_port = htons(atoi(argv[2]));
char msg1[] = "hi!";
sendto(sock, msg1, sizeof(msg1), 0, (struct sockaddr *)&your_adr, sizeof(your_adr));
接收函数
ssize_t recvfrom(int sock, void *buff, size_t nbytes, int flags, struct sockaddr *form,
socklen_t *addrlen)
成功时返回传输的字节数,失败时返回-1
sock: 套接字描述符
buff: 保存接收数据的缓冲地址值
nbytes: 可接收的最大字节数
flags: 可选参数,若无则传0
from: 存有发送端地址信息的sockaddr结构体变量的地址值
addrlen: 保存参数from的结构体变量长度的变量地址值
char message[BUFF_SIZE];
adr_sz = sizeof(your_adr);
str_len = recvfrom(sock, message, BUFF_SIZE, 0, (struct sockaddr *)&your_adr, &adr_sz);
存在数据边界的 UDP 套接字
输入函数的调用次数和输出函数的调用次数要完全一致。 发送端调用 3 次 sendto
sendto(sock, msg1, sizeof(msg1), 0, (struct sockaddr *)&your_adr, sizeof(your_adr));
sendto(sock, msg2, sizeof(msg2), 0, (struct sockaddr *)&your_adr, sizeof(your_adr));
sendto(sock, msg3, sizeof(msg3), 0, (struct sockaddr *)&your_adr, sizeof(your_adr));
接收端
for (int i = 0; i < 3; i++) {
//这里 sleep 5s,意味着在第一次调用recvfrom时发送端已经发送了 3 次 sendto,但最终这里仍然要调用 3 次才能把 3 次的 sendto 数据全部接收
sleep(5);
adr_sz = sizeof(your_adr);
str_len = recvfrom(sock, message, BUFF_SIZE, 0, (struct sockaddr *)&your_adr, &adr_sz);
printf("message %d: %s \n", i+1, message);
}
已连接 UDP 套接字
sendto
传输过程:
- 向udp套接字注册目标 IP 和端口号
- 传输数据
- 删除注册的目标地址信息
如果目标是固定的,可以创建已连接套接字,只需针对套接字调用
connect
函数
connect(sock, (struct sockaddr *)&your_adr, sizeof(your_adr));