epoll 連線以後 recv始終返回0

2021-10-03 10:24:33 字數 2066 閱讀 3040

起因

伺服器端寫了乙個簡單的epoll監聽,**如下(**是錯的啊!)

servfd = bind(0.0.0.0);

listen(servfd);

epollfd = create();

fcntl(server_sockfd, f_setfl, fcntl(server_sockfd, f_getfl, 0)|o_nonblock);

struct epoll_event ev;

ev.events = (epollin|epollet);

ev.data.fd = servfd;

epoll_ctl(epollfd, epoll_ctl_add, servfd, &servfd_ev);

while(1)

}然後我想試試它的穩定性啊,在客戶機不斷的run client -> ctrl+c -> run client -> …

然後突然有一次,客戶機連線上了,伺服器端直接報 recv(clientfd, ***) == 0

經查文件,返回0表示客戶端告知伺服器「關閉客戶端向伺服器的寫信道」(傳送fin),使這條tcp通道處於「半連線狀態」(server:),然後等待伺服器向客戶端發最後的一些資料並關閉「伺服器向客戶端的寫信道」(關閉tcp連線)。

但是!客戶端仍然在執行!tcp連線從伺服器端和客戶端看,都是established狀態。

然後目前伺服器端狀態如下:

連線前stat.jpg

(有很多close_wait)

排查過程

然後試試nc唄 nc 192.168.186.142,然後伺服器端居然提示recv()到一些訊息,然後再次recv()==0。

nc這條tcp連線依然沒有斷掉,在nc的發出連線的瞬間,在伺服器端netstat -antp發現有變化的一條:

tcp 49 0 192.168.186.142:12315 192.168.186.1:63358 close_wait 68104/./server

原來的狀態是:close_wait但是沒有所屬pid。

好奇怪啊,似乎是新的連線繼承了舊的連線,但是明明埠號不一樣啊?

抓包看看,發現了client和server端的兩個tcp通訊:

乙個是,

另乙個是, , 正常的三步握手

思前想後。。。。。。

(可能機智的看客早已發現一切,上圖第二條 recv-q == 11 有很多排隊等待連線的,便說明了問題。)

原因如下了

先說說socket連線和file descriptor的關係。

linux的思想,一切皆檔案。一條tcp連線,當被accept()以後,才有file descriptor(這個fd是屬於這個thread所管轄的,其編號也在thread內部排序)。

客戶端發起tcp連線,被伺服器端kernel應答以後,連線狀態直接變為established,無論是否accept()。

無論是close_wait狀態的連線,還是established的連線。

說白了accept就是分配file descriptor。

我們的epoll是邊緣觸發,即:當狀態有變化時候才觸發。

因此其實是accept佇列裡面積攢了之前的已經被關掉的連線,導致accept直接受到了乙個close_wait的socket連線。

或者把「accept用的socket」設為level trigger

可以換個角度理解問題

上述伺服器**執行著,然後同一時間 a、b、c三個客戶端練進去了。

然後a、b、c到達了伺服器kernel,三條鏈結在系統底層就被處理成了established狀態。

這三個連線進入accept()佇列,但我們的程式只accept了一次,獲得了a連線的fd。此時b、c兩個仍然在accept佇列裡,但是我們的程式沒理會。

然後我們把b、c兩個ctrl+c,在伺服器系統kernel看來:b、c兩個連線變成了close_wait。注意:連線變換狀態不會使epoll觸發。

然後此時,a正常結束,雙方close,這條鏈結從系統kernel去掉了。

然後我們嘗試從客戶機d發起連線,伺服器端listening socket邊緣觸發,但是接到了「b的close_wait狀態的連線」,才會出現上述結果。

socket程式設計中應用recv判斷連線已斷開

在網路程式設計中,經常會檢測網路的連線情況,進而進行下面的動作。在linux的socket程式設計中,有一種非常方便的方法,來判斷對方是否斷開了連線,就是使用recv函式。在apue 中,對 recv的表述如下,include ssize t recv int sockfd,void buf,siz...

關於epoll檢測非同步連線的方法

因為epoll本身沒有明確提出當非同步connect成功之後會返回什麼樣的訊號,通過測試有如下結果 1,當本地還沒呼叫connect函式,卻將套接字送交epoll檢測,epoll會產生一次 epollout epollhup,也就是產生乙個值為0x14的events.2,當本地connect事件發生...

18 11 27 高階的伺服器連線 epoll

恢復內容開始 之前的 http 伺服器 都是採用 輪詢的方式 就像 廚師挨個問誰餓了好做飯 一樣 而 epoll 用著高階的 方式 事件通知 直接問誰餓了 同時還和 計算機共享內純 預設閘道器 好像是接到路由器 上面的 mac位址 類似於網絡卡的預設 數值 在路由器上面 有兩個 mac 網絡卡 乙個...