作者:willorfang。 時間:二零一一年九月六日 說明:錯誤處理是網(wǎng)絡編程中比較棘手的問題。特別是企業(yè)級網(wǎng)絡程序,更是要考慮到各種意外的情況。本文總結(jié)了linux socket編程的異常原因及處理。有任何問題,歡迎交流、指正。 出處:http://blog.csdn.net/fangwei1235。 ----------------------------------------------------------------------- 序 對于UNIX系統(tǒng),大部分系統(tǒng)調(diào)用在非正常返回時,其返回值為-1,并設置全局變量errno。如socket()、bind()、accept()、listen()函數(shù)等。 變量errno存放一個正整數(shù)來表明上一個系統(tǒng)調(diào)用的錯誤值。僅當系統(tǒng)調(diào)用發(fā)生錯誤時才設置它。如果系統(tǒng)調(diào)用正常返回,它的值是不確定的。因此,當一個系統(tǒng)調(diào)用發(fā)生錯誤時應立即檢查errno的值,以避免下一個調(diào)用修改了errno的值。 對于線程而言,每個線程都有專用的errno變量,它不是一個共享的變量,因此不必考慮多線程同步問題。錯誤值定義在頭文件中,為常量。通常用函數(shù)perror()來顯示錯誤信息。 socket函數(shù)的出錯處理:(linux man) ERRORS EACCES Permission to create a socket of the specified type and/or protocol is denied. EAFNOSUPPORT The implementation does not support the specified address family. EINVAL Unknown protocol, or protocol family not available. EMFILE Process file table overflow. 打開文件數(shù)達到最大值。(每進程) 注: 1. 查看進程打開文件在/proc下,對應每個進程有一個以進程號命名的目錄,該目錄下有一個fd目錄,該目錄下面的每個文件是一個符號連接,其文件名對應該進程占用的一個文件描述符,而連接指向的內(nèi)容表示文件描述符對應的實際文件。 2. 修改進程打開文件數(shù)上限Linux 默認的進程打開文件上限是1024個,可以通過ulimit -n查看。很多系統(tǒng)上限可以通過修改/etc/security/limits.conf文件改變,這個文件有詳細的注釋,對如何修改做了說明。如果希望 把所有用戶的進程打開文件上限改為65536,可以加入下面兩行* soft nofile 65535* hard nofile 65535還可以只真對某個用戶或某個組做修改,具體方法參見文件注釋。修改后需要重新啟動系統(tǒng)才能生效。 ENFILE The system limit on the total number of open files has been reached. () ENOBUFS or ENOMEM Insufficient memory is available. The socket cannot be created until sufficient resources are freed. EPROTONOSUPPORT The protocol type or the specified protocol is not supported within this domain. Other errors may be generated by the underlying protocol modules. 方法:修正參數(shù),重新調(diào)用socket函數(shù)。 connect函數(shù)的出錯處理: –EADDRNOTAVAIL: 遠程地址無效。通常處理方法是選擇新的地址,并重新連接或提示用戶,關閉套接字并終止程序。 – ECONNREFUSED: 連接被拒絕。當客戶調(diào)用connect()函數(shù),初始一個TCP連接請求,而此時服務器端沒有進程等待在相應的端口上(例如服務器未啟動),遠程系統(tǒng)發(fā)回 一個連接復位信號(RST),這時connect()函數(shù)將返回ECONNREFUSED錯誤碼。通常的處理方法是等待一段時間后,仍無法連接則退出。 例如主機監(jiān)聽進程未啟用,tcp取消連接等。 – EINTR: 由于信號的傳遞而引起系統(tǒng)中斷該調(diào)用。通常的處理方法是重新執(zhí)行該函數(shù)調(diào)用。 – ENETUNREACH: 網(wǎng)絡無法抵達。由于路由器故障,導致網(wǎng)絡無法返回ICMP的響應包,此時系統(tǒng)將進行多次嘗試直到超時。通常處理方法是提示用戶,并退出運行。 –ENXIO: 服務器在建立連接成功之前退出。通常處理方法是提示用戶,并退出運行。 – ETIMEDOUT: 連接超時。當客戶發(fā)出連接請求信號(SYN)后,服務器在指定時間段內(nèi)(75秒)沒有響應(例如主機關閉),則產(chǎn)生連接超時錯誤。通常處理方法是提示用戶,并退出運行。 注: ETIMEOUT,“connect time out”,即“連接超時”,這種情況一般發(fā)生在服務器主機崩潰。此時客戶 TCP 將在一定時間內(nèi)(依具體實現(xiàn))持續(xù)重發(fā)數(shù)據(jù)分節(jié),試圖從服務 TCP 獲得一個 ACK 分節(jié)。當最終放棄嘗試后(此時服務器未重新啟動),內(nèi)核將會向客戶進程返回 ETIMEDOUT 錯誤。 如果某個中間路由器判定該服務器主機已經(jīng)不可達,則一般會響應“destination unreachable”-“目的地不可達”的ICMP消息,相應的客戶進程返回的錯誤是 EHOSTUNREACH 或ENETUNREACH。 當服務器重新啟動后,由于 TCP 狀態(tài)丟失,之前所有的連接信息也不存在了,此時對于客戶端發(fā)來請求將回應 RST。 如果客戶進程對檢測服務器主機是否崩潰很有必要,要求即使客戶進程不主動發(fā)送數(shù)據(jù)也能檢測出來,那么需要使用其它技術,如配置 SO_KEEPALIVE Socket 選項,或?qū)崿F(xiàn)某些心跳函數(shù)。 ENOMEM: 沒有足夠的用戶內(nèi)存。通常處理方法是提示用戶,并退出運行。 bind函數(shù)的出錯處理(linux man): ERRORS: EADDRINUSE The given address is already in use. 有時候出現(xiàn)close socket以后,重新bind,出現(xiàn)此錯誤,在調(diào)用setsockopt設置相應項即可。 EBADF sockfd is not a valid descriptor. EINVAL The socket is already bound to an address. ENOTSOCK sockfd is a descriptor for a file, not a socket. listen()函數(shù)錯誤處理(linux man): ERRORS EADDRINUSE Another socket is already listening on the same port. EBADF The argument sockfd is not a valid descriptor. ENOTSOCK The argument sockfd is not a socket. EOPNOTSUPP The socket is not of a type that supports the listen() operation. accept()函數(shù)錯誤處理 ERRORS EAGAIN or EWOULDBLOCK The socket is marked non-blocking and no connections are present to be accepted. 對非阻塞socket accept可能會出現(xiàn)此錯誤,繼續(xù)accept即可。 EBADF The descriptor is invalid. ECONNABORTED A connection has been aborted. EINTR The system call was interrupted by a signal that was caught before a valid connection arrived. 重試即可。 EINVAL Socket is not listening for connections, or addrlen is invalid (e.g., is negative). EMFILE The per-process limit of open file descriptors has been reached. ENFILE The system limit on the total number of open files has been reached. ENOTSOCK The descriptor references a file, not a socket. EOPNOTSUPP The referenced socket is not of type SOCK_STREAM. recv()函數(shù)錯誤處理 注:如果返回0,表示TCP連接已被關閉。 ERRORS These are some standard errors generated by the socket layer. Additional errors may be gener- ated and returned from the underlying protocol modules; see their manual pages. EAGAIN The socket is marked non-blocking and the receive operation would block, or a receive timeout had been set and the timeout expired before data was received. 繼續(xù)接收數(shù)據(jù)即可。 EBADF The argument s is an invalid descriptor. ECONNREFUSED A remote host refused to allow the network connection (typically because it is not run- ning the requested service). EFAULT The receive buffer pointer(s) point outside the process‘s address space. EINTR The receive was interrupted by delivery of a signal before any data were available. EINVAL Invalid argument passed. ENOMEM Could not allocate memory for recvmsg(). ENOTCONN The socket is associated with a connection-oriented protocol and has not been connected (see connect(2) and accept(2)). ENOTSOCK The argument s does not refer to a socket. send()函數(shù)錯誤處理ERRORS EAGAIN or EWOULDBLOCK The socket is marked non-blocking and the requested operation would block. EBADF An invalid descriptor was specified. ECONNRESET Connection reset by peer. EDESTADDRREQ The socket is not connection-mode, and no peer address is set. EFAULT An invalid user space address was specified for a parameter. EINTR A signal occurred before any data was transmitted. EINVAL Invalid argument passed. EISCONN The connection-mode socket was connected already but a recipient was specified. (Now either this error is returned, or the recipient specification is ignored.) EMSGSIZE The socket type requires that message be sent atomically, and the size of the message to be sent made this impossible. ENOBUFS The output queue for a network interface was full. This generally indicates that the interface has stopped sending, but may be caused by transient congestion. (Normally, this does not occur in Linux. Packets are just silently dropped when a device queue overflows.) ENOMEM No memory available. ENOTCONN The socket is not connected, and no target has been given. ENOTSOCK The argument s is not a socket. EOPNOTSUPP Some bit in the flags argument is inappropriate for the socket type. EPIPE The local end has been shut down on a connection oriented socket. In this case the pro- cess will also receive a SIGPIPE unless MSG_NOSIGNAL is set. 附錄: EPIPE錯誤的幾種典型情況: 1、Socket 關閉,但是socket號并沒有置-1。繼續(xù)在此socket上進行send和recv,就會返回這種錯誤。這個錯誤會引發(fā)SIGPIPE信號,系統(tǒng)會將 產(chǎn)生此EPIPE錯誤的進程殺死。所以,一般在網(wǎng)絡程序中,首先屏蔽此消息,以免發(fā)生不及時設置socket進程被殺死的情況。 2、write(..) on a socketthat has been closed at the other end will cause a SIGPIPE. // 寫一個對端socket已經(jīng)關閉的socket。 3、錯誤被描述為“broken pipe”,即“管道破裂”,這種情況一般發(fā)生在客戶進程不理會(或未及時處理)Socket 錯誤,繼續(xù)向服務 TCP 寫入更多數(shù)據(jù)時,內(nèi)核將向客戶進程發(fā)送 SIGPIPE 信號,該信號默認會使進程終止(此時該前臺進程未進行 core dump)。向一個 FIN_WAIT2 狀態(tài)的服務 TCP(已 ACK 響應 FIN 分節(jié))寫入數(shù)據(jù)不成問題,但是寫一個已接收了 RST 的 Socket 則是一個錯誤。 ECONNRESET錯誤的幾種典型情況: 1、在客戶端服務器程序中,客戶端異常退出,并沒有回收關閉相關的資源,服務器端會先收到ECONNRESET錯誤,然后收到EPIPE錯誤。 2、連接被遠程主機關閉。有以下幾種原因:遠程主機停止服務,重新啟動;當在執(zhí)行某些操作時遇到失敗,因為設置了“keep alive”選項,連接被關閉,一般與ENETRESET一起出現(xiàn)。 3、遠程端執(zhí)行了一個“hard”或者“abortive”的關閉。應用程序應該關閉socket,因為它不再可用。當執(zhí)行在一個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”,即“對方復位連接”,這種情況一般發(fā)生在服務進程較客戶進程提前終止。當服務進程終止時會向客戶 TCP 發(fā)送 FIN 分節(jié),客戶 TCP 回應 ACK,服務 TCP 將轉(zhuǎn)入 FIN_WAIT2 狀態(tài)。此時如果客戶進程沒有處理該 FIN (如阻塞在其它調(diào)用上而沒有關閉 Socket 時),則客戶 TCP 將處于 CLOSE_WAIT 狀態(tài)。當客戶進程再次向 FIN_WAIT2 狀態(tài)的服務 TCP 發(fā)送數(shù)據(jù)時,則服務 TCP 將立刻響應 RST。一般來說,這種情況還可以會引發(fā)另外的應用程序異常,客戶進程在發(fā)送完數(shù)據(jù)后,往往會等待從網(wǎng)絡IO接收數(shù)據(jù),很典型的如 read 或 readline 調(diào)用,此時由于執(zhí)行時序的原因,如果該調(diào)用發(fā)生在 RST 分節(jié)收到前執(zhí)行的話,那么結(jié)果是客戶進程會得到一個非預期的 EOF 錯誤。此時一般會輸出“server terminatedprematurely”-“服務器過早終止”錯誤。 |
|
來自: 改泡 > 《網(wǎng)絡編程》