zoukankan      html  css  js  c++  java
  • 网络协议栈中的 SO_REUSEADDR 选项

    在lwip协议栈中,在tcp.c中有如下代码

    #if SO_REUSE && SO_REUSE_RXTOALL
    if ((broadcast || ip_addr_ismulticast(&current_iphdr_dest)) &&
    ((pcb
    ->so_options & SOF_REUSEADDR) != 0)) {
    …………………………
    }
    #endif /* SO_REUSE && SO_REUSE_RXTOALL */

    对于其上的两个条件编译选项,其定义在opt.h中

    /**
    * SO_REUSE==1: Enable SO_REUSEADDR option.
    */
    #ifndef SO_REUSE
    #define SO_REUSE 0
    #endif

    /**
    * SO_REUSE_RXTOALL==1: Pass a copy of incoming broadcast/multicast packets
    * to all local matches if SO_REUSEADDR is turned on.
    * WARNING: Adds a memcpy for every packet if passing to more than one pcb!
    */
    #ifndef SO_REUSE_RXTOALL
    #define SO_REUSE_RXTOALL 0
    #endif

    根据SO_REUSEADDR线索,搜索其作用在Richard Stevens的《Unix网络编程指南》卷一里有很详细的解答(中文版P166-168页)。现摘抄如下

    SO_REUSEADDR可以用在以下四种情况下。
    (摘自《Unix网络编程》卷一,即UNPv1)
    1、当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,你的程序就要用到该选项。
    2、SO_REUSEADDR允许同一port上启动同一服务器的多个实例(多个进程)。但每个实例绑定的IP地址是不能相同的。在有多块网卡或用IP Alias技术的机器可以测试这种情况。
    3、SO_REUSEADDR允许单个进程绑定相同的端口到多个socket上,但每个socket绑定的ip地址不同。这和2很相似,区别请看UNPv1。
    4、SO_REUSEADDR允许完全相同的地址和端口的重复绑定。但这只用于UDP的多播,不用于TCP。

    注:当两个socket的address和port相冲突,而你又想重用地址和端口,则旧的socket和新的socket都要已经被设置了SO_REUSEADDR特性,只有两者之一有这个特性还是有问题的。

    参考文章给出了各个条件下的测试程序。也一并摘录如下

    View Code
    #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*/
    }
    }

    运行程序,然后telnet localhost 10013,看结果

    2.无测试用例

    3.

    View Code
    #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的使用没多大关系,所以就不写测试例子了。

    参考文章

    《socket编程:SO_REUSEADDR例解》

  • 相关阅读:
    CFgym102394I
    Infinite Fraction Path (后缀数组)
    2016ACM/ICPC亚洲区沈阳站-重现赛
    2sat学习笔记
    bzoj4176
    bzoj3309
    6C
    3U
    3T
    3R
  • 原文地址:https://www.cnblogs.com/westfly/p/1978341.html
Copyright © 2011-2022 走看看