一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

TCP通信接收數(shù)據(jù)不完整的解決方法

 行走在理想邊緣 2021-06-26

一、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 );
  • 1

  • 1

不論是客戶(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先比較待發(fā)送數(shù)據(jù)的長(zhǎng)度len套接字s的發(fā)送緩沖的長(zhǎng)度,
(1)如果len大于s的發(fā)送緩沖區(qū)的長(zhǎng)度,該函數(shù)返回SOCKET_ERROR;
(2)如果len小于或者等于s的發(fā)送緩沖區(qū)的長(zhǎng)度,那么send先檢查協(xié)議
是否正在發(fā)送s的發(fā)送緩沖中的數(shù)據(jù),如果是就等待協(xié)議把數(shù)據(jù)發(fā)送完,如果協(xié)議還沒(méi)有開(kāi)始發(fā)送s的發(fā)送緩沖中的數(shù)據(jù)或者s的發(fā)送緩沖中沒(méi)有數(shù)據(jù),那么 send就比較s的發(fā)送緩沖區(qū)的剩余空間和len,(2.1)如果len大于剩余空間大小send就一直等待協(xié)議把s的發(fā)送緩沖中的數(shù)據(jù)發(fā)送完,(2.2)如果len小于剩余空間大小send就僅僅把buf中的數(shù)據(jù)copy到剩余空間里(注意并不是send把s的發(fā)送緩沖中的數(shù)據(jù)傳到連接的另一端的,而是協(xié)議傳的,send僅僅是把buf中的數(shù)據(jù)copy到s的發(fā)送緩沖區(qū)的剩余空間里)。
如果send函數(shù)copy數(shù)據(jù)成功,就返回實(shí)際copy的字節(jié)數(shù),
如果send在copy數(shù)據(jù)時(shí)出現(xiàn)錯(cuò)誤,那么send就返回SOCKET_ERROR;如果send在等待協(xié)議傳送數(shù)據(jù)時(shí)網(wǎng)絡(luò)斷開(kāi)的話(huà),那么send函數(shù)也返回SOCKET_ERROR。

要注意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);
  • 1

  • 1

不論是客戶(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é)議傳送完畢,
(1)如果協(xié)議在傳送s的發(fā)送緩沖中的數(shù)據(jù)時(shí)出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤,那么recv函數(shù)返回SOCKET_ERROR,
(2)如果s的發(fā)送緩沖中沒(méi)有數(shù)據(jù)或者數(shù)據(jù)被協(xié)議成功發(fā)送完畢后,recv先檢查套接字s的接收緩沖區(qū),如果s接收緩沖區(qū)中沒(méi)有數(shù)據(jù)或者協(xié)議正在接收數(shù)據(jù),那么recv就一直等待,直到協(xié)議把數(shù)據(jù)接收完畢。當(dāng)協(xié)議把數(shù)據(jù)接收完畢,recv函數(shù)就把s的接收緩沖中的數(shù)據(jù)copy到buf中

特別提醒:
協(xié)議接收到的數(shù)據(jù)可能大于buf的長(zhǎng)度,所以在這種情況下要調(diào)用幾次recv函數(shù)才能把s的接收緩沖中的數(shù)據(jù)copy完。recv函數(shù)僅僅是copy數(shù)據(jù),真正的接收數(shù)據(jù)是協(xié)議來(lái)完成的,recv函數(shù)返回其實(shí)際copy的字節(jié)數(shù)。

如果recv在copy時(shí)出錯(cuò),那么它返回SOCKET_ERROR;
如果recv函數(shù)在等待協(xié)議接收數(shù)據(jù)時(shí)網(wǎng)絡(luò)中斷了,那么它返回0。

注意:在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 );
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

三、常見(jiàn)問(wèn)題

問(wèn)題1:send函數(shù)每次最多可以發(fā)送多少數(shù)據(jù)?是int的最大值嗎?
答:不是int的最大值

問(wèn)題二:如果buffer中的數(shù)據(jù)過(guò)大,我也只需要調(diào)用一次send函數(shù),而底層到底是一次傳輸成功還是陸續(xù)傳輸我不用管了嗎?
答:recv到的數(shù)據(jù)流可能是斷斷續(xù)續(xù)的,你要把他們放在一起然后解碼。

問(wèn)題三:阻塞和非阻塞的區(qū)別?
答:Send分為阻塞和非阻塞,
阻塞模式下,如果正常的話(huà),會(huì)直到把你所需要發(fā)送的數(shù)據(jù)發(fā)完再返回;
非阻塞模式,會(huì)根據(jù)你的socket在底層的可用緩沖區(qū)的大小,來(lái)將你的緩沖區(qū)當(dāng)中的數(shù)據(jù)拷貝過(guò)去,有多大緩沖區(qū)就拷貝多少,緩沖區(qū)滿(mǎn)了就立即返回,這個(gè)時(shí)候的返回值,只表示拷貝到緩沖區(qū)多少數(shù)據(jù),但是并不代表發(fā)送多少數(shù)據(jù),同時(shí)剩下的部分需要你再次調(diào)用send才會(huì)再一次拷貝到底層緩沖區(qū)。

特別注意:You can use setsockopt to enlarge the buffer.

作為一個(gè)套接字,它擁有兩個(gè)緩沖,接收數(shù)據(jù)緩沖發(fā)送數(shù)據(jù)緩沖(此緩沖不同與你自己定義的緩沖),當(dāng)有數(shù)據(jù)到達(dá)時(shí),首先進(jìn)入的
就是接收數(shù)據(jù)緩沖,然后用戶(hù)從這個(gè)緩沖中將數(shù)據(jù)讀出來(lái),這就是套接字接受的過(guò)程,這個(gè)緩沖的大小可以自己用**SetSocketOpt()**設(shè)定,
同時(shí)操作系統(tǒng)對(duì)它有一個(gè)默認(rèn)大小,如果對(duì)方在很短時(shí)間內(nèi)發(fā)送大量數(shù)據(jù)到達(dá)這個(gè)套接子時(shí),可能它沒(méi)來(lái)得及接收完,因此接收緩沖處于
滿(mǎn)的狀態(tài),再有數(shù)據(jù)來(lái)的時(shí)候就進(jìn)不去了,因此對(duì)方的 SEND可能就返回錯(cuò)誤,在對(duì)方發(fā)送的數(shù)據(jù)量很小時(shí)不會(huì)出現(xiàn)這種情況,當(dāng)數(shù)據(jù)量很大時(shí),情況就很明顯了,很容易造成收不到的情況。同樣,發(fā)送方的發(fā)送緩沖也有相對(duì)應(yīng)的問(wèn)題。

問(wèn)題四:緩沖區(qū)怎么理解?
答:socket緩沖區(qū)每一個(gè)socket在被創(chuàng)建之后,系統(tǒng)都會(huì)給它分配兩個(gè)緩沖區(qū),即輸入緩沖區(qū)和輸出緩沖區(qū)。
在這里插入圖片描述
send函數(shù)并不是直接將數(shù)據(jù)傳輸?shù)骄W(wǎng)絡(luò)中,而是負(fù)責(zé)將數(shù)據(jù)寫(xiě)入輸出緩沖區(qū),數(shù)據(jù)從輸出緩沖區(qū)發(fā)送到目標(biāo)主機(jī)是由TCP協(xié)議完成的。數(shù)據(jù)寫(xiě)入到輸出緩沖區(qū)之后,send函數(shù)就可以返回了,數(shù)據(jù)是否發(fā)送出去,是否發(fā)送成功,何時(shí)到達(dá)目標(biāo)主機(jī),都不由它負(fù)責(zé)了,而是由協(xié)議負(fù)責(zé)。

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í)還有技巧~
這樣包裝的目的顯而易見(jiàn),防止send或者 recv不完整,這樣你想發(fā)一個(gè)
幾MB直接調(diào)用下面方法就okay,不會(huì)少發(fā)~

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;}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

3.設(shè)置為阻塞方式:
阻塞就是干不完不準(zhǔn)回來(lái)!
非阻塞就是你先干,我現(xiàn)看看有其他事沒(méi)有,完了告訴我一聲!
sock默認(rèn)為阻塞模式,下面的代碼可對(duì)sock設(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)什么效果。
4:在發(fā)送方進(jìn)行數(shù)據(jù)發(fā)送時(shí)判斷發(fā)送是否成功,如果不成功重發(fā);
5:要求接收方收到數(shù)據(jù)后給發(fā)送方回應(yīng),發(fā)送方只在收到回應(yīng)后才發(fā)送下一條數(shù)據(jù)。

五、 參考資料,特此感謝,如有侵權(quán),立刪! 

1.關(guān)于setsockopt:https://blog.csdn.net/youxiazzz12/article/details/25634143
2.socket循環(huán)接收:https://blog.csdn.net/what951006/article/details/75114563
3.https://blog.csdn.net/u010270148/article/details/53605339
4.https://blog.csdn.net/zorelemn/article/details/52566397
6.https://blog.csdn.net/u012801153/article/details/79363575
7.C++ Socket send recv 循環(huán)發(fā)送和接收 阻塞與緩沖區(qū):
https://blog.csdn.net/u012801153/article/details/79363575
8.從 Linux 源碼看 socket 的阻塞和非阻塞
http://blog./113858/
9.Linux 編程之 Socket
http://blog./107743/?utm_source=blog.&utm_medium=relatedPosts
10.再次深入理解TCP網(wǎng)絡(luò)編程中的send和recv
https://www.cnblogs.com/my_life/articles/5363527.html

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多

    亚洲内射人妻一区二区| 九九热这里只有精品哦| 国产原创中文av在线播放| 欧美成人一区二区三区在线| 精品国产亚洲av久一区二区三区| 九九热这里只有免费精品| 九九九热在线免费视频| 中文字幕日韩欧美一区| 黑人巨大精品欧美一区二区区| 一个人的久久精彩视频| 午夜精品麻豆视频91| 国产精品欧美一级免费| 日本深夜福利视频在线| 成人精品国产亚洲av久久| 国产精品亚洲一级av第二区| 久久这里只精品免费福利| 亚洲欧洲日韩综合二区| 国产又粗又长又大高潮视频| 国产亚洲欧美一区二区| 麻豆视传媒短视频免费观看| 欧洲亚洲精品自拍偷拍| 亚洲第一区二区三区女厕偷拍 | av国产熟妇露脸在线观看| 国产精品蜜桃久久一区二区| 国产又黄又猛又粗又爽的片| 男人大臿蕉香蕉大视频| 亚洲综合天堂一二三区| 日本一级特黄大片国产| 夫妻性生活一级黄色录像| 男女激情视频在线免费观看| 国产色第一区不卡高清| 久久永久免费一区二区| 免费性欧美重口味黄色| 成人精品网一区二区三区| 国产欧美日韩视频91| 激情爱爱一区二区三区| 国产成人精品资源在线观看| 久久偷拍视频免费观看| 久久精品一区二区少妇| 人妻内射在线二区一区| 午夜成年人黄片免费观看|