1. 程式人生 > >TCP異常處理(accept返回前連線中止)與SO_LINGER選項

TCP異常處理(accept返回前連線中止)與SO_LINGER選項

一、accept返回前終止分析

問題一:

因為accept是堵塞的, 並且等待來自客戶端的連線, 但是, 如果在accept期間 , 如果因為系統呼叫中斷了accept就會返回一個非致命的錯誤, 而此時有來自客戶端進行TCP三路握手完成後, 而我們通過迴圈在此呼叫accept函式可以完成連線,。因為TCP的連線是在核心中完成的, 與accept函式的執行無關。

問題二:

如果我們在呼叫accept函式返回之前, 該客戶端TCP傳送了一個RST(復位)。在伺服器中, 表現為該連線仍在TCP佇列中, 等待伺服器程序呼叫accept的時候RST到達。此時返回的套接字是一個已連線,但是卻有接受了RST的套接字。

模型圖如下:

                                                            

*基於不同系統的處理方式如下:

1、源自Berkeley是完全有核心中斷連線, 不返回給我程序9

2、大多數的SVR4實現返回一個錯誤給伺服器程序, 作為accept的返回結果, 錯誤取決去實現的本身。SVR4中errno返回EPROTO, POSIX指出返回ECONNABORTED(軟體引起的連線中斷),

3、而非致命錯誤(系統中斷)的時候, 我們可以再次呼叫accept返回連線套接字。


實驗如下:

客戶端程式在建立呼叫connect函式後, 立刻呼叫設定SO_LINGER套接字選項產生一個RST傳送給對端。

而伺服器程式在socket bind listen後, 睡眠睡眠一段時間, 然後呼叫accept函式, 來模擬accept函式連線前中斷。


執行方案:在當伺服器在sleep,睡眠5s期間呼叫客戶端程式, 然後5s後伺服器處理完連線後進入一下迴圈, 繼續睡眠5s, 此時呼叫ctri +C退出程式

伺服器程式部分核心程式碼

for(;;){
    int confd;
    len = sizeof(cliaddr);
    sleep(5);
    fprintf(stdout, "accept...\n");
    confd = accept(sockfd, (struct sockaddr *) &cliaddr, &len);
    fprintf(stderr, "%s\n", strerror(errno));
    int n = read(confd, recvbuf, MAXLINE);
    fprintf(stderr, "%s\n", strerror(errno));
    close(confd);
}
客戶端程式部分核心程式碼

Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    lger.l_onoff = 1;
    lger.l_linger = 0;
    setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &lger, sizeof(lger));
    close(sockfd);

執行結果如下:


通過列印錯誤可以:程式accept照樣接收套接字, 沒有遵循Berkeley的處理原則, 而是SVR4 處理(根據作業系統不同, 核心處理方式不同)。



抓包結果如下:


二、SO_LINGER選項分析

SO_LINGER選項用以下資料結構來改變:

struct linger{
	int l_onoff;    // 0 = off,  nozero = on
	int l_linger;   // linger time
};

1、l_onoff = 0, 表示該選項關閉, l_linger值將會被忽略。close呼叫後, 將會立刻返回給呼叫則如下圖1所示

2、設定 l_onoff為非0,l_linger為0,則套介面關閉時TCP夭折連線,TCP將丟棄保留在套介面傳送緩衝區中的任何資料併發送一個RST給對方,而不是通常的四分組終止序列,這避免了TIME_WAIT狀態

3、l_onoff  = nozero ,l_linger = nozero的時候, 該套接字關閉時核心將拖延一段時間(由l_linger覺得)。呼叫close函式,當套接字緩衝區還有資料的話, 程序將會進入睡眠狀態, 核心將會繼續傳送資料直到所有的資料都被對方確認完全接受。但是如果, l_linger時間到達, 而資料還沒有完全傳送完畢, 那麼剩下的緩衝區的資料將會被摒棄,close將會返回EWOULDBLOCK錯誤。若資料傳送完畢, 那麼將會告訴我們資料和FIN都已經被對方確認。如果套接字在非堵塞的情況以上的假設不存在


三、總結

1、瞭解accept異常中斷和rst

2、熟悉SO_LINGER在堵塞情況的應用和在非堵塞情況下的區別

3、SO_LINGER選項中中close的工作原理