Linux協(xié)議棧accept和syn隊列問題
161310 cient端收到synack后,根據(jù)ack值,使用SACK算法,只重傳最后一個ack內(nèi)容。
Server端收到數(shù)據(jù)包,由于accept隊列仍然是滿的,所以server端處理也只是標記acked,然后返回。
162884 client端等待幾秒后,沒有收到對應(yīng)的ack,認為之前的數(shù)據(jù)包也丟失,所以重傳之前的內(nèi)容數(shù)據(jù)包。
Server端收到數(shù)據(jù)包,由于accept隊列仍然是滿的,所以server端處理也只是標記acked,然后返回。
164828 client端等待一段時間后,認為連接不可用,于是發(fā)送FIN、ACK給server端。Client端的狀態(tài)變?yōu)镕IN_WAIT1,等待一段時間后,client端將看不到該鏈接。
164829 server端收到ACK后,此時cgi程序處理完一個請求,從accept隊列中取走一個連接,此時accept隊列中有了空閑,server端將請求的連接放到accept隊列中。
這樣cgi所在的服務(wù)器上顯示該鏈接是established的,但是nginx(client端)所在的服務(wù)器上已經(jīng)沒有該鏈接了。
之后,當cgi程序從accept隊列中取到該連接后,調(diào)用read去讀取sock中的內(nèi)容,但是由于client端早就退出了,所以read就會block那里了。
問題解決
或許你會認為在164829中,server端不應(yīng)該建立連接,這是內(nèi)核的bug。但是內(nèi)核是按照RFC來實現(xiàn)的,在3次握手的過程中,是不會判斷FIN標志位的,只會處理SYN、ACK、RST這三種標志位。
從應(yīng)用層的角度來考慮解決問題的方法,那就是使用非阻塞的方式read,或者使用select超時方式read;亦或者nginx中關(guān)閉連接的時候使用RST方式,而不是FIN方式。
附錄1
when I use linux TCP socket, and find there is a bug in function sk_acceptq_is_full():
When a new SYN comes, TCP module first checks its validation. If valid,send SYN,ACK to the client and add the sock
to the syn hash table.
Next time if received the valid ACK for SYN,ACK from the client. server will accept this connection and increase the
sk->sk_ack_backlog -- which is done in function tcp_check_req().
We check wether acceptq is full in function tcp_v4_syn_recv_sock().
Consider an example:
After listen(sockfd, 1) system call, sk->sk_max_ack_backlog is set to
As we know, sk->sk_ack_backlog is initialized to 0. Assuming accept() system call is not invoked now
1. 1st connection comes. invoke sk_acceptq_is_full(). sk->sk_ack_backlog=0 sk->sk_max_ack_backlog=1, function return 0 accept this connection. Increase the sk->sk_ack_backlog
2. 2nd connection comes. invoke sk_acceptq_is_full(). sk->sk_ack_backlog=1 sk->sk_max_ack_backlog=1, function return 0 accept this connection. Increase the sk->sk_ack_backlog
3. 3rd connection comes. invoke sk_acceptq_is_full(). sk->sk_ack_backlog=2 sk->sk_max_ack_backlog=1, function return 1. Refuse this connection.I think it has bugs. after listen system call. sk->sk_max_ack_backlog=1
but now it can accept 2 connections.
評論