recv,recvfrom,send,sendto
一般情況下:
send(),recv()用於TCP,sendto()及recvfrom()用於UDP
但是send(),recv()也可以用於UDP,sendto()及recvfrom()也可以用於TCP
send函數
int send( SOCKET s, const char FAR *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 FAR *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信號,進程對該信號的默認處理是進程終止。
sendto和recvfrom一般用於UDP協議中,但是如果在TCP中connect函數調用後也可以用.
sendto()和recvfrom()— —利用數據報方式進行數據傳輸
在無連接的數據報socket方式下,由於本地socket並沒有與遠端機器建立連接,所以在發送數據時應指明目的地址,sendto()函數原型為:
int sendto(int sockfd, const void *msg,int len unsigned int flags, const struct sockaddr *to, int tolen);
該函數比send()函數多了兩個參數,to表示目地機的IP地址和端口號信息,而tolen常常被賦值為sizeof (struct sockaddr)。Sendto 函數也返回實際發送的數據字節長度或在出現發送錯誤時返回-1。
recvfrom()函數原型為:
int recvfrom(int sockfd,void *buf,int len,unsigned int lags,struct sockaddr *from,int *fromlen);
from是一個struct sockaddr類型的變量,該變量保存源機的IP地址及端口號。fromlen常置為sizeof (struct sockaddr)。當recvfrom()返回時,fromlen包含實際存入from中的數據字節數。Recvfrom()函數返回接收到的字節數或當出現錯誤時返回-1,並置相應的errno。
應註意的一點是,當你對於數據報socket調用了connect()函數時,你也可以利用send()和recv()進行數據傳輸,但該socket仍然是數據報socket,並且利用傳輸層的UDP服務。但在發送或接收數據報時,內核會自動為之加上目地和源地址信息。
與recv()函數的比較:UDP使用recvfrom()函數接收數據,他類似於標準的read(),但是在recvfrom()函數中要指明目的地址。從套接字上接收一個消息。對於recvfrom ,可同時應用於面向連接的和無連接的套接字。recv一般只用在面向連接的套接字,幾乎等同於recvfrom,只要將recvfrom的第五個參數設置NULL。不管是recv還是recvfrom,都有兩種模式,阻塞和非阻塞,可以通過ioctl函數來設置。阻塞模式是一直等待直到有數據到達,非阻塞模式是立即返回,需要通過消息,異步事件等來查詢完成狀態。
本文轉載於:http://blog.csdn.net/xvd217/article/details/38902081
騰訊面試題 Linux Socket TCP Recv的返回值>0是表示從接收緩沖區實際Copy了這麽多個字節的數據出來,也就是表示系統已實際收到發送方發過來的這麽多個數據或者更多的數據.返回值為0則表示服務器已主動斷開連接,也就是已收到了服務器發過來的Fin或者Rst.若返回值等於-1的話,且(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN),則表示接收緩沖區已無數據可讀,但連接是正常的,可以稍後再嘗試讀.否則表示連接發生異常,連接已斷開,無效描述符之類的. Linux Socket TCP Send的返回值>0是表示已經發送了的字節數(未被確認的),例如要發送10個字節,在異步模式下返回值為5個字節,則表示已經Copy了5個字節到發送緩沖區,系統隨後會發送這些數據,但這5個字節是未被確認的,也就是還沒收到接收方的Ack,如果服務器無回應或網絡斷開,那麽系統將會不斷重發該數據包,直到收到這5個字節的Ack.而剩下的5個未發的字節則由應用程序調用Send再發.若返回值為0則表示服務器已主動斷開連接,也就是已收到了服務器發過來的Fin或者Rst.若返回值等於-1的話,並且(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN),則表示系統在忙或者發送緩沖區已滿了,但連接是正常的,可以稍後再嘗試發.否則表示連接發生異常,連接已斷開,無效描述符之類的. UDP的sendto對應著recvfrom,一發一收.如果sendto的數據大於MTU,則會在IP層分片發送,到達目標後由IP層重組,再從recvfrom一次性返回.如果使用IP層分片重組則存在亂序,丟包,重包的問題.調用一次sendto,只要數據長度小於MTU都會以一個獨立的UDP包發送.recvfrom的接收大小必須大於或等於sendto時的是數據大小.更正確的說法應該是UDP的包單位是以IP層的包為單位的.
recv,recvfrom,send,sendto