一、TCP協(xié)議、Socket編程流程TCP/IP協(xié)議及socket封裝 二、Send 和 Recv的基本介紹2.1 Send函數(shù)int send( SOCKET s, const char FAR *buf, int len, int flags );
不論是客戶(hù)還是服務(wù)器應(yīng)用程序都用send函數(shù)來(lái)向TCP連接的另一端發(fā)送數(shù)據(jù)??蛻?hù)程序一般用send函數(shù)向服務(wù)器發(fā)送請(qǐng)求,而服務(wù)器則通常用send函數(shù)來(lái)向客戶(hù)程序發(fā)送應(yīng)答。 參數(shù)說(shuō)明: 第一個(gè)參數(shù)指定發(fā)送端套接字描述符;第二個(gè)參數(shù)指明一個(gè)存放應(yīng)用程序要發(fā)送數(shù)據(jù)的緩沖區(qū);第三個(gè)參數(shù)指明實(shí)際要發(fā)送的數(shù)據(jù)的字節(jié)數(shù);第四個(gè)參數(shù)一般置0。12341234 這里只描述同步Socket的send函數(shù)的執(zhí)行流程。當(dāng)調(diào)用該函數(shù)時(shí), 要注意send函數(shù)把buf中的數(shù)據(jù)成功copy到s的發(fā)送緩沖的剩余空間里后它就返回了,但是此時(shí)這些數(shù)據(jù)并不一定馬上被傳到連接的另一端。如果協(xié)議在后續(xù)的傳送過(guò)程中出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤的話(huà),那么下一個(gè)Socket函數(shù)就會(huì)返回SOCKET_ERROR。(每一個(gè)除send外的Socket函數(shù)在執(zhí)行的最開(kāi)始總要先等待套接字的發(fā)送緩沖中的數(shù)據(jù)被協(xié)議傳送完畢才能繼續(xù),如果在等待時(shí)出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤,那么該Socket函數(shù)就返回 SOCKET_ERROR) 注意:在Unix系統(tǒng)下,如果send在等待協(xié)議傳送數(shù)據(jù)時(shí)網(wǎng)絡(luò)斷開(kāi)的話(huà),調(diào)用send的進(jìn)程會(huì)接收到一個(gè)SIGPIPE信號(hào),進(jìn)程對(duì)該信號(hào)的默認(rèn)處理是進(jìn)程終止。 通過(guò)測(cè)試發(fā)現(xiàn),異步socket的send函數(shù)在網(wǎng)絡(luò)剛剛斷開(kāi)時(shí)還能發(fā)送返回相應(yīng)的字節(jié)數(shù),同時(shí)使用select檢測(cè)也是可寫(xiě)的,但是過(guò)幾秒鐘之后,再send就會(huì)出錯(cuò)了,返回-1。select也不能檢測(cè)出可寫(xiě)了。 2.2 Recv函數(shù)int recv( SOCKET s, char FAR *buf, int len, int flags);
不論是客戶(hù)還是服務(wù)器應(yīng)用程序都用recv函數(shù)從TCP連接的另一端接收數(shù)據(jù)。 參數(shù)說(shuō)明: 第一個(gè)參數(shù)指定接收端套接字描述符;第二個(gè)參數(shù)指明一個(gè)緩沖區(qū),該緩沖區(qū)用來(lái)存放recv函數(shù)接收到的數(shù)據(jù);第三個(gè)參數(shù)指明buf的長(zhǎng)度;第四個(gè)參數(shù)一般置0。12341234 這里只描述同步Socket的recv函數(shù)的執(zhí)行流程。當(dāng)應(yīng)用程序調(diào)用recv函數(shù)時(shí),recv先等待s的發(fā)送緩沖中的數(shù)據(jù)被協(xié)議傳送完畢, 特別提醒: 如果recv在copy時(shí)出錯(cuò),那么它返回SOCKET_ERROR; 注意:在Unix系統(tǒng)下,如果recv函數(shù)在等待協(xié)議接收數(shù)據(jù)時(shí)網(wǎng)絡(luò)斷開(kāi)了,那么調(diào)用recv的進(jìn)程會(huì)接收到一個(gè)SIGPIPE信號(hào),進(jìn)程對(duì)該信號(hào)的默認(rèn)處理是進(jìn)程終止。 int send ( SOCKET s, const char FAR * buf, int len, int flags );
三、常見(jiàn)問(wèn)題問(wèn)題1:send函數(shù)每次最多可以發(fā)送多少數(shù)據(jù)?是int的最大值嗎? 問(wèn)題二:如果buffer中的數(shù)據(jù)過(guò)大,我也只需要調(diào)用一次send函數(shù),而底層到底是一次傳輸成功還是陸續(xù)傳輸我不用管了嗎? 問(wèn)題三:阻塞和非阻塞的區(qū)別? 特別注意:You can use setsockopt to enlarge the buffer. 作為一個(gè)套接字,它擁有兩個(gè)緩沖,接收數(shù)據(jù)緩沖和發(fā)送數(shù)據(jù)緩沖(此緩沖不同與你自己定義的緩沖),當(dāng)有數(shù)據(jù)到達(dá)時(shí),首先進(jìn)入的 問(wèn)題四:緩沖區(qū)怎么理解? recv函數(shù)也是一樣的,它并不是直接從網(wǎng)絡(luò)中獲取數(shù)據(jù),而是從輸入緩沖區(qū)中讀取數(shù)據(jù)。 輸入輸出緩沖區(qū),系統(tǒng)會(huì)為每個(gè)socket都單獨(dú)分配,并且是在socket創(chuàng)建的時(shí)候自動(dòng)生成的。一般來(lái)說(shuō),默認(rèn)的輸入輸出緩沖區(qū)大小為8K。套接字關(guān)閉的時(shí)候,輸出緩沖區(qū)的數(shù)據(jù)不會(huì)丟失,會(huì)由協(xié)議發(fā)送到另一方;而輸入緩沖區(qū)的數(shù)據(jù)則會(huì)丟失。 四、 對(duì)于數(shù)據(jù)接收不完整的情況,可以考慮從以下幾個(gè)方面解決:1:利用SetSocketOpt()函數(shù)將接收方套接子接收緩沖設(shè)為足夠大??; 具體操作:在send()的時(shí)候,返回的是實(shí)際發(fā)送出去的字節(jié)(同步)或發(fā)送到socket緩沖區(qū)的字節(jié)(異步);系統(tǒng)默認(rèn)的狀態(tài)發(fā)送和接收一次為8688字節(jié)(約為8.5K);在實(shí)際的過(guò)程中發(fā)送數(shù)據(jù)和接收數(shù)據(jù)量比較大,可以設(shè)置socket緩沖區(qū),而避免了send(),recv()不斷的循環(huán)收發(fā): // 接收緩沖區(qū)int nRecvBuf=32*1024;//設(shè)置為32Ksetsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));//發(fā)送緩沖區(qū)int nSendBuf=32*1024;//設(shè)置為32Ksetsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));123456123456 2.基于winsock API,比較實(shí)用,自己寫(xiě)的,簡(jiǎn)單又粗暴同時(shí)還有技巧~ bool SendAll(SOCKET &sock, char*buffer, int size){ while (size>0) { int SendSize= send(sock, buffer, size, 0); if(SOCKET_ERROR==SendSize) return false; size = size - SendSize;//用于循環(huán)發(fā)送且退出功能 buffer+=SendSize;//用于計(jì)算已發(fā)buffer的偏移量 } return true;}bool RecvAll(SOCKET &sock, char*buffer, int size){ while (size>0)//剩余部分大于0 { int RecvSize= recv(sock, buffer, size, 0); if(SOCKET_ERROR==RecvSize) return false; size = size - RecvSize; buffer+=RecvSize; } return true;}
3.設(shè)置為阻塞方式: int flags = fcntl(sock, F_GETFL, 0); fcntl(sock, F_SETFL, flags | O_NONBLOCK);1212 假設(shè)當(dāng)前代碼為服務(wù)器,并且已經(jīng)執(zhí)行過(guò)如下代碼, 當(dāng)sock為阻塞模式,調(diào)用accept會(huì)阻塞直到一個(gè)請(qǐng)求到來(lái) 當(dāng)sock為非阻塞模式,accept會(huì)返回-1,errno設(shè)置為EAGAIN或者EWOULDBLOCK 3.在recv函數(shù)之前加sleep(0.01)函數(shù),而不是recv之后,但是感覺(jué)這樣沒(méi)什么效果。 五、 參考資料,特此感謝,如有侵權(quán),立刪!1.關(guān)于setsockopt:https://blog.csdn.net/youxiazzz12/article/details/25634143 |
|