1. 程式人生 > >tcp協議系列文章(7):send()的資料大小與可用的傳送緩衝區大小的關係

tcp協議系列文章(7):send()的資料大小與可用的傳送緩衝區大小的關係

筆者這裡要指出的是,man send 手冊上說的或許與send()的版本有關。詳細的,可以檢視筆者的另一篇部落格,上面有就send()的行為的詳細說法。

下面的部落格內容,其實驗證的方法與驗證的目的並不相同!!!請讀者注意!!!


自己做了個測試,伺服器只起socket在偵聽,不recv, 也不send.

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

客戶端,將傳送緩衝區大小設定成2k,然後一次傳送3k的資料。

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

互動報文


從這裡看出當傳送長度大於緩衝區大小時,是分次傳送,三次加起來 1448 + 1448 + 176 = 3072

在windows下,做同樣的測試

  1. // xp vc6.0 32bit
  2. #include <stdio.h>
  3. #include <winsock2.h>
  4. #include <iostream.h>
  5. #pragma  comment (lib,"ws2_32")
  6. void  main()  
  7. {      
  8.     int         len,optval,optlen,iResult;  
  9.     SOCKADDR_IN clientService;// 地址
  10.     SOCKET      ConnectSocket;// socket
  11.     WSADATA     wsaData;// 庫
  12.     unsigned char buf[1024*3];    
  13.     //初始化socket庫, 儲存ws2_32.dll已經載入
  14.     iResult = WSAStartup(MAKEWORD(2,2), &wsaData);  
  15.     if (iResult != NO_ERROR)  
  16.         printf("Error at WSAStartup()\n");  
  17.     // 建立socket
  18.     ConnectSocket = socket(AF_INET, // IPv4
  19.         SOCK_STREAM, // 順序的、可靠的、基於連線的、雙向的資料流通訊
  20.         IPPROTO_TCP  // 使用TCP協議
  21.         /*0*/);  
  22.         if (ConnectSocket == INVALID_SOCKET)  
  23.         {  
  24.             printf("Error at socket(): %d\n", WSAGetLastError());  
  25.             WSACleanup();  
  26.             return ;  
  27.         }  
  28.         optlen = sizeof(optval);  
  29.         getsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);  
  30.         printf("send buf len is %d\n",optval);           //預設傳送緩衝區8k
  31.         getsockopt(ConnectSocket, SOL_SOCKET, SO_RCVBUF, (char*)&optval, &optlen);  
  32.         printf("Recv buf len is %d\n",optval);          //預設接收緩衝區8k
  33.         optval = 1024 * 2;  
  34.         setsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, optlen);  
  35.         getsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);  
  36.         printf("send buf len change to %d\n",optval);  
  37.         // 設定服務端的通訊協議、IP地址、埠
  38.         clientService.sin_family = AF_INET;  
  39.         clientService.sin_addr.s_addr = inet_addr( "222.111.112.204" );  
  40.         clientService.sin_port = htons( 103 );  
  41.         // 連線到服務端
  42.         if ( connect(  
  43.             ConnectSocket, // socket
  44.             (SOCKADDR*) &clientService, // 地址
  45.             sizeof(clientService) // 地址的大小
  46.             ) == SOCKET_ERROR)  
  47.         {  
  48.             printf( "Failed to connect(%d)\n",WSAGetLastError() );  
  49.             WSACleanup();  
  50.             return ;  
  51.         }         
  52.         len = send(ConnectSocket, (char*)buf, 1024*3, 0);  
  53.         printf("send length is %d\n",len);             //這裡同樣直接返回3072
  54.         system("pause");  
  55.         return;   
  56. }  
抓取報文

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

上面都是阻塞的傳送,對於socket是非阻塞的話,做同樣的測試

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

和blocking socket表現是一樣的,一次send,協議棧分三幀傳送。

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

       可見,當send的資料長度大於socket的緩衝區長度時,不管是windows還是linux,send都會分幀傳送。

相關推薦

tcp協議系列文章7send()的資料大小可用傳送緩衝區大小關係

筆者這裡要指出的是,man send 手冊上說的或許與send()的版本有關。詳細的,可以檢視筆者的另一篇部落格,上面有就send()的行為的詳細說法。 下面的部落格內容,其實驗證的方法與驗證的目的並不相同!!!請讀者注意!!! 自己做了個測試,伺服器只起s

Git系列文章常見異常問題

1、GitHub提交的時顯示Updates were rejected because the remote contains work that you do  git push -u origin master 每次建立新的倉庫,提交的時總會出現這樣的錯誤。Updates

Git系列文章本地新建專案提交至遠端倉庫

------新建專案後,選中專案資料夾右鍵git Bash Here ------使用git init ------使用git status ------使用git add . ------使用git commit -m "新增檔案" ------使用gi

Git系列文章從github上下載專案,本地修改後提交至github

Spring官方demo綠房子:spring-projects/greenhouse 1、獲取github遠端倉庫地址: [email protected]:spring-projects/greenhouse.git 2、用git克隆專案到本地 選擇要克隆的程式碼,滑鼠右

Git系列文章Git簡介

Git:一個開源的分散式版本控制系統,可以有效的高速的控制管理各種從小到大的專案版本。他的作者就是大名鼎鼎的Linux系統創始人Linus。 git命令教程:https://www.yiibai.com/git   常用的幾個命令: git init 初始化版本倉庫 git

每日一python7基礎資料結構----字串

1、索引 例1: >>> s = "apple" >>> s[0] 'a' >>> s[1] 'p' >>> s[2] 'p' >>> s[3] 'l' >>> s[4]

DM 原始碼閱讀系列文章定製化資料同步功能的實現

作者:王相 本文為 DM 原始碼閱讀系列文章的第七篇,在 上篇文章 中我們介紹了 relay log 的實現,主要包括 relay

標號7python(就業階段)——網路TCP

<1>TCP概念 英文全拼(Transmission Control Protocol)簡稱傳輸控制協議,它是一種面向連線的、可靠的、基於位元組流的傳輸層通訊協議. <2>TCP特點  1、 面向連線 TCP不適用於廣播的應用程式,基

arcgis jsapi介面入門系列7滑鼠在地圖畫線

初始化,每個map執行一次就行 drawPolylineInit: function () { //畫幾何物件初始化 //新建一個圖形圖層用於存放畫圖過程中的圖形 let layer = new t

python快速學習系列7迭代器

迭代器協議 1.迭代器協議: ·迭代器是一個物件 ·迭代器可以被next()函式呼叫,並返回一個值 ·迭代器可以被iter()函式呼叫,並返回迭代器自己 ·連續被next()呼叫時返回一系列的值 ·如果到了迭代的末尾,則丟擲StopIteration異常 ·迭代器也可以沒有末尾,只要被nex

TCP/IP學習筆記7-廣播,多播IGMP協議

單播,多播,廣播的介紹 單播(unicast) 單播是說,對特定的主機進行資料傳送。例如給某一個主機發送IP資料包。這時候,資料鏈路層給出的資料頭裡面是非常具體的目的地址,對於乙太網來 說,就是網絡卡的MAC地址(不是FF-FF-FF-FF-FF-FF這樣的地址)

解讀ASP.NET 5 & MVC6系列7依賴注入

在前面的章節(Middleware章節)中,我們提到了依賴注入功能(Dependency Injection),ASP.NET 5正式將依賴注入進行了全功能的實現,以便開發人員能夠開發更具彈性的元件程式,MVC6也利用了依賴注入的功能重新對Controller和View的服務注入功能進行了重新設計;未來的依賴

ONVIF協議網路攝像機IPC客戶端程式開發7裝置搜尋

1 專欄導讀 本專欄第一篇文章「專欄開篇」列出了專欄的完整目錄,按目錄順序閱讀,有助於你的理解,專欄前面文章講過的知識點(或程式碼段),後面文章不會贅述。為了節省篇幅,突出重點,在文章中展示的示例程式碼僅僅是關鍵程式碼,你可以在「專欄開篇」中獲取完整程式碼。

【NLP】驀然回首談談學習模型的評估系列文章

作者:白寧超 2016年7月19日19:04:51 摘要:寫本文的初衷源於基於HMM模型序列標註的一個實驗,實驗完成之後,迫切想知道採用的序列標註模型的好壞,有哪些指標可以度量。於是,就產生了對這一專題進度學習總結,這樣也便於其他人蔘考,節約大家的時間。本文依舊旨在簡明扼要梳理出模型評估核心指標,

【NLP】驀然回首談談學習模型的評估系列文章

作者:白寧超 2016年7月19日10:24:24 摘要:寫本文的初衷源於基於HMM模型序列標註的一個實驗,實驗完成之後,迫切想知道採用的序列標註模型的好壞,有哪些指標可以度量。於是,就產生了對這一專題進度學習總結,這樣也便於其他人蔘考,節約大家的時間。本文依舊旨在簡明扼要梳理出模型評估核心指標,

【NLP】驀然回首談談學習模型的評估系列文章

作者:白寧超 2016年7月18日17:18:43 摘要:寫本文的初衷源於基於HMM模型序列標註的一個實驗,實驗完成之後,迫切想知道採用的序列標註模型的好壞,有哪些指標可以度量。於是,就產生了對這一專題進度學習總結,這樣也便於其他人蔘考,節約大家的時間。本文依舊旨在簡明扼要梳理出模型評估核心指標,

MVC之前的那點事兒系列7WebActivator的實現原理詳解

文章內容 上篇文章,我們分析如何動態註冊HttpModule的實現,本篇我們來分析一下通過上篇程式碼原理實現的WebActivator類庫,WebActivator提供了3種功能,允許我們分別在HttpApplication初始化之前,之後以及ShutDown的時候分別執行指定的程式碼,示例如下: [

樹莓派3學習筆記77分辨率800 480顯示器配置

樹莓派、顯示器配置樹莓派3學習筆記(7):7寸(分辨率800 480)顯示器配置 樹莓派搭載分辨率為800X480的顯示器在顯示的時候可能會遇到無法全屏顯示的問題, 顯示器只有部分能夠顯示,有一部分是黑邊,對於這一種情況,我們只需進入系統的boot目錄,找到config.txt文件,或者直接在命

Windows Phone開發7當好總舵主

發的 content 數據 new 窗口 sdn 內容 str 剛才 吹完了頁面有關的話題,今天我們來聊一下頁面之間是如何導航的,在更多情況下,我們的應用程序不會只有一個頁面的,應該會有N個,就像我們做桌面應 用開發那樣,我們一個應用程序中可能不止一個窗體(極簡單的程序除外

springBoot7web開發-錯誤處理

spring boot 錯誤處理 處理方式一:實現ErrorController接口原理:Spring Boot 將所有的錯誤默認映射到/error, 實現ErrorController接口代碼:package com.example.demo.controller; import org.sp