1. 程式人生 > >struct linger 用法

struct linger 用法

http://www.cnblogs.com/caosiyang/archive/2012/03/29/2422956.html

Linux下tcp連線斷開的時候呼叫close()函式,有優雅斷開和強制斷開兩種方式。

那麼如何設定斷開連線的方式呢?是通過設定socket描述符一個linger結構體屬性。

linger結構體資料結構如下: 

#include <arpa/inet.h>

struct linger {
  int l_onoff;
  int l_linger;
};

三種斷開方式:

1. l_onoff = 0; l_linger忽略

close()立刻返回,底層會將未傳送完的資料傳送完成後再釋放資源,即優雅退出。

2. l_onoff != 0; l_linger = 0;

close()立刻返回,但不會發送未傳送完成的資料,而是通過一個REST包強制的關閉socket描述符,即強制退出。

3. l_onoff != 0; l_linger > 0;

close()不會立刻返回,核心會延遲一段時間,這個時間就由l_linger的值來決定。如果超時時間到達之前,傳送完未傳送的資料(包括FIN包)並得到另一端的確認,close()會返回正確,socket描述符優雅性退出。否則,close()會直接返回錯誤值,未傳送資料丟失,socket描述符被強制性退出。需要注意的時,如果socket描述符被設定為非堵塞型,則close()會直接返回值。

具體用法:

struct linger ling = {0, 0};
setsockopt(socketfd, SOL_SOCKET, SO_LINGER, (void*)&ling, sizeof(ling));

http://blog.csdn.net/yunhua_lee/article/details/8146837

其取值和處理如下:
1、設定 l_onoff為0,則該選項關閉,l_linger的值被忽略,等於核心預設情況,close呼叫會立即返回給呼叫者,如果可能將會傳輸任何未傳送的資料;
2、設定 l_onoff為非0,l_linger為0,則套介面關閉時TCP夭折連線,TCP將丟棄保留在套介面傳送緩衝區中的任何資料併發送一個RST給對方,


   而不是通常的四分組終止序列,這避免了TIME_WAIT狀態;
3、設定 l_onoff 為非0,l_linger為非0,當套介面關閉時核心將拖延一段時間(由l_linger決定)。
   如果套介面緩衝區中仍殘留資料,程序將處於睡眠狀態,直 到(a)所有資料傳送完且被對方確認,之後進行正常的終止序列(描述字訪問計數為0)
   或(b)延遲時間到。此種情況下,應用程式檢查close的返回值是非常重要的,如果在資料傳送完並被確認前時間到,close將返回EWOULDBLOCK錯誤且套介面傳送緩衝區中的任何資料都丟失。
   close的成功返回僅告訴我們傳送的資料(和FIN)已由對方TCP確認,它並不能告訴我們對方應用程序是否已讀了資料。如果套介面設為非阻塞的,它將不等待close完成。

第一種情況其實和不設定沒有區別,第二種情況可以用於避免TIME_WAIT狀態,但在Linux上測試的時候,並未發現傳送了RST選項,而是正常進行了四步關閉流程,
初步推斷是“只有在丟棄資料的時候才傳送RST”,如果沒有丟棄資料,則走正常的關閉流程。
檢視Linux原始碼,確實有這麼一段註釋和原始碼:
=====linux-2.6.37 net/ipv4/tcp.c 1915=====
/* As outlined in RFC 2525, section 2.17, we send a RST here because
* data was lost. To witness the awful effects of the old behavior of
* always doing a FIN, run an older 2.1.x kernel or 2.0.x, start a bulk
* GET in an FTP client, suspend the process, wait for the client to
* advertise a zero window, then kill -9 the FTP client, wheee...
* Note: timeout is always zero in such a case.
*/
if (data_was_unread) {
/* Unread data was tossed, zap the connection. */
NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
tcp_set_state(sk, TCP_CLOSE);
tcp_send_active_reset(sk, sk->sk_allocation);

另外,從原理上來說,這個選項有一定的危險性,可能導致丟資料,使用的時候要小心一些,但我們在實測libmemcached的過程中,沒有發現此類現象,
應該是和libmemcached的通訊協議設定有關,也可能是我們的壓力不夠大,不會出現這種情況。


第三種情況其實就是第一種和第二種的折中處理,且當socket為非阻塞的場景下是沒有作用的。
對於應對短連線導致的大量TIME_WAIT連線問題,個人認為第二種處理是最優的選擇,libmemcached就是採用這種方式,
從實測情況來看,開啟這個選項後,TIME_WAIT連線數為0,且不受網路組網(例如是否虛擬機器等)的影響