最近在追查一個代理服務器請求后端業務邏輯服務時,出現地址不可達的bug,反映到tcp這邊的提示是 connection reset by peer。后來通過查看代理服務器這邊的代碼和業務邏輯服務器那邊的代碼后,發現是由於業務邏輯server那邊在對一個端口設置監聽的時候,對打開的socket設置了TCP_DEFER_ACCEPT這個選項,同時業務邏輯server這端對到來的tcp連接會在一個時間段后關閉這個連接。正常情況下,代理server這邊有重連機制,當發現業務邏輯server那邊關閉連接后,他會立即啟動重連機制;但由於業務邏輯server這邊對打開的socket設置了TCP_DEFER_ACCEPT選項,那么對於一個connect上來連接而言,它不會真正去完成tcp的三次握手進入establish狀態,而是會對最后一個客戶端的ack,僅僅把這個socket標記為acked,然后丟棄它,而對於這個socket,仍將是處於syn_recv狀態;只有當客戶端真正有數據到達的時候,內核才會去accept這個連接並且接收數據,到此時server這段的這個socket才會是establish狀態。
在客戶端client上來,到實際發送數據這段過程中,server這邊的這個socket始終是syn_recv的,同時它會去重發syn/ack,當重傳超過TCP_DEFER_ACCEPT指定的數值后(測試發現並不是十分精准,這個選項指定的是一個秒數,而內核會換算為重傳的次數)如果還沒數據到達,那么就會丟掉這個請求,並關閉連接。對地我們目前這個代理服務器來說,如果此時在通過它去請求后端邏輯,就會得到: connection reset by peer的錯誤了。
man 7 tcp中對這個選項的秒數:
TCP_DEFER_ACCEPT
Allows a listener to be awakened only when data arrives on the socket. Takes an integer value (seconds), this can bound the maximum number of
attempts TCP will make to complete the connection. This option should not be used in code intended to be portable.
這個就是說如果設置了這個選項,將允許一個監聽者只在有數據到達這個套接字的時候才會被喚醒,它將一個整型值(指定秒數)綁定為最大嘗試次數,但是后邊那個“TCP will make to complete the connection”到底如何理解,我沒有很清楚。
看了一篇分析文章:http://www.pagefault.info/?p=346,那里邊說是:“defer的連接將會被加入到establish隊列”, 但是我通過測試后發現,到超過一定時間后,這個連接會被close掉,不知道這個make to complete the connection到底是怎么回事。