1. 程式人生 > >淺談TCP/IP網路程式設計中socket的行為

淺談TCP/IP網路程式設計中socket的行為

socket錯誤碼:

EINTR: 4

阻塞的操作被取消阻塞的呼叫打斷。如設定了傳送接收超時,就會遇到這種錯誤。

只能針對阻塞模式的socket。讀,寫阻塞的socket時,-1返回,錯誤號為INTR。另外,如果出現EINTR即errno為4,錯誤描述Interrupted system call,操作也應該繼續。如果recv的返回值為0,那表明連線已經斷開,接收操作也應該結束。

ETIMEOUT:110

1、操作超時。一般設定了傳送接收超時,遇到網路繁忙的情況,就會遇到這種錯誤。

2、伺服器做了讀資料做了超時限制,讀時發生了超時。

3、錯誤被描述為“connect time out”,即“連線超時”,這種情況一般發生在伺服器主機崩潰。此時客戶 TCP 將在一定時間內(依具體實現)持續重發資料分節,試圖從服務 TCP 獲得一個 ACK 分節。當最終放棄嘗試後(此時伺服器未重新啟動),核心將會向客戶程序返回 ETIMEDOUT 錯誤。如果某個中間路由器判定該伺服器主機已經不可達,則一般會響應“destination unreachable”-“目的地不可達”的ICMP訊息,相應的客戶程序返回的錯誤是 EHOSTUNREACH 或ENETUNREACH。當伺服器重新啟動後,由於 TCP 狀態丟失,之前所有的連線資訊也不存在了,此時對於客戶端發來請求將回應 RST。如果客戶程序對檢測伺服器主機是否崩潰很有必要,要求即使客戶程序不主動傳送資料也能檢測出來,那麼需要使用其它技術,如配置 SO_KEEPALIVE Socket 選項,或實現某些心跳函式。

EAGAIN:

1、Send返回值小於要傳送的資料數目,會返回EAGAIN和EINTR。

2、recv 返回值小於請求的長度時說明緩衝區已經沒有可讀資料,但再讀不一定會觸發EAGAIN,有可能返回0表示TCP連線已被關閉。

3、當socket是非阻塞時,如返回此錯誤,表示寫緩衝佇列已滿,可以做延時後再重試.

4、在Linux進行非阻塞的socket接收資料時經常出現Resource temporarily unavailable,errno程式碼為11(EAGAIN),表明在非阻塞模式下呼叫了阻塞操作,在該操作沒有完成就返回這個錯誤,這個錯誤不會破壞socket的同步,不用管它,下次迴圈接著recv就可以。對非阻塞socket而言,EAGAIN不是一種錯誤。

EPIPE:

1、Socket 關閉,但是socket號並沒有置-1。繼續在此socket上進行send和recv,就會返回這種錯誤。這個錯誤會引發SIGPIPE訊號,系統會將產生此EPIPE錯誤的程序殺死。所以,一般在網路程式中,首先遮蔽此訊息,以免發生不及時設定socket程序被殺死的情況。

2、write(..) on a socket that has been closed at the other end will cause a SIGPIPE.

3、錯誤被描述為“broken pipe”,即“管道破裂”,這種情況一般發生在客戶程序不理會(或未及時處理)Socket 錯誤,繼續向服務 TCP 寫入更多資料時,核心將向客戶程序傳送 SIGPIPE 訊號,該訊號預設會使程序終止(此時該前臺程序未進行 core dump)。結合上邊的 ECONNRESET 錯誤可知,向一個 FIN_WAIT2 狀態的服務 TCP(已 ACK 響應 FIN 分節)寫入資料不成問題,但是寫一個已接收了 RST 的 Socket 則是一個錯誤。

EBADF:

read(..) or write(..) on a locally closed socket will return EBADF

EFAULT:

地址錯誤。

EBUSY:

ECONNREFUSED:

1、拒絕連線。一般發生在連線建立時。

拔伺服器端網線測試,客戶端設定keep alive時,recv較快返回0, 先收到ECONNREFUSED (Connection refused)錯誤碼,其後都是ETIMEOUT。

2、an error returned from connect(), so it can only occur in a client (if a client is defined as the party that initiates the connection

ECONNRESET:

1、在客戶端伺服器程式中,客戶端異常退出,並沒有回收關閉相關的資源,伺服器端會先收到ECONNRESET錯誤,然後收到EPIPE錯誤。

2、連線被遠端主機關閉。有以下幾種原因:遠端主機停止服務,重新啟動;當在執行某些操作時遇到失敗,因為設定了“keep alive”選項,連線被關閉,一般與ENETRESET一起出現。

3、遠端端執行了一個“hard”或者“abortive”的關閉。應用程式應該關閉socket,因為它不再可用。當執行在一個UDP socket上時,這個錯誤表明前一個send操作返回一個ICMP“port unreachable”資訊。

4、如果client關閉連線,server端的select並不出錯(不返回-1,使用select對唯一一個socket進行non- blocking檢測),但是寫該socket就會出錯,用的是send.錯誤號:ECONNRESET.讀(recv)socket並沒有返回錯誤。

5、該錯誤被描述為“connection reset by peer”,即“對方復位連線”,這種情況一般發生在服務程序較客戶程序提前終止。當服務程序終止時會向客戶 TCP 傳送 FIN 分節,客戶 TCP 迴應 ACK,服務 TCP 將轉入 FIN_WAIT2 狀態。此時如果客戶程序沒有處理該 FIN (如阻塞在其它呼叫上而沒有關閉 Socket 時),則客戶 TCP 將處於 CLOSE_WAIT 狀態。當客戶程序再次向 FIN_WAIT2 狀態的服務 TCP 傳送資料時,則服務 TCP 將立刻響應 RST。一般來說,這種情況還可以會引發另外的應用程式異常,客戶程序在傳送完資料後,往往會等待從網路IO接收資料,很典型的如 read 或 readline 呼叫,此時由於執行時序的原因,如果該呼叫發生在 RST 分節收到前執行的話,那麼結果是客戶程序會得到一個非預期的 EOF 錯誤。此時一般會輸出“server terminated prematurely”-“伺服器過早終止”錯誤。

EINVAL:

無效引數。提供的引數非法。有時也會與socket的當前狀態相關,如一個socket並沒有進入listening狀態,此時呼叫accept,就會產生EINVAL錯誤。

EMFILE:

打開了太多的socket。對程序或者執行緒而言,每種實現方法都有一個最大的可用socket數目處理,或者是全域性的,或者是區域性的。

EWOULDBLOCK:EAGAIN

資源暫時不可用。這個錯誤是從對非阻塞socket進行的不能立即結束的操作返回的,如當沒有資料在佇列中可以讀時,呼叫recv。並不是fatal錯誤,稍後操作可以被重複。呼叫在一個非阻塞的SOCK_STREAM socket 上呼叫connect時會產生這個錯誤,因為有時連線建立必須消耗一定的時間。

ENOTCONN

在一個沒有建立連線的socket上,進行read,write操作會返回這個錯誤。出錯的原因是socket沒有標識地址。Setsoc也可能會出錯。

ECONNRESET

 Connection reset by peer.

連線被遠端主機關閉。有以下幾種原因:遠端主機停止服務,重新啟動;當在執行某些操作時遇到失敗,因為設定了“keep alive”選項,連線被關閉,一般與ENETRESET一起出現。

ECONNABORTED

1、軟體導致的連線取消。一個已經建立的連線被host方的軟體取消,原因可能是資料傳輸超時或者是協議錯誤。

2、該錯誤被描述為“software caused connection abort”,即“軟體引起的連線中止”。原因在於當服務和客戶程序在完成用於 TCP 連線的“三次握手”後,客戶 TCP 卻傳送了一個 RST (復位)分節,在服務程序看來,就在該連線已由 TCP 排隊,等著服務程序呼叫 accept 的時候 RST 卻到達了。POSIX 規定此時的 errno 值必須 ECONNABORTED。源自 Berkeley 的實現完全在核心中處理中止的連線,服務程序將永遠不知道該中止的發生。伺服器程序一般可以忽略該錯誤,直接再次呼叫accept。

當TCP協議接收到RST資料段,表示連接出現了某種錯誤,函式read將以錯誤返回,錯誤型別為ECONNERESET。並且以後所有在這個套接字上的讀操作均返回錯誤。錯誤返回時返回值小於0。

ENETUNREACH

網路不可達。Socket試圖操作一個不可達的網路。這意味著local的軟體知道沒有路由到達遠端的host。

ENETRESET

網路重置時丟失連線。

由於設定了"keep-alive"選項,探測到一個錯誤,連線被中斷。在一個已經失敗的連線上試圖使用setsockopt操作,也會返回這個錯誤。

EINPROGRESS:

操作正在進行中。一個阻塞的操作正在執行。

ENOTSOCK:

在非socket上執行socket操作。

EDESTADDRREQ:

需要提供目的地址。

在一個socket上的操作需要提供地址。如往一個ADDR_ANY 地址上進行sendto操作會返回這個錯誤。

EMSGSIZE:

訊息體太長。

傳送到socket上的一個數據包大小比內部的訊息緩衝區大,或者超過別的網路限制,或是用來接收資料包的緩衝區比資料包本身小。

EPROTOTYPE

協議型別錯誤。標識了協議的Socket函式在不支援的socket上進行操作。如ARPA Internet

UDP協議不能被標識為SOCK_STREAM socket型別。

ENOPROTOOPT

該錯誤不是一個 Socket 連線相關的錯誤。errno 給出該值可能由於,通過 getsockopt 系統呼叫來獲得一個套接字的當前選項狀態時,如果發現了系統不支援的選項引數就會引發該錯誤。

EPROTONOSUPPORT

不支援的協議。系統中沒有安裝標識的協議,或者是沒有實現。如函式需要SOCK_DGRAM socket,但是標識了stream protocol.。

ESOCKTNOSUPPORT

Socket型別不支援。指定的socket型別在其address family中不支援。如可選選中選項SOCK_RAW,但實現並不支援SOCK_RAW sockets。

EOPNOTSUPP

 Operation not supported.

The attempted operation is not supported for the type of object referenced. Usually this occurs when a socket descriptor to a socket that cannot support this operation, for example, trying to accept a connection on a datagram socket.

EPFNOSUPPORT

 Protocol family not supported.

The protocol family has not been configured into the system or no implementation for it exists. Has a slightly different meaning to EAFNOSUPPORT, but is interchangeable in most cases, and all Windows Sockets functions that return one of these specify EAFNOSUPPORT.

EAFNOSUPPORT

 Address family not supported by protocol family.

An address incompatible with the requested protocol was used. All sockets are created with an associated "address family" (i.e. AF_INET for Internet Protocols) and a generic protocol type (i.e. SOCK_STREAM). This error will be returned if an incorrect protocol is explicitly requested in the socket call, or if an address of the wrong family is used for a socket, e.g. in sendto.

EADDRINUSE

 Address already in use.

Only one usage of each socket address (protocol/IP address/port) is normally permitted. This error occurs if an application attempts to bind a socket to an IP address/port that has already been used for an existing socket, or a socket that wasn't closed properly, or one that is still in the process of closing. For server applications that need to bind multiple sockets to the same port number, consider using setsockopt(SO_REUSEADDR). Client applications usually need not call bind at all - connect will choose an unused port automatically. When bind is called with a wild-card address (involving ADDR_ANY), a EADDRINUSE error could be delayed until the specific address is "committed." This could happen with a call to other function later, including connect, listen, Connect or JoinLeaf.

EADDRNOTAVAIL

 Cannot assign requested address.

The requested address is not valid in its context. Normally results from an attempt to bind to an address that is not valid for the local machine. This can also result from connect, sendto, Connect, JoinLeaf, or SendTo when the remote address or port is not valid for a remote machine (e.g. address or port 0).

ENETDOWN

 Network is down.

A socket operation encountered a dead network. This could indicate a serious failure of the network system (i.e. the protocol stack that the WinSock DLL runs over), the network interface, or the local network itself.

ENOBUFS

 No buffer space available.

An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full.

EISCONN

 Socket is already connected.

A connect request was made on an already connected socket. Some implementations also return this error if sendto is called on a connected SOCK_DGRAM socket (For SOCK_STREAM sockets, the to parameter in sendto is ignored), although other implementations treat this as a legal occurrence.

連線過程可能出現的錯誤情況有:

(1) 如果客戶機TCP協議沒有接收到對它的SYN資料段的確認,函式以錯誤返回,錯誤型別為ETIMEOUT。通常TCP協議在傳送SYN資料段失敗之後,會多次傳送SYN資料段,在所有的傳送都高中失敗之後,函式以錯誤返回。

注:SYN(synchronize)位:請求連線。TCP用這種資料段向對方TCP協議請求建立連線。在這個資料段中,TCP協議將它選擇的初始序列號通知對方,並且與對方協議協商最大資料段大小。SYN資料段的序列號為初始序列號,這個SYN資料段能夠被確認。當協議接收到對這個資料段的確認之後,建立TCP連線。

(2) 如果遠端TCP協議返回一個RST資料段,函式立即以錯誤返回,錯誤型別為ECONNREFUSED。當遠端機器在SYN資料段指定的目的埠號處沒有服務程序在等待連線時,遠端機器的TCP協議將傳送一個RST資料段,向客戶機報告這個錯誤。客戶機的TCP協議在接收到RST資料段後不再繼續傳送SYN資料段,函式立即以錯誤返回。

注:RST(reset)位:表示請求重置連線。當TCP協議接收到一個不能處理的資料段時,向對方TCP協議傳送這種資料段,表示這個資料段所標識的連接出現了某種錯誤,請求TCP協議將這個連線清除。有3種情況可能導致TCP協議傳送RST資料段:(1)SYN資料段指定的目的埠處沒有接收程序在等待;(2)TCP協議想放棄一個已經存在的連線;(3)TCP接收到一個數據段,但是這個資料段所標識的連線不存在。接收到RST資料段的TCP協議立即將這條連線非正常地斷開,並嚮應用程式報告錯誤。

(3) 如果客戶機的SYN資料段導致某個路由器產生“目的地不可到達”型別的ICMP訊息,函式以錯誤返回,錯誤型別為EHOSTUNREACH或ENETUNREACH。通常TCP協議在接收到這個ICMP訊息之後,記錄這個訊息,然後繼續幾次傳送SYN資料段,在所有的傳送都告失敗之後,TCP協議檢查這個ICMP訊息,函式以錯誤返回。

注:ICMP:Internet 訊息控制協議。Internet的執行主要是由Internet的路由器來控制,路由器完成IP資料包的傳送和接收,如果傳送資料包時發生錯誤,路由器使用 ICMP協議來報告這些錯誤。ICMP資料包是封裝在IP資料包的資料部分中進行傳輸的,其格式如下:

型別

校驗和

資料

0 8 16 24 31

型別:指出ICMP資料包的型別。

程式碼:提供ICMP資料包的進一步資訊。

校驗和:提供了對整個ICMP資料包內容的校驗和。

ICMP資料包主要有以下型別:

(1) 目的地不可到達:A、目的主機未執行;B、目的地址不存在;C、路由表中沒有目的地址對應的條目,因而路由器無法找到去往目的主機的路由。

(2) 超時:路由器將接收到的IP資料包的生存時間(TTL)域減1,如果這個域的值變為0,路由器丟棄這個IP資料包,並且傳送這種ICMP訊息。

(3) 引數出錯:當IP資料包中有無效域時傳送。

(4) 重定向:將一條新的路徑通知主機。

(5) ECHO請求、ECHO回答:這兩條訊息用語測試目的主機是否可以到達。請求者向目的主機發送ECHO請求ICMP資料包,目的主機在接收到這個ICMP資料包之後,返回ECHO回答ICMP資料包。

(6) 時戳請求、時戳回答:ICMP協議使用這兩種訊息從其他機器處獲得其時鐘的當前時間。

呼叫函式connect的過程中,當客戶機TCP協議傳送了SYN資料段的確認之後,TCP狀態由CLOSED狀態轉為SYN_SENT狀態,在接收到對 SYN資料段的確認之後,TCP狀態轉換成ESTABLISHED狀態,函式成功返回。如果呼叫函式connect失敗,應該用close關閉這個套接字描述符,不能再次使用這個套接字描述符來呼叫函式connect。

connect函式的出錯處理:

(1)ETIMEOUT-connection timed out 目的主機不存在,沒有返回任何相應,例如主機關閉

(2)ECONNREFUSED-connection refused(硬錯)到達目的主機後,由於各種原因建立不了連線,主機返回RST(復位)響應,例如主機監聽程序未啟用,tcp取消連線等

(3)EHOSTTUNREACH-no route to host(軟錯)路由上引發了一個目的地不可達的ICMP錯誤

其中(1)(3),客戶端會進行定時多次重試,一定次數後才返回錯誤。另外,當connect連線失敗時,sockfd套介面不可用,必須關閉後重新socket分配才行。

getsockopt 和 setsockopt 還可能引發以下錯誤:

getsockopt/setsockopt(2) man page 寫道

ERRORS

The getsockopt() and setsockopt() system calls will succeed unless:

[EBADF] The argument socket is not a valid file descriptor.

[EFAULT] The address pointed to by option_value is not in a valid part of the process dress space. For getsockopt(), this error may also be returned if option_len is not in a valid part of the process address space.

[EINVAL] The option is invalid at the level indicated.

[ENOBUFS]Insufficient memory buffers are available.

[ENOPROTOOPT] The option is unknown at the level indicated.

[ENOTSOCK] The argument socket is not a socket (e.g., a plain file).

The setsockopt() system call will succeed unless:

[EDOM] The argument option_value is out of bounds.

[EISCONN]socket is already connected and a specified option cannot be set while this is the case.