zoukankan      html  css  js  c++  java
  • tcp_tw_reuse文件及SO_REUSEADDR对端口重用的一点不同

    一、端口重用
    这本是没什么意思的一个东西,只是别人那么随便一问,自己也是没啥事情,就看了一下,可能没什么实际意义。从名字上看,两者都是端口重用的表示,可能是和socket的发送接收缓冲区一个,一个是全局的,一个是实例私有的。但是看了一下代码,感觉好像不是这样。
    二、SO_REUSEADDR选项的使用
    int sock_setsockopt(struct socket *sock, int level, int optname,
                char __user *optval, int optlen)

            case SO_REUSEADDR:
                sk->sk_reuse = valbool;
    当socket绑定端口时,该选项被检测使用
    static int tcp_v4_get_port(struct sock *sk, unsigned short snum)
    {
        return inet_csk_get_port(&tcp_hashinfo, sk, snum,
                     inet_csk_bind_conflict);
    }

    tb_found:
        if (!hlist_empty(&tb->owners)) {
            if (sk->sk_reuse > 1如果reuse的值大于1,则不管是否被占用,直接返回端口可用
                goto success;
            if (tb->fastreuse > 0 &&
                sk->sk_reuse && sk->sk_state != TCP_LISTEN) {
                goto success;
            } else {
                ret = 1;
                if (bind_conflict(sk, tb))
                    goto fail_unlock;
            }
        }

    /*
     * This array holds the first and last local port number.
     * For high-usage systems, use sysctl to change this to
     * 32768-61000
     */
    int sysctl_local_port_range[2] = { 1024, 4999 };

    int inet_csk_bind_conflict(const struct sock *sk,
                   const struct inet_bind_bucket *tb)
    {
        const __be32 sk_rcv_saddr = inet_rcv_saddr(sk);
        struct sock *sk2;
        struct hlist_node *node;
        int reuse = sk->sk_reuse;

        sk_for_each_bound(sk2, node, &tb->owners) {
            if (sk != sk2 &&
                !inet_v6_ipv6only(sk2) &&
                (!sk->sk_bound_dev_if ||
                 !sk2->sk_bound_dev_if ||
                 sk->sk_bound_dev_if == sk2->sk_bound_dev_if)) {
                if (!reuse || !sk2->sk_reuse ||
                    sk2->sk_state == TCP_LISTEN
    ) {
                    const __be32 sk2_rcv_saddr = inet_rcv_saddr(sk2);
                    if (!sk2_rcv_saddr || !sk_rcv_saddr ||
                        sk2_rcv_saddr == sk_rcv_saddr)
                        break;
                }
            }
        }
        return node != NULL;
    }
    所有使用该端口的socket都必须设置为可重用(包括新的申请该地址的socket),并且之前端口没有处于listening状态,则该端口可以给新申请者使用。
    三、proc文件选项使用
    tcp_v4_connect--->>inet_hash_connect--->>>__inet_check_established--->>tcp_twsk_unique

    int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
    {
        const struct tcp_timewait_sock *tcptw = tcp_twsk(sktw);
        struct tcp_sock *tp = tcp_sk(sk);

        /* With PAWS, it is safe from the viewpoint
           of data integrity. Even without PAWS it is safe provided sequence
           spaces do not overlap i.e. at data rates <= 80Mbit/sec.

           Actually, the idea is close to VJ's one, only timestamp cache is
           held not per host, but per port pair and TW bucket is used as state
           holder.

           If TW bucket has been already destroyed we fall back to VJ's scheme
           and use initial timestamp retrieved from peer table.
         */
        if (tcptw->tw_ts_recent_stamp &&
            (twp == NULL || (sysctl_tcp_tw_reuse &&
                     xtime.tv_sec - tcptw->tw_ts_recent_stamp > 1))) {
            tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
            if (tp->write_seq == 0)
                tp->write_seq = 1;
            tp->rx_opt.ts_recent       = tcptw->tw_ts_recent;
            tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
            sock_hold(sktw);
            return 1;
        }

        return 0;
    }
    也就是proc中参数只有在执行connect操作时才会生效,而server如果bind一个地址,即使系统设置为tcp_tw_reuse为1,而sol_sockreuse没有设置,端口依然无法绑定成功。
    4、验证一下
    下面测试代码修改自google搜索到的一个文件http://blog.csdn.net/zxcasdqwe123asd/article/details/8184647
    [root@Harry tcpsvr]# netstat -anp | grep tcps
    tcp        0      0 0.0.0.0:46175               0.0.0.0:*                   LISTEN      3030/tcpsvr.c.out   
    [root@Harry tcpsvr]# vi tcpsvr.c
    [root@Harry tcpsvr]# cat tcpsvr.c
    #include<stdio.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<errno.h>
    #include<stdlib.h>
    #include<strings.h>
    #include<string.h>

    #define TRY_TIME 128
    #define MAX_CONNECT 12
    #define BUFFER_SIZE 1024
    //端口
    #define SERPORT 2222

    void printerr()
    {
     fprintf(stderr,"server error %s",strerror(errno));
    }

    int main(int argc,char *argv){
     struct sockaddr_in serv;
     struct sockaddr_in client;
     char addr[INET_ADDRSTRLEN];
     int fd;
     char buf[BUFFER_SIZE];
     int accept_fd;
     int reuse=1;
     int len=sizeof(struct sockaddr_in);
     bzero(&serv,sizeof(struct sockaddr_in));
     serv.sin_family=AF_INET;
     serv.sin_addr.s_addr=htonl(INADDR_ANY);
     serv.sin_port=htons(SERPORT);
     //创建一个socket;
     if((fd=socket(AF_INET,SOCK_STREAM,0))<0)
     {
      printf("socket");
      printerr();
      return -1;
     }
     /*
     if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(int))<0)
     {
      printerr();
      return -1;
     }
     */
     //绑定
     if(bind(fd,(struct sockaddr *)&serv,sizeof(struct sockaddr)))
     {
      printf("bind");
      printerr();
      return -1;
     }
     //监听
     
     if(listen(fd,MAX_CONNECT)){
      printf("listen");
      printerr();
      return -1;
     }
     //不断地接受连接
     while(1){
      bzero(buf,BUFFER_SIZE);
      if((accept_fd=accept(fd,(struct sockaddr *)&client,&len))<0)
      {
      printerr();
      return -1;
      }
      inet_ntop(AF_INET,&client.sin_addr,addr,INET_ADDRSTRLEN);
      printf("accept from %s ",addr);
      
      recv(accept_fd,buf,BUFFER_SIZE,0);
      printf("%s ",buf);
      //close(accept_fd);
      sleep(1000);
     }
     close(fd);
     return 0;
    }
    [root@Harry tcpsvr]# gcc tcpsvr.c -o tcpsvr.c.out
    [root@Harry tcpsvr]# ./tcpsvr.c.out &
    [1] 3068
    [root@Harry tcpsvr]# cat /proc/sys/net/ipv4/tcp_tw_reuse 
    1
    [root@Harry tcpsvr]# ./tcpsvr.c.out &
    [1] 3082
    该步骤完成之后在另一个终端中telnet 127.0.0.1 2222 之后不作任何操作,也就是只链接到该端口,建立一个完成三次握手的tcp链路。之后快速杀死服务器。
    [root@Harry tcpsvr]# accept from 127.0.0.1

    [root@Harry tcpsvr]# kill 3082 ; sleep 1 ; ./tcpsvr.c.out 

    [1]+  Terminated              ./tcpsvr.c.out
    server error Address already in usebind[root@Har
    可以看到,依然提示该端口已经被占用。

  • 相关阅读:
    jQuery DOM
    jQuery DOM基础
    h1标签
    DOM节点操作
    css选择器
    机器指令程序编写方法
    处理器和指令
    《支撑处理器的技术——永无止境地追求速度的世界》图书信息
    浮点数精度的转换
    《细说PHP(第2版)》图书信息
  • 原文地址:https://www.cnblogs.com/tsecer/p/10487499.html
Copyright © 2011-2022 走看看