1. 首先來(lái)看一下recv函數(shù)的各個(gè)參數(shù)函數(shù)原型:int recv( SOCKET s, char *buf, int len, int flags) 功能:不論是客戶還是服務(wù)器應(yīng)用程序都用recv函數(shù)從TCP連接的另一端接收數(shù)據(jù)。 參數(shù)一:指定接收端套接字描述符; 參數(shù)二:指明一個(gè)緩沖區(qū),該緩沖區(qū)用來(lái)存放recv函數(shù)接收到的數(shù)據(jù); 參數(shù)三:指明buf的長(zhǎng)度; 參數(shù)四 :一般置為0。 同步Socket的recv函數(shù)的執(zhí)行流程:當(dāng)應(yīng)用程序調(diào)用recv函數(shù)時(shí),recv先等待s的發(fā)送緩沖中的數(shù)據(jù)被協(xié)議傳送完畢, 如果協(xié)議在傳送s的發(fā)送緩沖中的數(shù)據(jù)時(shí)出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤,那么recv函數(shù)返回SOCKET_ERROR; 如果s的發(fā)送緩沖中沒有數(shù)據(jù)或者數(shù)據(jù)被協(xié)議成功發(fā)送完畢后,recv先檢查套接字s的接收緩沖區(qū),如果s接收緩沖區(qū)中沒有數(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。 2.“坑”在哪里,在返回值上,在阻塞與非阻塞上要知道,recv函數(shù)是阻塞的,也就是會(huì)一直等待服務(wù)端發(fā)送來(lái)的數(shù)據(jù)包。如果沒有數(shù)據(jù)包到來(lái),就一直會(huì)等待。 這就直接導(dǎo)致了我用python寫服務(wù)端的時(shí)候出現(xiàn)的一個(gè)解決了很久的錯(cuò)誤,我的代碼很簡(jiǎn)單,就是python服務(wù)端循環(huán)調(diào)用recv函數(shù)接收從客戶端發(fā)來(lái)的一個(gè)文件,如下:
而實(shí)際上只有當(dāng)recv函數(shù)在等待協(xié)議接收數(shù)據(jù)時(shí)網(wǎng)絡(luò)中斷了,它才返回0,這就導(dǎo)致我在服務(wù)端一直沒有反應(yīng),等待了很久之后,我將客戶端關(guān)閉之后,出現(xiàn)了結(jié)果,其實(shí)就是這時(shí)候 len(data)=0 了。 3. 解決方法有哪些呢?后來(lái)搜集資料才發(fā)現(xiàn)recv本身是一個(gè)阻塞的,所以可以通過(guò)以下兩種方法,將recv設(shè)置為非阻塞: 1. socket.setblocking(0) 2. 使用 socket.MSG_DONTWAIT 注意將套接字設(shè)置為非阻塞時(shí),可能會(huì)報(bào)一個(gè)錯(cuò): WinError 10035 無(wú)法立即完成一個(gè)非阻止性套接字操作 這個(gè)錯(cuò)誤就是,recv不阻塞了。在 send 數(shù)據(jù)出去后,服務(wù)端還來(lái)沒來(lái)得急返回?cái)?shù)據(jù),客戶端已經(jīng)跑到了recv這里,而又不阻塞,所以拋出了一個(gè)異常。 要解決這個(gè)問(wèn)題,你需要在每個(gè) recv 函數(shù)前加一句 time.sleep(2), 其實(shí)還可以通過(guò)設(shè)置 socket.settimeout(5) 即超時(shí)時(shí)間來(lái)改變r(jià)ecv的阻塞狀態(tài),間接地將阻塞狀態(tài)變?yōu)榉亲枞麪顟B(tài)。當(dāng)然,你得確保你的超時(shí)時(shí)間足夠使recv函數(shù)接收完所有的數(shù)據(jù)才行。 我就是通過(guò)設(shè)置超時(shí)時(shí)間解決了這個(gè)問(wèn)題,你如果也有同樣的問(wèn)題的話,你也可以試試。 但是設(shè)置超時(shí)在實(shí)際情況中是一個(gè)不是很推薦的方法,后來(lái)在對(duì)socket原理的了解上更改了代碼,解決了這個(gè)問(wèn)題,服務(wù)端把設(shè)置超時(shí)去掉,然后在Android即Java客戶端發(fā)送完所有的數(shù)據(jù)之后,加上如下語(yǔ)句:
就可以解決這個(gè)服務(wù)端阻塞的問(wèn)題。最后遇到問(wèn)題,一定要去多看看別人是怎么解決的,多和有經(jīng)驗(yàn)的人交流交流,不然時(shí)間就在糾結(jié)苦惱中溜走了。當(dāng)然,自己首先要進(jìn)行獨(dú)立思考,并將問(wèn)題一步步放小,找到真正的問(wèn)題癥結(jié)所在,再去下手。最后如果問(wèn)題得到解決了,記得分享一下以供更多人參考哦~ |
|
來(lái)自: 昵稱45893186 > 《待分類》