socket编程:SO_REUSEADDR例解

news/2024/7/8 13:51:58
                                      kevintz 2000-6-19

    网友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平台运行。

http://www.niftyadmin.cn/n/4819809.html

相关文章

使用docker简易部署zabbix6.0笔记

1 前言 1.1 实验背景 因zabbix 6.0新增许多新特性&#xff0c;为熟悉界面特意在本地部署一套简易版&#xff08;未启用HA功能&#xff09;。原本想要在烧制了centos7.9系统树莓派上部署&#xff0c;一查之下armv7果然冷门&#xff0c;mariadb无论是直接安装或者docker都不支持…

字母转为小写字母

传入大写字母&#xff0c;输出为小字母&#xff0c;如果传入小写字母&#xff0c;直接输出小写字母。传入非字母&#xff0c;输出为‘’。 如&#xff1a;A-->a;B-->b;C-->c;...Z-->z参考函数&#xff1a; SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- -…

SVN使用blog收藏

SVN使用blog收藏 新一篇: SQLite介绍 | 旧一篇: LUA几个相关网站和编程IDE <script>function StorePage(){ddocument;td.selection?(d.selection.type!None?d.selection.createRange().text:):(d.getSelection?d.getSelection():);void(keyitwindow.open(http://www.3…

单节点kafka部署笔记

1 背景 因为工作中需要对接kafka&#xff0c;准备在测试环境中自己部署一套&#xff0c;考虑方便决定部署一台单点。 2 部署 2.1 scala 2.1.1 java环境 openjdk即可&#xff0c;我使用的是openjdk1.8 2.1.2 下载软件 下载scala-2.12.17.tgz并解压&#xff0c;例如解压到…

树莓派摄像头笔记

背景 淘宝几十块钱买了个摄像头&#xff0c;usb接口&#xff0c;免驱动&#xff0c;支持uvc协议&#xff0c;于是装在树莓派上准备当监控用。 查看 $ lsusb Bus 001 Device 007: ID 038f:6001 lihappe8 Corp. USB 2.0 Camera Bus 001 Device 006: ID 046d:c534 Logitech, In…

我打江南走过

我打江南走过 那等在季节里的容颜如莲花的开落 东风不来&#xff0c;三月的柳絮不飞 你底心如小小寂寞的城 恰若青石的街道向晚 跫音不响&#xff0c;三月的春帷不揭 你底心是小小的窗扉紧掩 我达达的马蹄是美丽的错误 我不是归人&#xff0c;是个过客…… 图&#xff1a;百涧…

Linux下文件的操作

前言&#xff1a; 我们在这一节将要讨论linux下文件操作的各个函数。 文件的创建和读写 文件的各个属性 目录文件的操作 管道文件 1.文件的创建和读写 我假设你已经知道了标准级的文件操作的各个函数(fopen&#xff0c;fread&#xff0c;fwrite等等)。当然如果你不清楚的…

#ifndef #define #endif的用法 整理:shichenghua

#ifndef#define#endif的用法 发表于 2007-1-20 13:20:55 #ifndef #define #endif的用法 整理&#xff1a;shichenghuahttp://www.eetop.cn/blog/?56085/action_viewspace_itemid_1145.html(前段时间要到这个&#xff0c;感觉shichenghua整理得不错&#xff0c;所以收藏到此处…