1. 程式人生 > >linux網路程式設計之shutdown() 與 close()函式詳解

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);
      }