1. UDP简介
UDP 和TCP 的区别包括 1. 面向字节流和面向报文 2. TCP必须要建立连接后才能进行数据交换,但是UDP则并没有连接的建立和释放过程。面向字节流说明,tcp报文段(segment)是没有边界的,当服务器发送多个报文段到客户端时,客户端可能会把着多个报文段合并成一个报文段进行接收。但是对于UDP来说,数据是通过报文段进行传输的。
如果说TCP协议可以用打电话去比喻,那么UDP协议可以用邮箱来形容。用邮箱传递信息时,虽然有丢失的可能性。但是用户1发送的信件和用户2接受的信件肯定是相同的。
2. 用UDP编写echo server
使用UDP,我们只需要修改一下socket函数的参数就OK了。
int client_sock = socket(PF_INET, SOCK_DGRAM, 0);
然后就是创建两个表示客户端和服务端地址的sockaddr_in结构,其中serv_addr的各个字段需要我们自己填充。但是client_addr的各个字段是通过recvfrom通过指针进行自动填充。
struct sockaddr_in serv_addr, client_addr;
填充serv_addr的各个字段:
serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(8080);
下来使用bind函数将套接字和服务器地址进行绑定。
if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { err_exit("bind error"); }
数据传输方面使用recv族和sendto进行数据传输,这里值得注意的是只有sendto中的sockaddr_in结构是需要手动填充的,sendto中的地址表示要发送的目标地址。recvfrom中的是获取数据的源地址。下图是echo服务器的交互流程。

3. 地址长度所遇到的一个坑
int sLen = sendto(serv_sock, buffer, rLen, MSG_CONFIRM, (struct sockaddr *)&client_addr, recv_addr_len);
client_addr是通过服务端调用recvfrom获取的,我心想这也不用我手动填啊,怎么就又错了。又反复看了书上的代码,确定地址的获取方式没有错,通过打印我发现地址确实有问题,端口和地址都不对。在网上搜了下 udp echo server的代码,用可以正常运行的代码和我的代码细细比对,终于发现了问题所在。问题就出在地址长度的初始化上,我一直以为recvfrom中的地址长度是值-返回模式,但是实际上这个值也是要在recvfrom中起作用的。如果这个值不正确,client_addr的值也是错误的,导致服务器无法正常的把数据发送到客户端。就酱。。。
socklen_t recv_addr_len = sizeof(client_addr); //正确的初始化方式 //socklen_t recv_addr_len; //错误的初始化方式 int rLen = recvfrom(serv_sock, buffer, MAX_LEN, MSG_WAITALL, (struct sockaddr *)&client_addr, &recv_addr_len);