SO_LINGER選項用來設置延遲關閉的時間,等待套接字發送緩沖區中的數據發送完成。沒有設置該選項時,在調用close()后,在發送完FIN后會立即進行一些清理工作並返回。如果設置了SO_LINGER選項,並且等待時間為正值,則在清理之前會等待一段時間。
以調用close()主動關閉為例,在發送完FIN包后,會進入FIN_WAIT_1狀態。如果沒有延遲關閉(即設置SO_LINGER選項),在調用tcp_send_fin()發送FIN后會立即調用sock_orphan()將sock結構從進程上下文中分離。分離后,用戶層進程不會再接收到套接字的讀寫事件,也不知道套接字發送緩沖區中的數據是否被對端接收。如果設置了SO_LINGER選項,並且等待時間為大於0的值,會等待套接字的狀態從FIN_WAIT_1遷移到FIN_WAIT_2狀態。我們知道套接字進入FIN_WAIT_2狀態是在發送的FIN包被確認后,而FIN包肯定是在發送緩沖區中的最后一個字節,所以FIN包的確認就表明發送緩沖區中的數據已經全部被接收。當然,如果等待超過SO_LINGER選項設置的時間后,還是沒有收到FIN的確認,則繼續進行正常的清理工作,Linux下也沒有返回錯誤。從這里看來,SO_LINGER選項的作用是等待發送緩沖區中的數據發送完成,但是並不保證發送緩沖區中的數據一定被對端接收(對端宕機或線路問題),只是說會等待一段時間讓這個過程完成。如果在等待的這段時間里接收到了帶數據的包,還是會給對端發送RST包,並且會reset掉套接字,因為此時已經關閉了接收通道。
在使用這個選項來延遲關閉連接的時候有兩個地方需要注意:
1. 進程會睡眠,直到狀態不為FIN_WAIT_1、CLOSING、LAST_ACK(也就是接收到對FIN的ACK包),或者等待超時
2. 在等待的過程中如果接收到帶數據的包還是會發送RST包
3.消耗更多的額外資源
TCP協議是一個通用的傳輸層協議,不關心上層具體的業務,如果要延遲關閉連接,最好是結合自己的業務和場景自己來管理,不要依賴這個選項。nginx的延遲關閉就是自己來管理的,覺得要比直接使用SO_LINGER選項好一些,並且不會導致進程阻塞。 ngxin在發送錯誤信息后,會等待一段時間,讓用戶把所有的數據都發送完。超過等待時間后,會直接關閉連接。通過lingering_close,nginx可以保持更好的客戶端兼容性,避免客戶端被reset掉。
SO_LINGER還有一個作用就是用來減少TIME_WAIT套接字的數量。在設置SO_LINGER選項時,指定等待時間為0,此時調用主動關閉時不會發送FIN來結束連接,而是直接將連接設置為CLOSE狀態,清除套接字中的發送和接收緩沖區,直接對對端發送RST包。