tcp协议系列文章(7):send()的数据大小与可用的发送缓冲区大小的关系

笔者这里要指出的是,man send 手册上说的或许与send()的版本有关。详细的,可以查看笔者的另一篇博客,上面有就send()的行为的详细说法。

tcp协议系列文章(6):send

下面的博客内容,其实验证的方法与验证的目的并不相同!!!请读者注意!!!


本博客原始地址在http://blog.csdn.net/smart55427/article/details/9112941     对开发者表示感谢。

关于send函数在发送的数据长度大于发送缓冲区大小,或者大于发送缓冲区剩余大小时,socket会怎么反应。参见这篇博客的两种说法http://blog.csdn.net/gukesdo/article/details/7295592




自己做了个测试,服务器只起socket在侦听,不recv, 也不send.

  1. //ubuntu10.04 32bit  
  2.   
  3. #include <stdio.h>  
  4. #include <sys/types.h>  
  5. #include <sys/socket.h>  
  6. #include <arpa/inet.h>  
  7.    
  8. int main(void)  
  9. {  
  10.     int fd;  
  11.     struct sockaddr_in addr;  
  12.   
  13.     fd = socket(AF_INET, SOCK_STREAM, 0);  
  14.     addr.sin_family = AF_INET;  
  15.     addr.sin_port = htons(103);  
  16.     addr.sin_addr.s_addr = htonl(INADDR_ANY);  
  17.     bind(fd, (struct sockaddr *)&addr, sizeof(addr));  
  18.     listen(fd,5);  
  19. }  

客户端,将发送缓冲区大小设置成2k,然后一次发送3k的数据。

  1. //ubuntu10.04 32bit  
  2.   
  3. #include <stdio.h>  
  4. #include <stdlib.h>  
  5. #include <sys/types.h>  
  6. #include <sys/socket.h>  
  7. #include <unistd.h>  
  8. #include <arpa/inet.h>  
  9. int main()  
  10. {  
  11.     int fd,ret,tmp,sendlen;  
  12.     struct sockaddr_in addr;  
  13.     char *buf;  
  14.     int sendBufLen = 1024*2;  
  15.     socklen_t optlen = sizeof(int);  
  16.   
  17.     buf = (char *)malloc(1024 * 3);   
  18.     fd = socket(AF_INET, SOCK_STREAM,0 );  
  19.   
  20.     setsockopt(fd,SOL_SOCKET, SO_SNDBUF,(const char*)&sendBufLen, sizeof(int));  
  21.     getsockopt(fd,SOL_SOCKET, SO_SNDBUF,(int *)&tmp, &optlen);  
  22.     printf("send_tmp=%d,optlen=%d\n",tmp,(int)optlen);    //设置发送缓冲区2048  
  23.      
  24.     getsockopt(fd,SOL_SOCKET, SO_RCVBUF,(int *)&tmp, &optlen);  
  25.     printf("recv_tmp=%d,optlen=%d\n",tmp,(int)optlen);  
  26.   
  27.     addr.sin_family = AF_INET;  
  28.     addr.sin_port = htons(103);  
  29.     addr.sin_addr.s_addr = inet_addr("222.111.112.204"); //填上自己的IP  
  30.       
  31.     ret = connect (fd, (struct sockaddr *)&addr, sizeof(addr));  
  32.     printf("connect return %d\n",ret);  
  33.     getchar();  
  34.     if (ret >= 0)  
  35.     sendlen = send(fd,buf,1024*3,0);  
  36.     printf("sendlen=%d\n",sendlen);       //此处返回3072  
  37.     getchar();  
  38.   
  39.     return 0;  
  40. }  

交互报文


从这里看出当发送长度大于缓冲区大小时,是分次发送,三次加起来 1448 + 1448 + 176 = 3072

在windows下,做同样的测试

  1. // xp vc6.0 32bit  
  2.   
  3. #include <stdio.h>  
  4. #include <winsock2.h>  
  5. #include <iostream.h>  
  6.   
  7. #pragma  comment (lib,"ws2_32")  
  8.   
  9. void  main()  
  10. {      
  11.       
  12.     int         len,optval,optlen,iResult;  
  13.     SOCKADDR_IN clientService;// 地址  
  14.     SOCKET      ConnectSocket;// socket  
  15.     WSADATA     wsaData;// 库  
  16.     unsigned char buf[1024*3];    
  17.       
  18.     //初始化socket库, 保存ws2_32.dll已经加载  
  19.     iResult = WSAStartup(MAKEWORD(2,2), &wsaData);  
  20.     if (iResult != NO_ERROR)  
  21.         printf("Error at WSAStartup()\n");  
  22.       
  23.     // 创建socket  
  24.     ConnectSocket = socket(AF_INET, // IPv4  
  25.         SOCK_STREAM, // 顺序的、可靠的、基于连接的、双向的数据流通信  
  26.         IPPROTO_TCP  // 使用TCP协议  
  27.         /*0*/);  
  28.           
  29.         if (ConnectSocket == INVALID_SOCKET)  
  30.         {  
  31.             printf("Error at socket(): %d\n", WSAGetLastError());  
  32.             WSACleanup();  
  33.             return ;  
  34.         }  
  35.   
  36.         optlen = sizeof(optval);  
  37.         getsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);  
  38.         printf("send buf len is %d\n",optval);           //默认发送缓冲区8k  
  39.         getsockopt(ConnectSocket, SOL_SOCKET, SO_RCVBUF, (char*)&optval, &optlen);  
  40.         printf("Recv buf len is %d\n",optval);          //默认接收缓冲区8k  
  41.   
  42.         optval = 1024 * 2;  
  43.         setsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, optlen);  
  44.         getsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);  
  45.         printf("send buf len change to %d\n",optval);  
  46.   
  47.         // 设置服务端的通信协议、IP地址、端口  
  48.         clientService.sin_family = AF_INET;  
  49.         clientService.sin_addr.s_addr = inet_addr( "222.111.112.204" );  
  50.         clientService.sin_port = htons( 103 );  
  51.           
  52.         // 连接到服务端  
  53.         if ( connect(  
  54.             ConnectSocket, // socket  
  55.             (SOCKADDR*) &clientService, // 地址  
  56.             sizeof(clientService) // 地址的大小  
  57.             ) == SOCKET_ERROR)  
  58.         {  
  59.             printf( "Failed to connect(%d)\n",WSAGetLastError() );  
  60.             WSACleanup();  
  61.             return ;  
  62.         }         
  63.           
  64.         len = send(ConnectSocket, (char*)buf, 1024*3, 0);  
  65.         printf("send length is %d\n",len);             //这里同样直接返回3072  
  66.         system("pause");  
  67.         return;   
  68. }  
抓取报文

分三次发送1460+1460+152 = 3072

上面都是阻塞的发送,对于socket是非阻塞的话,做同样的测试

  1. //ubuntu10.04 32bit  
  2.   
  3. #include <stdio.h>  
  4. #include <stdlib.h>  
  5. #include <fcntl.h>  
  6. #include <sys/types.h>  
  7. #include <sys/socket.h>  
  8. #include <unistd.h>  
  9. #include <errno.h>  
  10. #include <arpa/inet.h>  
  11. int main()  
  12. {  
  13.     int fd,ret,tmp,sendlen,flags,fd_num;  
  14.     fd_set  rset,wset;  
  15.     struct timeval tval;  
  16.     struct sockaddr_in addr;  
  17.     char *buf;  
  18.     int sendBufLen = 1024*2;  
  19.     socklen_t optlen = sizeof(int);  
  20.   
  21.     buf = (char *)malloc(1024 * 3);   
  22.     fd = socket(AF_INET, SOCK_STREAM,0 );  
  23.   
  24.     setsockopt(fd,SOL_SOCKET, SO_SNDBUF,(const char*)&sendBufLen, sizeof(int));  
  25.     getsockopt(fd,SOL_SOCKET, SO_SNDBUF,(int *)&tmp, &optlen);  
  26.     printf("send_tmp=%d,optlen=%d\n",tmp,(int)optlen);  
  27.      
  28.     getsockopt(fd,SOL_SOCKET, SO_RCVBUF,(int *)&tmp, &optlen);  
  29.     printf("recv_tmp=%d,optlen=%d\n",tmp,(int)optlen);  
  30.       
  31.     flags = fcntl(fd,F_GETFL,0);  
  32.     fcntl(fd,F_SETFL,flags|O_NONBLOCK);  
  33.   
  34.     addr.sin_family = AF_INET;  
  35.     addr.sin_port = htons(103);  
  36.     addr.sin_addr.s_addr = inet_addr("222.111.112.204");   
  37.       
  38.     ret = connect (fd, (struct sockaddr *)&addr, sizeof(addr));  
  39.   
  40.     if ((ret < 0) && (errno == EINPROGRESS))  
  41.     {  
  42.         FD_ZERO(&rset);  
  43.         FD_SET(fd,&rset);  
  44.         wset = rset;  
  45.         tval.tv_sec = 3;  
  46.         tval.tv_usec = 0;  
  47.   
  48.         if ( -1 == (fd_num = select(fd+1, &rset, &wset, NULL, &tval)))  
  49.         {  
  50.             perror("select error");  
  51.             exit(0);  
  52.         }   
  53.                 else  
  54.         {  
  55.             if ((fd_num == 1) && FD_ISSET(fd, &wset))  //connect only can be write  
  56.             {  
  57.                 printf("connect OK!\n");  
  58.             }  
  59.             else  
  60.             {  
  61.                 perror("state error");  
  62.                 exit(0);  
  63.             }  
  64.         }     
  65.     }  
  66.   
  67.     if (-1 ==(sendlen = send(fd,buf,1024*3,0)))  
  68.     {  
  69.     perror("send error");  
  70.     }   
  71.     else  
  72.     {  
  73.     printf("sendlen=%d\n",sendlen);   //直接返回3072  
  74.     }         
  75.     return 0;  
  76. }  

和blocking socket表现是一样的,一次send,协议栈分三帧发送。

  1. //xp vc6.0 32bit  
  2.   
  3. #include <stdio.h>  
  4. #include <winsock2.h>  
  5. #include <iostream.h>  
  6.   
  7. #pragma  comment (lib,"ws2_32")  
  8.   
  9. void  main()  
  10. {         
  11.     int len,optval,optlen,iResult,fd_num,on=1;  
  12.     SOCKADDR_IN clientService;// 地址  
  13.     SOCKET      ConnectSocket;// socket  
  14.     WSADATA     wsaData;// 库  
  15.     unsigned char buf[1024*3];//  
  16.     fd_set      rset,wset;  
  17.     struct timeval tval;      
  18.       
  19.     //初始化socket库, 保存ws2_32.dll已经加载  
  20.     iResult = WSAStartup(MAKEWORD(2,2), &wsaData);  
  21.     if (iResult != NO_ERROR)  
  22.         printf("Error at WSAStartup()\n");  
  23.       
  24.     // 创建socket  
  25.     ConnectSocket = socket(AF_INET, // IPv4  
  26.         SOCK_STREAM, // 顺序的、可靠的、基于连接的、双向的数据流通信  
  27.         IPPROTO_TCP  // 使用TCP协议  
  28.         /*0*/);  
  29.           
  30.         if (ConnectSocket == INVALID_SOCKET)  
  31.         {  
  32.             printf("Error at socket(): %d\n", WSAGetLastError());  
  33.             WSACleanup();  
  34.             return ;  
  35.         }  
  36.           
  37.         if(0 == ioctlsocket(ConnectSocket, FIONBIO, (unsigned long *)&on))  
  38.         {  
  39.             printf("socket non-blocking set success!\n");  
  40.         }  
  41.           
  42.         optlen = sizeof(optval);  
  43.         getsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);  
  44.         printf("send buf len is %d\n",optval);  
  45.         getsockopt(ConnectSocket, SOL_SOCKET, SO_RCVBUF, (char*)&optval, &optlen);  
  46.         printf("Recv buf len is %d\n",optval);  
  47.           
  48.         optval = 1024 * 2;  
  49.         setsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, optlen);  
  50.         getsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);  
  51.         printf("send buf len change to %d\n",optval);  
  52.           
  53.         // 设置服务端的通信协议、IP地址、端口  
  54.         clientService.sin_family = AF_INET;  
  55.         clientService.sin_addr.s_addr = inet_addr( "222.111.112.204" );  
  56.         clientService.sin_port = htons( 103 );  
  57.           
  58.         // 连接到服务端  
  59.         if ( connect(  
  60.             ConnectSocket, // socket  
  61.             (SOCKADDR*) &clientService, // 地址  
  62.             sizeof(clientService) // 地址的大小  
  63.             ) == SOCKET_ERROR)  
  64.         {  
  65.             if ( WSAEWOULDBLOCK == WSAGetLastError() )   
  66.             {  
  67.                 FD_ZERO(&rset);  
  68.                 FD_SET(ConnectSocket,&rset);  
  69.                 wset = rset;  
  70.                 tval.tv_sec = 3;  
  71.                 tval.tv_usec = 0;  
  72.                 if (SOCKET_ERROR == (fd_num = select(ConnectSocket+1, &rset, &wset, NULL, &tval)))  
  73.                 {  
  74.                     printf( "select Failed (%d)\n",WSAGetLastError() );  
  75.                     WSACleanup();  
  76.                     return ;  
  77.                 }   
  78.                 else  
  79.                 {  
  80.                     if ((fd_num == 1) && FD_ISSET(ConnectSocket, &wset))  //connect成功后,只能可写,不能可读可写  
  81.                     {  
  82.                         printf("connect OK!\n");  
  83.                     }  
  84.                     else  
  85.                     {  
  86.                         printf( "connect Failed (%d)\n",WSAGetLastError() );  
  87.                         WSACleanup();  
  88.                         return ;  
  89.                     }             
  90.                 }  
  91.             }   
  92.             else  
  93.             {  
  94.                 printf( "Failed to connect(%d)\n",WSAGetLastError() );  
  95.                 WSACleanup();  
  96.                 return ;  
  97.             }             
  98.         }             
  99.           
  100.         if (SOCKET_ERROR == (len=send(ConnectSocket, (char*)buf, 1024*3, 0)))  
  101.         {  
  102.             printf("send error %d\n", WSAGetLastError());  
  103.         }   
  104.         else  
  105.         {  
  106.             printf("send length is %d\n",len);  //直接返回3072  
  107.         }  
  108.           
  109.         system("pause");  
  110.         return;   
  111. }  


       可见,当send的数据长度大于socket的缓冲区长度时,不管是windows还是linux,send都会分帧发送。


发布了248 篇原创文章 · 获赞 90 · 访问量 73万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览