linux網路程式設計之shutdown() 與 close()函式詳解
1.close()函式
#include<unistd.h>
int close(int sockfd); //返回成功為0,出錯為-1.
close 一個套接字的預設行為是把套接字標記為已關閉,然後立即返回到呼叫程序,該套接字描述符不能再由呼叫程序使用,也就是說它不能再作為read或write的第一個引數,然而TCP將嘗試傳送已排隊等待發送到對端的任何資料,傳送完畢後發生的是正常的TCP連線終止序列。
在多程序併發伺服器中,父子程序共享著套接字,套接字描述符引用計數記錄著共享著的程序個數,當父程序或某一子程序close掉套接字時,描述符引用計數會相應的減一,當引用計數仍大於零時,這個close呼叫就不會引發TCP的四路握手斷連過程。
2.shutdown()函式
#include<sys/socket.h>
int shutdown(int sockfd,int howto); //返回成功為0,出錯為-1.
該函式的行為依賴於howto的值
1.SHUT_RD:值為0,關閉連線的讀這一半。
2.SHUT_WR:值為1,關閉連線的寫這一半。
3.SHUT_RDWR:值為2,連線的讀和寫都關閉。
終止網路連線的通用方法是呼叫close函式。但使用shutdown能更好的控制斷連過程(使用第二個引數)。
3.兩函式的區別
close與shutdown的區別主要表現在:
close函式會關閉套接字ID,如果有其他的程序共享著這個套接字,那麼它仍然是開啟的,這個連線仍然可以用來讀和寫,並且有時候這是非常重要的 ,特別是對於多程序併發伺服器來說。
而shutdown會切斷程序共享的套接字的所有連線,不管這個套接字的引用計數是否為零,那些試圖讀得程序將會接收到EOF標識,那些試圖寫的程序將會檢測到SIGPIPE訊號,同時可利用shutdown的第二個引數選擇斷連的方式。
下面將展示一個客戶端例子片段來說明使用close和shutdown所帶來的不同結果:
客戶端有兩個程序,父程序和子程序,子程序是在父程序和伺服器建連之後fork出來的,子程序傳送標準輸入終端鍵盤輸入資料到伺服器端,知道接收到EOF標識,父程序則接受來自伺服器端的響應資料。
/* First Sample client fragment, * 多餘的程式碼及變數的宣告已略 */ s=connect(...); if( fork() ){ /* The child, it copies its stdin to the socket */ while( gets(buffer) >0) write(s,buf,strlen(buffer)); close(s); exit(0); } else { /* The parent, it receives answers */ while( (n=read(s,buffer,sizeof(buffer)){ do_something(n,buffer); /* Connection break from the server is assumed */ /* ATTENTION: deadlock here */ wait(0); /* Wait for the child to exit */ exit(0); }
對於這段程式碼,我們所期望的是子程序獲取完標準終端的資料,寫入套接字後close套接字,並退出,伺服器端接收完資料檢測到EOF(表示資料已傳送完),也關閉連線,並退出。接著父程序讀取完伺服器端響應的資料,並退出。然而,事實會是這樣子的嘛,其實不然!子程序close套接字後,套接字對於父程序來說仍然是可讀和可寫的,儘管父程序永遠都不會寫入資料。因此,此socket的斷連過程沒有發生,因此,伺服器端就不會檢測到EOF標識,會一直等待從客戶端來的資料。而此時父程序也不會檢測到伺服器端發來的EOF標識。這樣伺服器端和客戶端陷入了死鎖(deadlock)。如果用shutdown代替close,則會避免死鎖的發生。
if( fork() ) { /* The child */
while( gets(buffer)
write(s,buffer,strlen(buffer));
shutdown(s,1); /* Break the connection
*for writing, The server will detect EOF now. Note: reading from
*the socket is still allowed. The server may send some more data
*after receiving EOF, why not? */
exit(0);
}