linux網路編程之socket(十) shutdown與close函數的區別

來源:互聯網
上載者:User

假設server和client 已經建立了串連,server調用了close, 發送FIN 段給client(其實不一定會發送FIN段,後面再說 ),此時server不能再通過socket發送和接收資料,此時client調用read,如果接收到FIN 段會返回0,但client此時還是 可以write 給server的,write調用只負責把資料交給TCP發送緩衝區就可以成功返回了,所以不會出錯,而server收到資料 後應答一個RST段,表示伺服器已經不能接收資料,串連重設,client收到RST段後無法立刻通知應用程式層,只把這個狀態儲存 在TCP協議層。如果client再次調用write發資料給server,由於TCP協議層已經處於RST狀態了,因此不會將資料發出,而是 發一個SIGPIPE訊號給應用程式層,SIGPIPE訊號的預設處理動作是終止程式。

有時候代碼中需要連續多次調用write,可 能還來不及調用read得知對方已關閉了串連就被SIGPIPE訊號終止掉了,這就需要在初始化時調用sigaction處理SIGPIPE信 號,對於這個訊號的處理我們通常忽略即可,signal(SIGPIPE, SIG_IGN); 如果SIGPIPE訊號沒有導致進程異常退出,write 返回-1並且errno為EPIPE。

#include <unistd.h>

int close(int fd);

close 關閉了自身資料傳 輸的兩個方向。

#include <sys/socket.h>

int shutdown(int sockfd, int how);

shutdown 可 以選擇關閉某個方向或者同時關閉兩個方向,shutdown how = 1 or how = 2 (SHUT_WR or SHUT_RDWR),可以保證對等方接 收到一個EOF字元(即發送了一個FIN段),而不管其他進程是否已經開啟了這個通訊端。而close不能保證,只有當某個 sockfd的引用計數為0,close 才會發送FIN段,否則只是將引用計數減1而已。也就是說只有當所有進程(可能fork多個子 進程都開啟了這個通訊端)都關閉了這個通訊端,close 才會發送FIN 段。

所以說,如果是調用shutdown how = 1 ,則意味著往一個已經接收FIN的通訊端中寫是允許的,接收到FIN段僅代表對方不再發送資料,但對方還是可以讀取資料的 ,可以讓對方可以繼續讀取緩衝區剩餘的資料。

下面使用shutdown 修改用戶端程式,在前面講過的使用select函數 修改後的用戶端程式基礎上,修改很小一部分:

if (FD_ISSET(fd_stdin, &rset)){        if (fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)    {        stdineof = 1; //表示已經輸入完畢        /* 關閉sock的寫端,還能夠接收資料,在sock的緩衝區末尾添加一個FIN段 */        shutdown(sock, SHUT_WR);    }    else    {        writen(sock, sendbuf, strlen(sendbuf));        memset(sendbuf, 0, sizeof(sendbuf));        }}

為了測試我們想要的效果,需要在select函數修改後的伺服器端程式 的 134 行代碼之後,即writen 之前 sleep(4); 目的是接收到用戶端資料後不馬上回射回去,睡眠4s 後在用戶端已經關閉串連的情況下再發送資料。

先運行伺服器端程式,再運行用戶端程式,在用戶端標準輸入,迅速敲入兩行:AAAAA\n  BBBBB\n 然後按下 ctrl+d 即fgets 會返回NULL,然後調用shutdown關閉寫端,雖然伺服器端延時才發送資料,此時用戶端寫端已經關閉,但 還是可以讀取到回射回來的資料,伺服器端最後得到一個FIN段,read 返回0,列印輸出 client close ,並且close (conn); 而用戶端在讀取服務端回射回來的兩次資料後,再次read 也返回0,故列印 server connect close,break退出循 環,進程順利退出。從下面的輸出還可以看出,因為延時的關係,所以不像以前那樣發射一行就回射一行。

simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./echoser_select

recv connect ip=127.0.0.1 port=54010

fdsgfgd

gfedg

client close .

..........................

simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./echocli_select_shutdown

local ip=127.0.0.1 port=54010

fdsgfgd

gfedg

fdsgfgd

gfedg

server connect close

 

如果我們將用戶端程式中的shutdown 改成了 close,那麼當延時後伺服器端發送資料給用戶端時 ,用戶端的讀端和寫端都已經關閉,第一次發AAAAA會返回一個RST段,根據本文前面所說,再次發BBBBB直接產生SIGPIPE信 號,預設會終止進程,但因為我們已經設定了忽略SIGPIPE訊號,所以伺服器端進程不會被終止,但用戶端也會出錯,因為 回到while迴圈開頭,select阻塞等待時發現通訊端的讀端已經關閉,所以不能再關心可讀事件了,select會返回-1,錯誤 碼是 EBADF: Bad File Descriptor。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.