linux socket send函式和recv函式詳解以及修改緩衝區大小
技術標籤:計算機網路
一、send/recv詳解
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訊號,程序對該訊號的預設處理是程序終止。
二、如何獲取/修改預設緩衝區大小?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <assert.h>
int main(int argc,char **argv)
{
int err = -1; /* 返回值 */
int s = -1; /* socket描述符 */
int snd_size = 0; /* 傳送緩衝區大小 */
int rcv_size = 0; /* 接收緩衝區大小 */
socklen_t optlen; /* 選項值長度 */
/*
* 建立一個TCP套接字
*/
s = socket(PF_INET,SOCK_STREAM,0);
if( s == -1){
printf("建立套接字錯誤\n");
return -1;
}
/*
* 先讀取緩衝區設定的情況
* 獲得 原始傳送緩衝區大小
*/
optlen = sizeof(snd_size);
err = getsockopt(s, SOL_SOCKET, SO_SNDBUF,&snd_size, &optlen);
if(err<0){
printf("獲取傳送緩衝區大小錯誤\n");
}
/*
* 列印原始緩衝區設定情況
*/
/*
* 獲得 原始接收緩衝區大小
*/
optlen = sizeof(rcv_size);
err = getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcv_size, &optlen);
if(err<0){
printf("獲取接收緩衝區大小錯誤\n");
}
printf(" 傳送緩衝區原始大小為: %d 位元組\n",snd_size);
printf(" 接收緩衝區原始大小為: %d 位元組\n",rcv_size);
/*
* 設定傳送緩衝區大小
*/
snd_size = 10*1024; /* 傳送緩衝區大小為8K */
optlen = sizeof(snd_size);
err = setsockopt(s, SOL_SOCKET, SO_SNDBUF, &snd_size, optlen);
if(err<0){
printf("設定傳送緩衝區大小錯誤\n");
}
/*
* 設定接收緩衝區大小
*/
rcv_size = 10*1024; /* 接收緩衝區大小為8K */
optlen = sizeof(rcv_size);
err = setsockopt(s,SOL_SOCKET,SO_RCVBUF, (char *)&rcv_size, optlen);
if(err<0){
printf("設定接收緩衝區大小錯誤\n");
}
/*
* 檢查上述緩衝區設定的情況
* 獲得修改後傳送緩衝區大小
*/
optlen = sizeof(snd_size);
err = getsockopt(s, SOL_SOCKET, SO_SNDBUF,&snd_size, &optlen);
if(err<0){
printf("獲取傳送緩衝區大小錯誤\n");
}
/*
* 獲得修改後接收緩衝區大小
*/
optlen = sizeof(rcv_size);
err = getsockopt(s, SOL_SOCKET, SO_RCVBUF,(char *)&rcv_size, &optlen);
if(err<0){
printf("獲取接收緩衝區大小錯誤\n");
}
/*
* 列印結果
*/
printf(" 傳送緩衝區大小為: %d 位元組\n",snd_size);
printf(" 接收緩衝區大小為: %d 位元組\n",rcv_size);
close(s);
return 0;
}
執行結果(QNX系統中):