1. 程式人生 > >socket函式send和recv函式

socket函式send和recv函式

在傳送端,一次傳送4092個位元組,
在接收端,一次接收4092個位元組,
但是在接收端,偶爾會出現 socket.receive 接收不全的情況 ,

ret 
= sock.recv(bBuffer,iBufferLen,0); //也有可能無法收到全部資料!
必須要考慮0 < ret < iBufferLen的情況:繼續接收iBufferLen - ret位元組,然後合併
注意第recv函式的第四個引數:
MSG_WAITALL The receive request will complete only when one of the following events occurs:
  • The buffer supplied by the caller is completely full.
  • The connection has been closed.
  • The request has been canceled.
Note that if the underlying transport does not support MSG_WAITALL, or if the socket is in a non-blocking mode, then this call will fail with WSAEOPNOTSUPP. Also, if MSG_WAITALL is specified along with MSG_OOB, MSG_PEEK, or MSG_PARTIAL, then this call will fail with WSAEOPNOTSUPP. This flag is not supported on datagram sockets or message-oriented CO sockets.


Socket的Send,Recv的長度問題:

一個包沒有固定長度,乙太網限制在46-1500位元組,1500就是乙太網的MTU,超過這個量,TCP會為IP資料報設定偏移量進行分片傳輸,現在一般可允許應用層設定8k(NTFS系統)的緩衝區,8k的資料由底層分片,而應用層看來只是一次傳送。
windows的緩衝區經驗值是4k。
Socket本身分為兩種,流(TCP)和資料報(UDP),你的問題針對這兩種不同使用而結論不一樣。甚至還和你是用阻塞、還是非阻塞Socket來程式設計有關。
1、通訊長度,這個是你自己決定的,沒有系統強迫你要發多大的包,實際應該根據需求和網路狀況來決定。對於TCP,這個長度可以大點,但要知道,Socket內部預設的收發緩衝區大小大概是8K,你可以用SetSockOpt來改變。但對於UDP,就不要太大,一般在1024至10K。注意一點,你無論發多大的包,IP層和鏈路層都會把你的包進行分片傳送,一般區域網就是1500左右,廣域網就只有幾十位元組。分片後的包將經過不同的路由到達接收方,對於UDP而言,要是其中一個分片丟失,那麼接收方的IP層將把整個傳送包丟棄,這就形成丟包。顯然,要是一個UDP發包佷大,它被分片後,鏈路層丟失分片的機率就佷大,你這個UDP包,就佷容易丟失,但是太小又影響效率。最好可以配置這個值,以根據不同的環境來調整到最佳狀態。
send()函式返回了實際傳送的長度,在網路不斷的情況下,它絕不會返回(傳送失敗的)錯誤,最多就是返回0。對於TCP你可以寫一個迴圈傳送。當send函式返回SOCKET_ERROR時,才標誌著有錯誤。但對於UDP,你不要寫迴圈傳送,否則將給你的接收帶來極大的麻煩。所以UDP需要用SetSockOpt來改變Socket內部Buffer的大小,以能容納你的發包。明確一點,TCP作為流,發包是不會整包到達的,而是源源不斷的到,那接收方就必須組包。而UDP作為訊息或資料報,它一定是整包到達接收方。
2、關於接收,一般的發包都有包邊界,首要的就是你這個包的長度要讓接收方知道,於是就有個包頭資訊,對於TCP,接收方先收這個包頭資訊,然後再收包資料。一次收齊整個包也可以,可要對結果是否收齊進行驗證。這也就完成了組包過程。UDP,那你只能整包接收了。要是你提供的接收Buffer過小,TCP將返回實際接收的長度,餘下的還可以收,而UDP不同的是,餘下的資料被丟棄並返回WSAEMSGSIZE錯誤。注意TCP,要是你提供的Buffer佷大,那麼可能收到的就是多個發包,你必須分離它們,還有就是當Buffer太小,而一次收不完Socket內部的資料,那麼Socket接收事件(OnReceive),可能不會再觸發,使用事件方式進行接收時,密切注意這點。這些特性就是體現了流和資料包的區別。
補充一點,接收BuffSize 
>= 傳送BuffSize >= 實際傳送Size,對於內外部的Buffer都適用,上面講的主要是Socket內部的Buffer大小關係。
3、TCP是有多少就收多少,如果沒有當然阻塞Socket的recv就會等,直到有資料,非阻塞Socket不好等,而是返回WSAEWOULDBLOCK。UDP,如果沒有資料,阻塞Socket就會等,非阻塞Socket也返回WSAEWOULDBLOCK。如果有資料,它是會等整個發包到齊,並接收到整個發包,才返回。

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

不論是客戶還是伺服器應用程式都用send函式來向TCP連線的另一端傳送資料。

客戶程式一般用send函式向伺服器傳送請求,而伺服器則通常用send函式來向客戶程式傳送應答。

該函式的第一個引數指定傳送端套接字描述符;

第二個引數指明一個存放應用程式要傳送資料的緩衝區;

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

第四個引數一般置0。

這裡只描述同步Socket的send函式的執行流程。當呼叫該函式時,send先比較待發送資料的長度len和套接字s的傳送緩衝的 長度,如果len大於s的傳送緩衝區的長度,該函式返回SOCKET_ERROR;如果len小於或者等於s的傳送緩衝區的長度,那麼send先檢查協議 是否正在傳送s的傳送緩衝中的資料,如果是就等待協議把資料傳送完,如果協議還沒有開始傳送s的傳送緩衝中的資料或者s的傳送緩衝中沒有資料,那麼 send就比較s的傳送緩衝區的剩餘空間和len,如果len大於剩餘空間大小send就一直等待協議把s的傳送緩衝中的資料傳送完,如果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訊號,程序對該訊號的預設處理是程序終止。

recv函式

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

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

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

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

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

第四個引數一般置0。

這裡只描述同步Socket的recv函式的執行流程。當應用程式呼叫recv函式時,recv先等待s的傳送緩衝 中的資料被協議傳送完畢,如果協議在傳送s的傳送緩衝中的資料時出現網路錯誤,那麼recv函式返回SOCKET_ERROR,如果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 
"socketclient.h"
int main()
{
    SocketClient cli;
    
char* szIp    = "127.0.0.1";
    WORD  wPort 
= 5082;
    
int nRet = cli.Open(szIp,wPort);
    
if (nRet!=0)
    {
        printf(
"Open %s:%d error:%d \r\n",szIp,wPort,nRet);
        
return -1;
    }
    
char buf[1600];
    
int i=1;
    DWORD dwTickCount0 
= 0;
    DWORD dwTickCount1 
= 0;
    
int nSended = 0;
    
while (1)
    {
        dwTickCount0 
= cli.GetTickCount();
        
//printf("%d,TickCount(0):%u \r\n",i,dwTickCount0);

        nSended 
= cli.Send(buf,1500); //超時2秒傳送

        dwTickCount1 
= cli.GetTickCount();
        
//printf("%d,TickCount(1):%u \r\n",i,dwTickCount1);
        
//usleep(20*100);
        printf("%d,time:%u,sended:%d,err:%d \r\n",i,dwTickCount1 - dwTickCount0,nSended,errno);
        
if (nSended<1)
        {
            
break;
        }
        i
++;
    }
}

1,time:0,sended:1500,err:0 
2,time:0,sended:1500,err:0 
3,time:0,sended:1500,err:0 
4,time:0,sended:1500,err:0 
5,time:0,sended:1500,err:0 
6,time:0,sended:1500,err:0 
7,time:0,sended:1500,err:0 
8,time:1,sended:1500,err:0 
9,time:0,sended:1500,err:0 
10,time:0,sended:1500,err:0 
11,time:0,sended:1500,err:0 
12,time:0,sended:1500,err:0 
13,time:0,sended:1500,err:0 
14,time:0,sended:1500,err:0 
15,time:0,sended:1500,err:0 
16,time:0,sended:1500,err:0 
17,time:0,sended:1500,err:0 
18,time:0,sended:1500,err:0 
19,time:0,sended:1500,err:0 
20,time:0,sended:1500,err:0 
21,time:0,sended:1500,err:0 
22,time:0,sended:1500,err:0 
23,time:0,sended:1500,err:0 
24,time:0,sended:1500,err:0 
25,time:0,sended:1500,err:0 
26,time:0,sended:1500,err:0 
27,time:0,sended:1500,err:0 
28,time:0,sended:1500,err:0 
29,time:0,sended:1500,err:0 
30,time:0,sended:1500,err:0 
31,time:0,sended:1500,err:0 
32,time:0,sended:1500,err:0 
33,time:0,sended:1500,err:0 
34,time:0,sended:1500,err:0 
35,time:0,sended:1500,err:0 
36,time:0,sended:1500,err:0 
37,time:0,sended:1500,err:0 
38,time:0,sended:1500,err:0 
39,time:0,sended:1500,err:0 
40,time:0,sended:1500,err:0 
41,time:0,sended:1500,err:0 
42,time:0,sended:1500,err:0 
43,time:0,sended:1500,err:0 
44,time:0,sended:1500,err:0 
45,time:0,sended:1500,err:0 
46,time:0,sended:1500,err:0 
47,time:0,sended:1500,err:0 
48,time:39,sended:1500,err:0 
49,time:0,sended:1500,err:0 
50,time:0,sended:1500,err:0 
51,time:0,sended:1500,err:0 
52,time:0,sended:1500,err:0 
53,time:0,sended:1500,err:0 
54,time:0,sended:1500,err:0 
55,time:0,sended:1500,err:0 
56,time:0,sended:1500,err:0 
57,time:0,sended:1500,err:0 
58,time:0,sended:1500,err:0 
59,time:0,sended:1500,err:0 
60,time:0,sended:1500,err:0 
61,time:0,sended:1500,err:0 
62,time:0,sended:1500,err:0 
63,time:0,sended:1500,err:0 
64,time:0,sended:1500,err:0 
65,time:0,sended:1500,err:0 
66,time:0,sended:1500,err:0 
67,time:0,sended:1500,err:0 
68,time:0,sended:1500,err:0 
69,time:0,sended:1500,err:0 
70,time:0,sended:1500,err:0 
71,time:0,sended:1500,err:0 
72,time:0,sended:1500,err:0 
73,time:0,sended:1500,err:0 
74,time:0,sended:1500,err:0 
75,time:0,sended:1500,err:0 
76,time:0,sended:1500,err:0 
77,time:0,sended:1500,err:0 
78,time:0,sended:1500,err:0 
79,time:0,sended:1500,err:0 
80,time:0,sended:1500,err:0 
81,time:0,sended:1500,err:0 
82,time:0,sended:1500,err:0 
83,time:0,sended:1500,err:0 
84,time:0,sended:1500,err:0 
85,time:0,sended:1500,err:0 
86,time:0,sended:1500,err:0 
87,time:0,sended:1500,err:0 
88,time:0,sended:1500,err:0 
89,time:0,sended:1500,err:0 
90,time:0,sended:1500,err:0 
91,time:0,sended:1500,err:0 
92,time:0,sended:1500,err:0 
93,time:0,sended:1500,err:0 
94,time:0,sended:1500,err:0 
95,time:0,sended:1500,err:0 
96,time:0,sended:1500,err:0 
97,time:0,sended:1500,err:0 
98,time:0,sended:1500,err:0 
99,time:0,sended:1500,err:0 
100,time:0,sended:1500,err:0 
101,time:0,sended:1500,err:0 
102,time:0,sended:1500,err:0 
103,time:0,sended:1500,err:0 
104,time:0,sended:1500,err:0 
105,time:0,sended:1500,err:0 
106,time:0,sended:1500,err:0 
107,time:0,sended:1500,err:0 
108,time:0,sended:1500,err:0 
109,time:0,sended:1500,err:0 
110,time:0,sended:1500,err:0 
111,time:0,sended:1500,err:0 
112,time:0,sended:1500,err:0 
113,time:0,sended:1500,err:0 
114,time:0,sended:1500,err:0 
115,time:0,sended:1500,err:0 
116,time:0,sended:1500,err:0 
117,time:0,sended:1500,err:0 
118,time:0,sended:1500,err:0 
119,time:0,sended:1500,err:0 
120,time:0,sended:1500,err:0 
121,time:0,sended:1500,err:0 
122,time:0,sended:1500,err:0 
123,time:0,sended:1500,err:0 
124,time:0,sended:1500,err:0 
125,time:1999,sended:340,err:0 //這裡出現了傳送不全
126,time:0,sended:1500,err:0 
127,time:0,sended:1500,err:0 
128,time:0,sended:1500,err:0 
129,time:0,sended:1500,err:0 
130,time:0,sended:1500,err:0 
131,time:0,sended:1500,err:0 
132,time:0,sended:1500,err:0 
133,time:0,sended:1500,err:0 
134,time:0,sended:1500,err:0 
135,time:0,sended:1500,err:0 
136,time:0,sended:1500,err:0 
137,time:0,sended:1500,err:0 
138,time:0,sended:1500,err:0 
139,time:0,sended:1500,err:0 
140,time:0,sended:1500,err:0 
141,time:0,sended:1500,err:0 
142,time:0,sended:1500,err:0 
143,time:0,sended:1500,err:0 
144,time:0,sended:1500,err:0 
145,time:0,sended:1500,err:0 
146,time:0,sended:1500,err:0 
147,time:2000,sended:1268,err:0 
148,time:2000,sended:-1,err:11

同樣send也會出現和recv一樣,會有傳送不全的現象.