ECONNRESET和WSAECONNRESET怎么產生的以及如何避免


ECONNRESET是linux環境網絡編程產生的錯誤,錯誤碼為104,

WSAECONNRESET是windows環境網絡編程產生的錯誤,錯誤碼為10054

兩者產生的原因都一樣,分以下幾種情況:

1接收端recv或者read, 對端已經關閉連接,recv/read返回該錯誤

2 對端重啟連接,還未建立連接

3 發送端已經斷開連接,但是調用send會觸發這個錯誤

 

第二點第三點都可以通過判斷返回值解決,第一點在一些砍死正常情況下也會觸發該錯誤。

比如對端close(fd),接收端調用recv並沒有返回0,而是-1,打印錯誤碼為104或

10054,按道理講這種情況按照返回值為0處理是可以的,但是盡量將代碼寫的規范一些,

避免不必要的錯誤。

為什么close(fd)會導致接收端讀到復位RST,也就是收到錯誤的104呢,

因為close(fd)只是將文件描述符關閉,並沒有關閉tcp建立起來的連接,斷開連接需要四次握手,

倘若發送端發送緩沖區有數據未發送完或者接受緩沖區有數據未讀完,調用close(fd),那么連接並沒有關閉,這樣,接收端

收到的就是所謂的104或10054錯誤了。如何避免這個錯誤呢,就需要我們判斷發送端發送和接受操作

是否進行完,也就是判斷緩沖區是否有數據,如果有數據需要等待數據處理完畢在關閉,否則會出現上述錯誤。

我有一個做法是通過調用shutdown(s,SHUT_WR);關閉發送端的寫端,這樣發送端不發送數據,然后調用close

這次會發送關閉連接的FIN標志,接收端接收到FIN,那么recv或者read返回的就是0.

int shutdown(int sockfd,int how);
  Sockfd是需要關閉的socket的描述符。參數 how允許為shutdown操作選擇以下幾種方式:
    SHUT_RD:關閉連接的讀端。也就是該套接字不再接受數據,任何當前在套接字接受緩沖區的數據將被丟棄。

  進程將不能對該套接字發出任何讀操作。

  對 TCP套接字該調用之后接受到的任何數據將被確認然后無聲的丟棄掉。
    SHUT_WR:關閉連接的寫端,進程不能在對此套接字發出寫操作
    SHUT_RDWR:相當於調用shutdown兩次:首先是以SHUT_RD,然后以SHUT_WR

下面摘用網上的一段話來說明二者的區別:
close-----關閉本進程的socket id,但鏈接還是開着的,用這個socket id的其它進程還能用這個鏈接,能讀或寫這個socket id
shutdown--則破壞了socket 鏈接,讀的時候可能偵探到EOF結束符,寫的時候可能會收到一個SIGPIPE信號,這個信號可能直到
socket buffer被填充了才收到。

 close(sockfd);使用close中止一個連接,但它只是減少描述符的參考數,並不直接關閉連接,只有當描述符的參考數為0時才關閉連接。

而且shutdown只是處理連接關閉,並不能回收描述符,所以最終還是要調用close(fd)才能回收描述符,在所有描述符引用次數為0時發送

FIN消息給對端。

出了采取shutdown的方式,還可以通過設置socket屬性,調用close時,檢測在socket完成緩沖區讀寫后,才關閉連接。

 

struct linger {

     int l_onoff; /* 0 = off, nozero = on */

     int l_linger; /* linger time */

};

 

 

有下列三種情況:

1、設置 l_onoff為0,則該選項關閉,l_linger的值被忽略,等於內核缺省情況,close調用會立即返回給調用者,如果可能將會傳輸任何未發送的數據;

 

2、設置 l_onoff為非0,l_linger為0,則套接口關閉時TCP夭折連接,TCP將丟棄保留在套接口發送緩沖區中的任何數據並發送一個RST給對方,而不是通常的四分組終止序列,這避免了TIME_WAIT狀態;

 

3、設置 l_onoff 為非0,l_linger為非0,當套接口關閉時內核將拖延一段時間(由l_linger決定)。如果套接口緩沖區中仍殘留數據,進程將處於睡眠狀態,直 到(a)所有數據發送完且被對方確認,之后進行正常的終止序列(描述字訪問計數為0)或(b)延遲時間到。

 

下面是代碼:

 

 

int z; 
 int s;       
 struct linger so_linger;
 
 so_linger.l_onoff = 1
 so_linger.l_linger = 30;
 z = setsockopt(s,SOL_SOCKET,SO_LINGER,&so_linger,
     sizeof so_linger);
 if ( z )
     perror("setsockopt(2)");
     close(s); 

 

 到目前為止,我覺得比較好的主動關閉方式是:

關閉端:

1確保發送緩存區沒有數據未發送,調用shutdown(fd,SHUTWR);

2如果能接收到數據,繼續接受,直到接收到對方的FIN,也就是

read返回0或者-1

3如果接收到關閉信號,那么調用close正常關閉。

 

謝謝關注我的公眾號:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM