天天看點

recv函數和send函數

int recv( SOCKET s, char FAR *buf, int len, int flags);   

不論是客戶還是伺服器應用程式都用recv函數從TCP連接配接的另一端接收資料。

該函數的第一個參數指定接收端套接字描述符;

第二個參數指明一個緩沖區,該緩沖區用來存放recv函數接收到的資料;

第三個參數指明buf的長度;

第四個參數一般置0。

因為UDP是按資料包接收的,我們在接收之前并不知道這個資料包有多大。一個政策是,我們準備足夠大的應用程式緩存以免出錯,但是這個“足夠大”的概念是建立在我們對傳送的資料事先有了解的情況下,比如是我們自己設計伺服器端和用戶端并且制定應用層協定;另外一種政策是,将一個資料包的相關資訊記錄在資料包的前面的一些位元組中,比如說大小,這樣,我們可以通過預讀資料包的前面一段,得到這個資料包的相關資訊,比如說大小,然後再安排緩存。

這個預讀的flag就是MSG_PEEK。使用預讀後,的下一條UDP資料包資訊被讀出來,但是并不從RecvQ中彈出。

UDP也可以通過recvfrom()預讀獲得來向的遠端位址,進而可以提供給比如connect()等函數使用。

需要說明的是,在Linux下(我是Debian系統)從一個n位元組的UDP資料包中預讀取小于n個位元組的資料是完全沒有問題的;但是在WinSock下會引起一個異常10040(WSAEMSGSIZE),即是說win32下recv()或者recvfrom()在這種情況下會傳回-1。其異常資訊大概是讀取的資料長度小于資料包的長度——而這個正是我們計劃中的事情。

1.send 函數

int send( SOCKET s, const char FAR *buf, int len, int flags );  

    不論是客戶還是伺服器應用程式都用send函數來向TCP連接配接的另一端發送資料。客戶程式一般用send函數向伺服器發送請求,而伺服器則通常用send函數來向客戶程式發送應答。

    該函數的第一個參數指定發送端套接字描述符;

    第二個參數指明一個存放應用程式要發送資料的緩沖區;

    第三個參數指明實際要發送的資料的位元組數;

    第四個參數一般置0。 

    這裡隻描述同步Socket的send函數的執行流程。當調用該函數時,

   (1)send先比較待發送資料的長度len和套接字s的發送緩沖的長度, 如果len大于s的發送緩沖區的長度,該函數傳回SOCKET_ERROR;

   (2)如果len小于或者等于s的發送緩沖區的長度,那麼send先檢查協定是否正在發送s的發送緩沖中的資料,如果是就等待協定把資料發送完,如果協定還沒有開始發送s的發送緩沖中的資料或者s的發送緩沖中沒有資料,那麼send就比較s的發送緩沖區的剩餘空間和len

   (3)如果len大于剩餘空間大小,send就一直等待協定把s的發送緩沖中的資料發送完

   (4)如果len小于剩餘 空間大小,send就僅僅把buf中的資料copy到剩餘空間裡(注意并不是send把s的發送緩沖中的資料傳到連接配接的另一端的,而是協定傳的,send僅僅是把buf中的資料copy到s的發送緩沖區的剩餘空間裡)。

   如果send函數copy資料成功,就傳回實際copy的位元組數,如果send在copy資料時出現錯誤,那麼send就傳回SOCKET_ERROR;如果send在等待協定傳送資料時網絡斷開的話,那麼send函數也傳回SOCKET_ERROR。

   要注意send函數把buf中的資料成功copy到s的發送緩沖的剩餘空間裡後它就傳回了,但是此時這些資料并不一定馬上被傳到連接配接的另一端。如果協定在後續的傳送過程中出現網絡錯誤的話,那麼下一個Socket函數就會傳回SOCKET_ERROR。(每一個除send外的Socket函數在執 行的最開始總要先等待套接字的發送緩沖中的資料被協定傳送完畢才能繼續,如果在等待時出現網絡錯誤,那麼該Socket函數就傳回 SOCKET_ERROR)

注意:在Unix系統下,如果send在等待協定傳送資料時網絡斷開的話,調用send的程序會接收到一個SIGPIPE信号,程序對該信号的預設處理是程序終止。

通過測試發現,異步socket的send函數在網絡剛剛斷開時還能發送傳回相應的位元組數,同時使用select檢測也是可寫的,但是過幾秒鐘之後,再send就會出錯了,傳回-1。select也不能檢測出可寫了。

2. recv函數

int recv( SOCKET s, char FAR *buf, int len, int flags);   

    不論是客戶還是伺服器應用程式都用recv函數從TCP連接配接的另一端接收資料。該函數的第一個參數指定接收端套接字描述符;

    第二個參數指明一個緩沖區,該緩沖區用來存放recv函數接收到的資料;

    第三個參數指明buf的長度;

    第四個參數一般置0。

    這裡隻描述同步Socket的recv函數的執行流程。當應用程式調用recv函數時,

    (1)recv先等待s的發送緩沖中的資料被協定傳送完畢,如果協定在傳送s的發送緩沖中的資料時出現網絡錯誤,那麼recv函數傳回SOCKET_ERROR,

    (2)如果s的發送緩沖中沒有資料或者資料被協定成功發送完畢後,recv先檢查套接字s的接收緩沖區,如果s接收緩沖區中沒有資料或者協定正在接收資料,那麼recv就一直等待,直到協定把資料接收完畢。當協定把資料接收完畢,recv函數就把s的接收緩沖中的資料copy到buf中(注意協定接收到的資料可能大于buf的長度,是以 在這種情況下要調用幾次recv函數才能把s的接收緩沖中的資料copy完。recv函數僅僅是copy資料,真正的接收資料是協定來完成的),

    recv函數傳回其實際copy的位元組數。如果recv在copy時出錯,那麼它傳回SOCKET_ERROR;如果recv函數在等待協定接收資料時網絡中斷了,那麼它傳回0。

注意:在Unix系統下,如果recv函數在等待協定接收資料時網絡斷開了,那麼調用recv的程序會接收到一個SIGPIPE信号,程序對該信号的預設處理是程序終止。