1. connect出錯:
(1) 若TCP客戶端沒有收到syn分節的響應,則返回ETIMEOUT錯誤;調用connect函數時,內核發送一個syn,若無響應則等待6s后再發送一個,若仍然無響應則等待24s后在發送一個,若總共等待75s后仍未收到響應則返回本錯誤;
(2) 若對客戶的syn響應是rst,則表明該服務器在我們指定的端口上沒有進程在等待與之連接,這是一種硬錯誤,客戶一收到rst馬上返回ECONNREFUSED錯誤;
(3) 若客戶發送的syn在中間的某個路由器上引發了目的不可達icmp錯誤,則認為是一種軟錯誤。客戶主機內核保存該消息,並按照第一種情況的時間間隔繼續發送syn,咋某個規定時間后仍未收到響應,則把保存的消息作為EHOSTUNREACH或者ENETUNREACH錯誤返回給進程;
2. accept返回前連接中止:
在比較忙的服務器中,在建立三次握手之后,調用accept之前,可能出現客戶端斷開連接的情況;如,三次握手之后,客戶端發送rst,然后服務器調用accept。posix指出這種情況errno設置為CONNABORTED;
注意Berkeley實現中,沒有返回這個錯誤,而是EPROTO,同時完成三次握手的連接會從已完成隊列中移除;在這種情況下,如果我們用select監聽到有新的連接完成,但之后又被從完成隊列中刪除,此時如果調用阻塞accept就會產生阻塞;
解決辦法:
(1) 使用select監聽套接字是否有完成連接的時候,總是把這個監聽套接字設置為非阻塞;
(2) 在后續的accept調用中忽略以下錯誤,EWOULDBLOCK(Berkeley實現,客戶中止連接), ECONNABORTED(posix實現,客戶中止連接), EPROTO(serv4實現,客戶中止連接)和EINTR(如果有信號被捕獲);
3. 服務器進程終止(崩潰):
在客戶端和服務器端建立連接之后,使用kill命令殺死服務器進程,進程終止會關閉所有打開的描述符,這導致了其向客戶端發送了一個FIN,而客戶端則響應了一個ack,這就完成了tcp連接終止的前半部分,只代表服務器不在發送數據了;但是客戶端並不知道服務器端已經終止了,當客戶端向服務器寫數據的時候,由於服務器進程終止,所以響應了rst,如果我們使用select等方式,能夠立即知道當前連接狀態;如下:
(1) 如果對端tcp發送數據,那么套接字可讀,並且read返回一個大於0的值(讀入字節數);
(2) 如果對端tcp發送了fin(對端進程終止),那么該套接字變為可讀,並且read返回0(EOF);
(3) 如果對端tcp發送rst(對端主機崩潰並重啟),那么該套接字變為可讀,並且read返回-1,errno中含有確切錯誤碼;
4. sigpipe信號:
當一個進程向某個收到rst的套接字執行寫操作的時候,內核向該進程發送一個SIGPIPE信號,該信號的默認行為是終止進程,因此進程必須捕獲它以免不情願的被終止;
不論進程是捕捉了該信號並從信號處理函數中返回,還是簡單忽略該信號,寫操作都講返回EPIPE錯誤;
5. 服務器主機崩潰:
建立連接之后,服務器主機崩潰,此時如果客戶端發送數據,會發現客戶端會在一定時間內持續重傳,視圖從服務器端收到數據的ack,當重傳時間超過指定時間后,服務器仍然沒有響應,那么返回的是ETIMEDOUT;
6. 服務器主機不可達:
建立連接之后,服務器主機未崩潰,但是由於中間路由器故障燈,判定主機或網絡不可達,此時如果客戶端發送數據,會發現客戶端會在一定時間內持續重傳,視圖從服務器端收到數據的ack,當重傳時間超過指定時間后,服務器仍然沒有響應,那么返回的是EHOSTUNREACH或ENETUNREACH;
7. 服務器主機崩潰后重啟:
當服務器主機崩潰重啟后,之前所有的tcp連接丟失,此時服務器若收到來自客戶端的數據,會響應一個rst;客戶端調用read將返回一個ECONNRESET錯誤;
8. 服務器主機關機:
系統關機時,init進程給所有進程發送SIGTERM信號,等待固定的時間,然后給所有仍在運行的進程發送SIGKILL信號,我們的進程會被SIGTERM或者SIGKILL信號終止,所以與前面服務器進程終止相同,進程關閉所有描述符,並發送fin,完成服務器端的半關閉;