首页 > 代码库 > Socket编程实践(14) --UDP编程基础(2)
Socket编程实践(14) --UDP编程基础(2)
使用UDP注意事项
1.UDP报文可能会丢失、重复、乱序
2.UDP缺乏流量控制:当缓冲区写满以后,由于UDP没有流量控制机制,因此会覆盖缓冲区。
3.UDP协议数据报文截断:如果接收到的UDP数据报大于缓冲区,报文可能被截断,后面的部分会丢失。
4.使用UDP: recvfrom返回0,不代表连接关闭,因为UDP是无连接的。
而且sendto可以发送数据0包(只含有UDP首部[20字节]);
5.ICMP异步错误
观察现象:关闭UDP服务端,启动客户端,从键盘接受数据后,再发送数据。UDP客户端阻塞在recvfrom位置;
说明:
1)UDP发送报文的时,只把数据copy到发送缓冲区。在服务器没有起来的情况下,可以发送成功。
2)所谓ICMP异步错误是指:发送的报文的时候,没有错误,接受报文recvfrom的时候,回收到ICMP应答.
3)异步的错误,是无法返回未连接的套接字
6.UDP调用connect
1)UDP调用connet,并没有三次握手,只是维护了一个(和对等方的)状态信息
2)一但调用connect,就可以使用send函数
//注意点3实验: #include "commen.h" int main() { //创建UDP socket int sockfd = socket(AF_INET,SOCK_DGRAM,0); if (sockfd == -1) { err_exit("socket error"); } //填写server端信息 struct sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(9001); serverAddr.sin_addr.s_addr =INADDR_ANY; //绑定server端端口号 if (bind(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == -1) { err_exit("bind error"); } //向自己发送数据 if (sendto(sockfd,"1234",4,0,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == -1) { err_exit("sendto error"); } //接收一个字节,但想通过四次接收 //在接收数据时,如果缓冲区较小,则将截断报文,后面的报文将丢失. char buf[1]; for (int i = 0; i < 4; ++i) { int readCount = recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL); if (readCount == -1) { err_exit("recvfrom error"); } cout << "Have recv " << readCount << " bytes." << endl; write(STDOUT_FILENO,buf,readCount); } return 0; }
//注意点5实验: #include "commen.h" int main() { //创建UDP socket int sockfd = socket(AF_INET,SOCK_DGRAM,0); if (sockfd == -1) { err_exit("socket error"); } //填写server端信息 struct sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(9001); serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); char buf[BUFSIZ]; //从键盘接收数据,注意:是在服务器没有起来的情况之下发送数据 while (fgets(buf,sizeof(buf),stdin) != NULL) { //向server端传送数据 if (sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == -1) { err_exit("sendto error"); } else { cout << "send success...!" << endl; } memset(buf,0,sizeof(buf)); int readCount = 0; //从server端接收数据,并且忽略server端信息recvfrom(xx,xx,xx,xx,NULL,NULL) if ((readCount = recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL)) == -1) { err_exit("recvfrom error"); } else { cout << "recv success...!" << endl; } buf[readCount] = 0; //将数据写到屏幕 fputs(buf,stdout); } return 0; }
//注意点6实验(注意查看第18~26行) #include "commen.h" int main() { //创建UDP socket int sockfd = socket(AF_INET,SOCK_DGRAM,0); if (sockfd == -1) { err_exit("socket error"); } //填写server端信息 struct sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(9001); serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //添加connect调用 if (connect(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == -1) { err_exit("connect error"); } else { cout << "connect success!" << endl; } char buf[BUFSIZ]; //从键盘接收数据,注意:是在服务器没有起来的情况之下发送数据 while (fgets(buf,sizeof(buf),stdin) != NULL) { //向server端传送数据 if (sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == -1) { err_exit("sendto error"); } else { cout << "send success...!" << endl; } memset(buf,0,sizeof(buf)); int readCount = 0; //从server端接收数据,并且忽略server端信息recvfrom(xx,xx,xx,xx,NULL,NULL) if ((readCount = recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL)) == -1) { err_exit("recvfrom error"); } else { cout << "recv success...!" << endl; } buf[readCount] = 0; //将数据写到屏幕 fputs(buf,stdout); } return 0; }
实验结果:还是需要在server端不启动的情况下(此时返回一个ICMP的应答报文)
实验:直接调用send函数(不再是sendto了-33行)
#include "commen.h" int main() { //创建UDP socket int sockfd = socket(AF_INET,SOCK_DGRAM,0); if (sockfd == -1) { err_exit("socket error"); } //填写server端信息 struct sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(9001); serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //添加connect调用 if (connect(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == -1) { err_exit("connect error"); } else { cout << "connect success!" << endl; } char buf[BUFSIZ]; //从键盘接收数据,注意:是在服务器没有起来的情况之下发送数据 while (fgets(buf,sizeof(buf),stdin) != NULL) { //向server端传送数据 if (send(sockfd,buf,strlen(buf),0) == -1) { err_exit("sendto error"); } memset(buf,0,sizeof(buf)); int readCount = 0; //从server端接收数据,并且忽略server端信息recvfrom(xx,xx,xx,xx,NULL,NULL) if ((readCount = recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL)) == -1) { err_exit("recvfrom error"); } buf[readCount] = 0; //将数据写到屏幕 fputs(buf,stdout); } return 0; }
结论:
1.UDP也可以调用connet:(与不调用connect的区别)UDP客户端调用了connect以后,不会阻塞在recvfrom函数这里。
2.一但调用connect,就可以使用send函数
3.UDP协议数据截断:如果接收到的数据报大于缓冲区,则报文被截断,后面的部分会丢失。
Socket编程实践(14) --UDP编程基础(2)