關於SO_REUSEADDR的使用說明~


參考WINDOWS   網絡編程技術   
    
  1.   可以對一個端口進行多次綁定,一般這個是不支持使用的;   
  2.   對於監聽套接字,比較特殊。如果你定義了SO_REUSEADDR,並且讓兩個套接字在同一個端口上進行接聽,那么對於由誰來ACCEPT,就會出現歧義。如果你定義個SO_REUSEADDR,只定義一個套接字在一個端口上進行監聽,如果服務器出現意外而導致沒有將這個端口釋放,那么服務器重新啟動后,你還可以用這個端口,因為你已經規定可以重用了,如果你沒定義的話,你就會得到提示,ADDR已在使用中。 

  我用在多播的時候,也經常使用SO_REUSEADDR,也是為了防止機器出現意外,導致端口沒有釋放,而使重啟后的綁定失敗~

 我的總結:防止服務器在發生意外時,端口未被釋放~可以重新使用~

 一個兄弟提出的問題:

 SOCKET   BindSocket1,BindSocket2.;   
    
              struct   sockaddr_in     MyAddress;   
              MyAddress.sin_family   =AF_INET;   
              MyAddress.sin_addr.sin_addr=strMyAddress;   
              MyAddress.sin_port   =htonl(MyPort);   
   

              setsockopt(....,SO_REUSEADDR,.....)
              Bind(BindSocket1,(strunct   socketaddr*)MyAddress,sizeof(MyAddress))   
              Bind(BindSocket2,(strunct   socketaddr*)MyAddress,sizeof(MyAddress))   
    
              Listen(BindSocket1,5);   
              Listen(BindSocket2,5);   
              ....   
              這樣可以么,有問題或是要注意的地方?    

             問綁定第二次是否可以成功?

            答復:

            網友vmstat多次提出了這個問題:SO_REUSEADDR有什么用處和怎么使用。而
且很多網友在編寫網絡程序時也會遇到這個問題。所以特意寫了這么一篇文章,
希望能夠解答一些人的疑難。 
    其實這個問題在Richard Stevens的《Unix網絡編程指南》卷一里有很詳細的
解答(中文版P166-168頁)。這里我只是寫幾個基本的例子來驗證這個問題。 
    首先聲明一個問題:當兩個socket的address和port相沖突,而你又想重用地
址和端口,則舊的socket和新的socket都要已經被設置了SO_REUSEADDR特性,只
有兩者之一有這個特性還是有問題的。 
    SO_REUSEADDR可以用在以下四種情況下。 
    (摘自《Unix網絡編程》卷一,即UNPv1) 
    1、當有一個有相同本地地址和端口的socket1處於TIME_WAIT狀態時,而你啟
動的程序的socket2要占用該地址和端口,你的程序就要用到該選項。 
    2、SO_REUSEADDR允許同一port上啟動同一服務器的多個實例(多個進程)。但
每個實例綁定的IP地址是不能相同的。在有多塊網卡或用IP Alias技術的機器可
以測試這種情況。 
    3、SO_REUSEADDR允許單個進程綁定相同的端口到多個socket上,但每個soc
ket綁定的ip地址不同。這和2很相似,區別請看UNPv1。 
    4、SO_REUSEADDR允許完全相同的地址和端口的重復綁定。但這只用於UDP的
多播,不用於TCP。

例子1:測試上面第一種情況。

#include <netinet/in.h> 
#include <sys/socket.h> 
#include <time.h> 
#include <stdio.h> 
#include <string.h> 

#define MAXLINE 100 

int main(int argc, char** argv) 

   int listenfd,connfd; 
   struct sockaddr_in servaddr; 
   char buff[MAXLINE+1]; 
   time_t ticks; 
   unsigned short port; 
   int flag=1,len=sizeof(int); 

   port=10013; 
   if( (listenfd=socket(AF_INET,SOCK_STREAM,0)) == -1) 
   
     perror("socket"); 
     exit(1); 
   } 
   bzero(&servaddr,sizeof(servaddr)); 
   servaddr.sin_family=AF_INET; 
   servaddr.sin_addr.s_addr=htonl(INADDR_ANY); 
   servaddr.sin_port=htons(port); 
   if( setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &flag, len) == -
1) 
   
      perror("setsockopt"); 
      exit(1); 
   } 
   if( bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == 
-1) 
   
      perror("bind"); 
      exit(1); 
   } 
   else 
      printf("bind call OK!/n"); 
   if( listen(listenfd,5) == -1) 
   
      perror("listen"); 
      exit(1); 
   } 
   for(;;) 
   
      if( (connfd=accept(listenfd,(struct sockaddr*)NULL,NULL)) == -1)

      
          perror("accept"); 
          exit(1); 
      } 
      if( fork() == 0)/*child process*/ 
      
        close(listenfd);/*這句不能少,原因請大家想想就知道了。*/ 
        ticks=time(NULL); 
        snprintf(buff,100,"%.24s/r/n",ctime(&ticks)); 
        write(connfd,buff,strlen(buff)); 
        close(connfd); 
        sleep(1); 
        execlp("f1-9d",NULL); 
        perror("execlp"); 
        exit(1); 
     } 
     close(connfd); 
     exit(0);/* end parent*/ 
  } 

測試:編譯為f1-9d程序,放到一個自己PATH環境變量里的某個路徑里,例如$HO
ME/bin,運行f1-9d,然后telnet localhost 10013看結果。 

2、第二種情況我沒有環境測,所以就不給測試程序了,大家有條件的可以自己寫
一個來測試一下。 
3、測試第三種情況的程序

#include <netinet/in.h> 
#include <sys/socket.h> 
#include <time.h> 
#include <stdio.h> 
#include <string.h> 

#define MAXLINE 100 

int main(int argc, char** argv) 

   int fd1,fd2; 
   struct sockaddr_in servaddr1,servaddr2; 
   char buff[MAXLINE+1]; 
   time_t ticks; 
   unsigned short port; 
   int flag=1,len=sizeof(int); 

   port=10013; 
   if( (fd1=socket(AF_INET,SOCK_STREAM,0)) == -1) 
   
       perror("socket"); 
       exit(1); 
   } 
   if( (fd2=socket(AF_INET,SOCK_STREAM,0)) == -1) 
   
       perror("socket"); 
       exit(1); 
   } 
   bzero(&servaddr1,sizeof(servaddr1)); 
   bzero(&servaddr2,sizeof(servaddr2)); 
   servaddr1.sin_family=AF_INET; 
   servaddr2.sin_family=AF_INET; 

   if( inet_pton(AF_INET, "127.0.0.1", &servaddr1.sin_addr) <= 0) 

printf("inet_pton() call error:127.0.0.1/n"); 
exit(1); 

if( inet_pton(AF_INET, "128.160.1.230", &servaddr2.sin_addr) <= 0) 


printf("inet_pton() call error:128.160.1.230/n"); 
exit(1); 

servaddr1.sin_port=htons(port); 
servaddr2.sin_port=htons(port); 
if( setsockopt(fd1, SOL_SOCKET, SO_REUSEADDR, &flag, len) == -1) 

perror("setsockopt"); 
exit(1); 

if( setsockopt(fd2, SOL_SOCKET, SO_REUSEADDR, &flag, len) == -1) 

perror("setsockopt"); 
exit(1); 

if( bind(fd1,(struct sockaddr*)&servaddr1,sizeof(servaddr1)) == -1)


perror("bind fd1"); 
exit(1); 

if( bind(fd2,(struct sockaddr*)&servaddr2,sizeof(servaddr2)) == -1)


perror("bind fd2"); 
exit(1); 

printf("bind fd1 and fd2 OK!/n"); 
/*put other process here*/ 
getchar(); 
exit(0);/* end */ 

4、由於第四種情況只用於UDP的多播,和TCP的使用沒多大關系,所以就不寫測試
例子了。自己有興趣的可以寫。 

以上的程序都是在Linux下編譯通過的。也可以在其他unix平台運行。 

參考資料: 
1、《Unix網絡編程》卷一 R. Stevens 
2、《Socket programming FAQ》 http://www.ibrado.com/sock-faq/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM