起因
伺服器端寫了乙個簡單的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 網絡卡 乙個...