---------------------------------------------------------------------
/** * UDP 客户端 */ #include <iostream> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> using namespace std; int main(int argc, char **argv) { int connFd = -1; struct sockaddr_in servAddr; char buff[64] = "hi, udp server"; char buffRecv[64]; connFd = socket(AF_INET, SOCK_DGRAM, 0); memset(&servAddr, 0, sizeof(servAddr)); servAddr.sin_family = AF_INET; servAddr.sin_port = htons(6060); servAddr.sin_addr.s_addr = inet_addr("192.168.1.100"); for (int i = 0; i < 2; i++) { sendto(connFd, buff, sizeof(buff), 0, (struct sockaddr *)&servAddr, sizeof(servAddr)); int recvLen = recvfrom(connFd, buffRecv, sizeof(buffRecv), 0, NULL, NULL); buffRecv[recvLen] = '\0'; cout << buffRecv << endl; } close(connFd); return 0; }注意:代码中为了方便没有处理函数的返回值 :(
UDP编程会有数据包的丢失问题,因为UDP是不可靠的,如果一个客户的数据包丢失,客户端将永远阻塞在recvfrom函数调用;类似的,如果客户数据到达了服务端,然后响应数据包丢失了,则客户永远阻塞在recvfrom调用。为了防止这样的问题出现,一般可以给recvfrom设置一个超时时间。
一个有意思的小问题当UDP服务端未运行时,UDP客户端发送给服务端数据包后就阻塞在了recvfrom调用了,等待一个永远也不可能的服务端应答。但是通过抓包分析,服务器主机响应了一个ICMP端口不可达的消息,但是这个ICMP错误不返回给客户进程。
这里测试用的是Socket Tool工具,在192.168.1.100(window主机)上往192.168.1.150(linux主机)上发送UDP包的测试结果。
这个ICMP错误是异步错误,该错误是由sendto引起,但是sendto本身成功返回,从UDP输出操作成功仅仅表示数据包已加入到数据链路层的输出队列了。而该ICMP报文是后来才接收来的。因此,对于一个UDP套接字来说,由它引发的异步错误并不返回给它,除非它已经建立连接。那么,这个问题如何解决呢,请搬个小板凳,拿着鼠标,继续翻滚 :)
UDP可以使用connect函数吗UDP是可以调用connect函数的,但是UDP的connect函数和TCP的connect函数调用确是大相径庭的,这里没有三次握手过程。内核只是检查是否存在立即可知的错误(比如目的地址不可达),记录对端的IP和端口号,然后立即返回调用进程。
使用了connect的UDP编程就可不必使用sendto函数了,直接使用write/read即可。以下代码使用了connect函数,然后往未运行UDP服务的主机发送数据:
#include <iostream> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <cstring> #include <unistd.h> using namespace std; int main(int argc, char **argv) { int connFd = -1; struct sockaddr_in servAddr; char buff[64] = "hi, udp server"; char buffRecv[64]; connFd = socket(AF_INET, SOCK_DGRAM, 0); memset(&servAddr, 0, sizeof(servAddr)); servAddr.sin_family = AF_INET; servAddr.sin_port = htons(60); servAddr.sin_addr.s_addr = inet_addr("192.168.1.150"); connect(connFd, (struct sockaddr *) &servAddr, sizeof(servAddr)); write(connFd, buff, sizeof(buff)); int recvLen = read(connFd, buffRecv, sizeof(buffRecv)); if (recvLen < 0) { perror("read error"); } close(connFd); return 0; }
输出结果为:
这里把测试代码中的read函数替换成以下代码输出结果也是一样的。
int recvLen = recvfrom(connFd, buffRecv, sizeof(buffRecv), 0, NULL, NULL);
UDP缺乏流量控制