Socket 錯誤分析及錯誤碼
平臺:xp sp3+vc6。
測試方法:
retval = function(....)
if(retval == SOCKET_ERROR)
r = WSAGetLastError();
各類狀況下的返回值由retval取得。
錯誤號由r取得。
錯誤查詢使用vc6自帶的tool:「Error Lookup」
1。socket
SOCKET socket( int
af
, int
type
, int
protocol
);
》》af,一般爲AF_INET
使用AF_ISO等其餘地址族標識,而非AF_INET。
返回:-1。
錯誤:10047(使用了與請求的協議不兼容的地址)
》》type,一般爲SOCK_STREAM或SOCK_DGRAM
頭文件中定義的只有以下幾種標準類型:
#define SOCK_STREAM 1 /* stream socket */
#define SOCK_DGRAM 2 /* datagram socket */
#define SOCK_RAW 3 /* raw-protocol interface */
#define SOCK_RDM 4 /* reliably-delivered message */
#define SOCK_SEQPACKET 5 /* sequenced packet stream */
使用非如上定義的類型。
返回:-1。
錯誤:10044(在這個地址家族中不存在對指定的插槽類型的支持)
》》protocol,一般爲0
type = SOCK_STREAM,protocol = 6
正常
type = SOCK_STREAM,protocol = 7
返回:-1
錯誤號:10043(請求的協議尚未在系統中配置,或者沒有它存在的跡象)
type = SOCK_DGRAM,protocol = 17
正常
type = SOCK_DGRAM,protocol = 19
返回:-1
錯誤號:10043(請求的協議尚未在系統中配置,或者沒有它存在的跡象)
》》結論
Socket暫時只支持AF_INET協議族。
對非標準的套接字類型不支持。
協議號參數能夠爲0,則使用與套接字類型相應的協議號;
不然,協議號參數必須與相應的套接字類型相同。
2。bind
int bind( SOCKET
s
, const struct sockaddr FAR*
name
, int
namelen
);
》》s
在沒有用socket申請資源的套接字上操做。
返回:-1
錯誤號:10038(在一個非套接字上嘗試了一個操做)
》》name,一般使用AF_INET地址族、INADDR_ANY(0)地址
a、在local結構中,sin_family成員賦值爲AF_OSI,
返回: -1
錯誤碼:10047(使用了與請求的協議不兼容的地址)
b、在local結構中,sin_addr成員賦值爲本計算機的IP地址,
正常
c、在local結構中,sin_addr成員賦值爲非本計算機的IP地址,如同小組的另外一個同窗的IP地址;
返回: -1
錯誤碼:10049( 在其上下文中,該請求的地址無效)
d、在local結構中,sin_port成員賦值爲135;
返回: -1
錯誤碼:10048(一般每一個套接字地址(協議/網絡地址/端口)只容許使用一次)
》》namelen,一般爲name所指的結構的大小,如sizeof(SOCKADDR_IN)
namelen = 10
返回: -1
錯誤碼:10014(系統檢測到在一個調用中嘗試使用指針參數時的無效指針地址)
namelen = 16
返回: 0
正常
namelen = 40
返回: 0
正常
》》結論
能夠bind本機擁有的地址(或INADDR_ANY),非本機擁有的地址出錯。
bind已經被佔用的端口值會出錯。
len參數要
大於等於
地址結構實際上所佔的長度。
》》思考
由於本機能夠有多個IP,因此須要有方法指出從哪一個實體接收數據。
固然,提供一種表達「從全部實體接收」的方法是必要的。
在頭文件中INADDR_ANY被明肯定義爲0。
關於bind
已佔用的端口
。
是指端口被bind,而且上層仍是活的。(不設置複用)
處於TIME-WAIT狀態的端口表面上是被佔用,
其實是能夠bind成功的,但connect會失敗。
詳見關於
TIME-WAIT的筆記
,第六條。
3。listen
int listen( SOCKET
s
, int
backlog
);
》》s
使用還沒有半相關的套接字。(未成功bind的)
返回:-1
錯誤號:10022(提供了一個無效的參數)
》》backlog
純引用一段:(無出處)
「windows套接字實現中最多隻容許服務器同時監聽5個套接字。
使用參數0,則系統將把該參數改成1,而使用超過5的值,系統將自動把該參數改成5。」
設置參數值爲0,有 1 個客戶機可同時與服務器鏈接
(在vista下有時有2個能夠鏈接,有時有3個能夠鏈接,-_0//)
設置參數值爲1,有 1 個客戶機可同時與服務器鏈接
設置參數值爲10,有 10 個客戶機可同時與服務器鏈接
》》結論
第一個參數的套接字必須是成功bind事後的。
監聽個數爲0的話,會自動設置爲1。
最大監聽個數在XP SP3下能夠超過5。
》》問題
如何得到實際的backlog值?
MSDN: There is no standard provision to obtain the actual backlog value.
如何結束套接字的監聽狀態?
首先,close掉是能夠的。若是不close呢?
最初猜測backlog爲0,-1等特殊值能夠達到此效果,結果失敗。
求解。
4。accept
SOCKET accept( SOCKET
s
, struct sockaddr FAR*
addr
, int FAR*
addrlen
);
》》s
在沒有listen的套接字上面。
返回:-1
錯誤號:10022(提供了一個無效的參數)
》》addr,輸出參數,通常不填
單機測試,填上本機的IP和某個端口號。
結果:沒法限制所接收的地址,執行完後addr中存放實際的地址。
》》addrlen
同bind
》》結論
主套接字必須處於監聽狀態。
在地址字段填上任何值不能限制接受的鏈接。
len參數要求所攜帶的值大於等於16。(AF_INET地址結構的長度)
5。recv
int recv( SOCKET
s
, char FAR*
buf
, int
len
, int
flags
);
對於服務器,通常是
ns = accept(s , &addr , &len)
;
》》s,通常是用上面accept正常返回的值
在沒有accept的從套接字上操做。(上面的ns)
返回:-1
錯誤號:10038(在一個非套接字上嘗試了一個操做)
在主套接字上操做。(上面的s)
返回:-1
錯誤號:10057(因爲套接字沒有鏈接而且
(當使用一個 sendto 調用發送數據報套接字時)沒有提供地址,
發送或接收數據的請求沒有被接受。 )
》》buf,要求指向一個有效的緩衝區
若是指向的無效的內存區域
返回: -1
錯誤號:10014(系統檢測到在一個調用中嘗試使用指針參數時的無效指針地址)
》》len,發送的字節數
len過長可能形成緩衝區溢出。
這個屬於編程中的廣泛考慮問題,不是socket函數特有。
》》flags,通常用0
設置MSG_PEEK標誌後,接收但不移除數據。
(再次接收可獲得相同的數據)
》》結論
對服務器來講,必須傳遞成功accept以後返回的套接字。
緩衝區指針所指位置必須有效。
緩衝區長度參數不可超過實際準備的緩衝區長度。
MSG_PEEK標誌在接收的時候將保留數據。
6。send
int send( SOCKET
s
, const char FAR*
buf
, int
len
, int
flags
);
》》s,同recv
在沒有accept的從套接字上操做。(上面的ns)
返回:-1
錯誤號:10038(在一個非套接字上嘗試了一個操做)
在主套接字上操做。(上面的s)
返回:-1
錯誤號:10057(因爲套接字沒有鏈接而且
(當使用一個 sendto 調用發送數據報套接字時)沒有提供地址,
發送或接收數據的請求沒有被接受。 )
》》buf
必須指向有效緩衝區,同recv
》》len
必須和要發送的數據長度一致。
》》結論
除flag可選項不一樣外,和recv一致。
7。closesocket
int closesocket( SOCKET
s
);
》》s
在申請套接字資源(調用socket)以前closesocket
返回: -1
錯誤號:10038(在一個非套接字上嘗試了一個操做)
再已經closesocket的套接字上closesocket
返回: -1
錯誤號:10038(在一個非套接字上嘗試了一個操做)
》》結論
s必須是有效打開的套接字。
不得重複關閉。
8。connect
int connect( SOCKET
s
, const struct sockaddr FAR*
name
, int
namelen
);
》》沒有對端響應的狀況
在沒有運行服務器的狀況下,connect是否會一直阻塞?
結果:等待必定時間後返回錯誤。
返回: -1
錯誤碼:10061(因爲目標機器積極拒絕,沒法鏈接)
》》s
沒有使用過bind的套接字。
成功鏈接。
產生隱式綁定,
相關應用的詳細資料
。
》》name
使用一些特殊的地址來測試。
a、 使用遠端點IP地址爲INADDR_ANY測試。
返回:-1
錯誤號:10049(在其上下文中,該請求的地址無效)
b、 使用遠端點IP地址爲10.1.1.255廣播地址。
返回: -1
錯誤號:10060(因爲鏈接方在一段時間後沒有正確答覆或鏈接的主機沒有反應,鏈接嘗試失敗)
》》namelen
同bind等須要傳遞地址結構長度的函數
》》結論
服務器必須啓動listen。
能夠不創建本地半相關,則進行隱式綁定。
客戶不能夠與INADDR_ANY主動相連,當即返回報錯。
客戶不能夠與廣播地址鏈接,會等待好久,返回失敗。
》》討論
10060和10061兩種錯誤不一樣。
其中10061解釋爲目標機器積極拒絕,返回錯誤很快(秒級)。
10060的狀況,返回錯誤須要很長時間(幾十秒級)。
此處值得深刻研究,兩種狀況下的抓包應該不同。
9。recvfrom
int recvfrom( SOCKET
s
, char FAR*
buf
, int
len
, int
flags
, struct sockaddr FAR*
from
, int FAR*
fromlen
);
》》缺乏半相關
在沒有bind的套接字上面,直接recvfrom。
返回:-1
錯誤號:10022(提供了一個無效的參數)
》》填寫from結構
向其中填入非對端使用的地址或端口。
正常接收,而且from內置對端地址信息。
》》結論
必須先進行本地半相關,指定端口,纔可以接收。
沒法經過recvfrom的地址結構限制接收的地址和端口。
10。sendto
int sendto( SOCKET
s
, const char FAR*
buf
, int
len
, int
flags
, const struct sockaddr FAR*
to
, int
tolen
);
》》缺乏半相關
在沒有bind的套接字上面,直接sendto。
成功。
返回發送的數據個數。
》》不存在的對端實體
sendto到一個不存在的實體(to結構)
返回:發送的字符數
錯誤號:無
緊接着調用recvfrom
結果:沒有阻塞,直接返回
返回:-1
錯誤號:10054 (遠程主機強迫關閉了一個現有的鏈接。)
》》結論
能夠在未本地半相關的狀況下發送數據。
由系統隨機選擇端口。
能夠向不存在的遠端點發送數據,
本地仍然報告發送的字節數(
無論有沒有人接收
)。
通常狀況下,沒有數據的時候recvfrom會阻塞。
可是當給不存在的對端發送過數據後,會收到錯誤報告,
緊接着的一次recvfrom會當即返回失敗。(後面的仍然阻塞)
》》思考
SOCK_DGRAM類型的服務,
沒法爲用戶確保數據的正常交付。
可是經過recvfrom返回的錯誤,
能夠對發送狀況做出必定的判斷。
這也啓示在使用SOCK_DGRAM時候的編程框架要考慮下,
當recvfrom錯誤的時候,判斷一下錯誤號,再進一步處理。
猜想
當對端沒有bind的時候,使用icmp的端口不可達通知。
須要進一步驗證。
歡迎關注本站公眾號,獲取更多信息