您的位置:首页 > 理论基础 > 计算机网络

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

2017-06-23 10:48 405 查看
笔者这里要指出的是,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.

[cpp]
view plaincopy

//ubuntu10.04 32bit  
  
#include <stdio.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <arpa/inet.h>  
   
int main(void)  
{  
    int fd;  
    struct sockaddr_in addr;  
  
    fd = socket(AF_INET, SOCK_STREAM, 0);  
    addr.sin_family = AF_INET;  
    addr.sin_port = htons(103);  
    addr.sin_addr.s_addr = htonl(INADDR_ANY);  
    bind(fd, (struct sockaddr *)&addr, sizeof(addr));  
    listen(fd,5);  
}  

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

[cpp]
view plaincopy

//ubuntu10.04 32bit  
  
#include <stdio.h>  
#include <stdlib.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <unistd.h>  
#include <arpa/inet.h>  
int main()  
{  
    int fd,ret,tmp,sendlen;  
    struct sockaddr_in addr;  
    char *buf;  
    int sendBufLen = 1024*2;  
    socklen_t optlen = sizeof(int);  
  
    buf = (char *)malloc(1024 * 3);   
    fd = socket(AF_INET, SOCK_STREAM,0 );  
  
    setsockopt(fd,SOL_SOCKET, SO_SNDBUF,(const char*)&sendBufLen, sizeof(int));  
    getsockopt(fd,SOL_SOCKET, SO_SNDBUF,(int *)&tmp, &optlen);  
    printf("send_tmp=%d,optlen=%d\n",tmp,(int)optlen);    //设置发送缓冲区2048  
     
    getsockopt(fd,SOL_SOCKET, SO_RCVBUF,(int *)&tmp, &optlen);  
    printf("recv_tmp=%d,optlen=%d\n",tmp,(int)optlen);  
  
    addr.sin_family = AF_INET;  
    addr.sin_port = htons(103);  
    addr.sin_addr.s_addr = inet_addr("222.111.112.204"); //填上自己的IP  
      
    ret = connect (fd, (struct sockaddr *)&addr, sizeof(addr));  
    printf("connect return %d\n",ret);  
    getchar();  
    if (ret >= 0)  
    sendlen = send(fd,buf,1024*3,0);  
    printf("sendlen=%d\n",sendlen);       //此处返回3072  
    getchar();  
  
    return 0;  
}  

交互报文



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

在windows下,做同样的测试

[cpp]
view plaincopy

// xp vc6.0 32bit  
  
#include <stdio.h>  
#include <winsock2.h>  
#include <iostream.h>  
  
#pragma  comment (lib,"ws2_32")  
  
void  main()  
{      
      
    int         len,optval,optlen,iResult;  
    SOCKADDR_IN clientService;// 地址  
    SOCKET      ConnectSocket;// socket  
    WSADATA     wsaData;// 库  
    unsigned char buf[1024*3];    
      
    //初始化socket库, 保存ws2_32.dll已经加载  
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);  
    if (iResult != NO_ERROR)  
        printf("Error at WSAStartup()\n");  
      
    // 创建socket  
    ConnectSocket = socket(AF_INET, // IPv4  
        SOCK_STREAM, // 顺序的、可靠的、基于连接的、双向的数据流通信  
        IPPROTO_TCP  // 使用TCP协议  
        /*0*/);  
          
        if (ConnectSocket == INVALID_SOCKET)  
        {  
            printf("Error at socket(): %d\n", WSAGetLastError());  
            WSACleanup();  
            return ;  
        }  
  
        optlen = sizeof(optval);  
        getsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);  
        printf("send buf len is %d\n",optval);           //默认发送缓冲区8k  
        getsockopt(ConnectSocket, SOL_SOCKET, SO_RCVBUF, (char*)&optval, &optlen);  
        printf("Recv buf len is %d\n",optval);          //默认接收缓冲区8k  
  
        optval = 1024 * 2;  
        setsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, optlen);  
        getsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);  
        printf("send buf len change to %d\n",optval);  
  
        // 设置服务端的通信协议、IP地址、端口  
        clientService.sin_family = AF_INET;  
        clientService.sin_addr.s_addr = inet_addr( "222.111.112.204" );  
        clientService.sin_port = htons( 103 );  
          
        // 连接到服务端  
        if ( connect(  
            ConnectSocket, // socket  
            (SOCKADDR*) &clientService, // 地址  
            sizeof(clientService) // 地址的大小  
            ) == SOCKET_ERROR)  
        {  
            printf( "Failed to connect(%d)\n",WSAGetLastError() );  
            WSACleanup();  
            return ;  
        }         
          
        len = send(ConnectSocket, (char*)buf, 1024*3, 0);  
        printf("send length is %d\n",len);             //这里同样直接返回3072  
        system("pause");  
        return;   
}  

抓取报文



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

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

[cpp]
view plaincopy

//ubuntu10.04 32bit  
  
#include <stdio.h>  
#include <stdlib.h>  
#include <fcntl.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <unistd.h>  
#include <errno.h>  
#include <arpa/inet.h>  
int main()  
{  
    int fd,ret,tmp,sendlen,flags,fd_num;  
    fd_set  rset,wset;  
    struct timeval tval;  
    struct sockaddr_in addr;  
    char *buf;  
    int sendBufLen = 1024*2;  
    socklen_t optlen = sizeof(int);  
  
    buf = (char *)malloc(1024 * 3);   
    fd = socket(AF_INET, SOCK_STREAM,0 );  
  
    setsockopt(fd,SOL_SOCKET, SO_SNDBUF,(const char*)&sendBufLen, sizeof(int));  
    getsockopt(fd,SOL_SOCKET, SO_SNDBUF,(int *)&tmp, &optlen);  
    printf("send_tmp=%d,optlen=%d\n",tmp,(int)optlen);  
     
    getsockopt(fd,SOL_SOCKET, SO_RCVBUF,(int *)&tmp, &optlen);  
    printf("recv_tmp=%d,optlen=%d\n",tmp,(int)optlen);  
      
    flags = fcntl(fd,F_GETFL,0);  
    fcntl(fd,F_SETFL,flags|O_NONBLOCK);  
  
    addr.sin_family = AF_INET;  
    addr.sin_port = htons(103);  
    addr.sin_addr.s_addr = inet_addr("222.111.112.204");   
      
    ret = connect (fd, (struct sockaddr *)&addr, sizeof(addr));  
  
    if ((ret < 0) && (errno == EINPROGRESS))  
    {  
        FD_ZERO(&rset);  
        FD_SET(fd,&rset);  
        wset = rset;  
        tval.tv_sec = 3;  
        tval.tv_usec = 0;  
  
        if ( -1 == (fd_num = select(fd+1, &rset, &wset, NULL, &tval)))  
        {  
            perror("select error");  
            exit(0);  
        }   
                else  
        {  
            if ((fd_num == 1) && FD_ISSET(fd, &wset))  //connect only can be write  
            {  
                printf("connect OK!\n");  
            }  
            else  
            {  
                perror("state error");  
                exit(0);  
            }  
        }     
    }  
  
    if (-1 ==(sendlen = send(fd,buf,1024*3,0)))  
    {  
    perror("send error");  
    }   
    else  
    {  
    printf("sendlen=%d\n",sendlen);   //直接返回3072  
    }         
    return 0;  
}  



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

[cpp]
view plaincopy

//xp vc6.0 32bit  
  
#include <stdio.h>  
#include <winsock2.h>  
#include <iostream.h>  
  
#pragma  comment (lib,"ws2_32")  
  
void  main()  
{         
    int len,optval,optlen,iResult,fd_num,on=1;  
    SOCKADDR_IN clientService;// 地址  
    SOCKET      ConnectSocket;// socket  
    WSADATA     wsaData;// 库  
    unsigned char buf[1024*3];//  
    fd_set      rset,wset;  
    struct timeval tval;      
      
    //初始化socket库, 保存ws2_32.dll已经加载  
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);  
    if (iResult != NO_ERROR)  
        printf("Error at WSAStartup()\n");  
      
    // 创建socket  
    ConnectSocket = socket(AF_INET, // IPv4  
        SOCK_STREAM, // 顺序的、可靠的、基于连接的、双向的数据流通信  
        IPPROTO_TCP  // 使用TCP协议  
        /*0*/);  
          
        if (ConnectSocket == INVALID_SOCKET)  
        {  
            printf("Error at socket(): %d\n", WSAGetLastError());  
            WSACleanup();  
            return ;  
        }  
          
        if(0 == ioctlsocket(ConnectSocket, FIONBIO, (unsigned long *)&on))  
        {  
            printf("socket non-blocking set success!\n");  
        }  
          
        optlen = sizeof(optval);  
        getsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);  
        printf("send buf len is %d\n",optval);  
        getsockopt(ConnectSocket, SOL_SOCKET, SO_RCVBUF, (char*)&optval, &optlen);  
        printf("Recv buf len is %d\n",optval);  
          
        optval = 1024 * 2;  
        setsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, optlen);  
        getsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);  
        printf("send buf len change to %d\n",optval);  
          
        // 设置服务端的通信协议、IP地址、端口  
        clientService.sin_family = AF_INET;  
        clientService.sin_addr.s_addr = inet_addr( "222.111.112.204" );  
        clientService.sin_port = htons( 103 );  
          
        // 连接到服务端  
        if ( connect(  
            ConnectSocket, // socket  
            (SOCKADDR*) &clientService, // 地址  
            sizeof(clientService) // 地址的大小  
            ) == SOCKET_ERROR)  
        {  
            if ( WSAEWOULDBLOCK == WSAGetLastError() )   
            {  
                FD_ZERO(&rset);  
                FD_SET(ConnectSocket,&rset);  
                wset = rset;  
                tval.tv_sec = 3;  
                tval.tv_usec = 0;  
                if (SOCKET_ERROR == (fd_num = select(ConnectSocket+1, &rset, &wset, NULL, &tval)))  
                {  
                    printf( "select Failed (%d)\n",WSAGetLastError() );  
                    WSACleanup();  
                    return ;  
                }   
                else  
                {  
                    if ((fd_num == 1) && FD_ISSET(ConnectSocket, &wset))  //connect成功后,只能可写,不能可读可写  
                    {  
                        printf("connect OK!\n");  
                    }  
                    else  
                    {  
                        printf( "connect Failed (%d)\n",WSAGetLastError() );  
                        WSACleanup();  
                        return ;  
                    }             
                }  
            }   
            else  
            {  
                printf( "Failed to connect(%d)\n",WSAGetLastError() );  
                WSACleanup();  
                return ;  
            }             
        }             
          
        if (SOCKET_ERROR == (len=send(ConnectSocket, (char*)buf, 1024*3, 0)))  
        {  
            printf("send error %d\n", WSAGetLastError());  
        }   
        else  
        {  
            printf("send length is %d\n",len);  //直接返回3072  
        }  
          
        system("pause");  
        return;   
}  



       可见,当send的数据长度大于socket的缓冲区长度时,不管是windows还是linux,send都会分帧发送。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: