thlm0 2019-10-28
前面从stackoverflow上找了一篇讲这两个选项的文章,文章内容很长,读到最后对Linux中的这两个选项还是有些迷茫,所以重新写一篇文章来做一个总结;
本文只总结TCP单播部分,并且只讨论该选项的bind()系统调用部分,UDP,组播,开启选项之后数据包的调度等不做讨论;
SO_REUSEADDR Indicates that the rules used in validating addresses supplied in a bind(2) call should allow reuse of local addresses. For AF_INET sockets this means that a socket may bind, except when there is an active listening socket bound to the address. When the listening socket is bound to INADDR_ANY with a specific port then it is not possible to bind to this port for any local address. Argument is an integer boolean flag.
原文简译:让bind()系统调用能够重用本地地址;对于AF_INET协议族的套接字来讲,除非有活动的监听套接字绑定到该地址,否则其他情况下可以重复绑定;如果一个监听套接字绑定到了通配地址INADDR_ANY,那么其他任何绑定到该端口的请求都是不允许的;
解释:
(1) 如果原监听绑定是指定地址,则相同IP+相同端口,通配IP+相同端口不允许;但是不同IP+相同端口的绑定是允许的;
(2) 若果原监听绑定是通配地址,则任何向该端口的绑定都不可以;
如下面表格所示:
已绑定 请求绑定 成功与否 ------------------------------------------------ 192.168.0.100:22345 192.168.0.100:22345 失败 192.168.0.100:22345 0.0.0.0:22345 失败 192.168.0.100:22345 192.168.0.101:22345 成功 0.0.0.0:22345 192.168.0.100:22345 失败 0.0.0.0:22345 0.0.0.0:22345 失败
SO_REUSEPORT Permits multiple AF_INET or AF_INET6 sockets to be bound to an identical socket address. This option must be set on each socket (including the first socket) prior to calling bind(2) on the socket. To prevent port hijacking, all of the processes binding to the same address must have the same effective UID. This option can be employed with both TCP and UDP sockets. For TCP sockets, this option allows accept(2) load distribution in a multi-threaded server to be improved by using a distinct listener socket for each thread. This provides improved load distribution as compared to traditional techniques such using a single accept(2)ing thread that distributes connections, or having multiple threads that compete to accept(2) from the same socket. For UDP sockets, the use of this option can provide better distribution of incoming datagrams to multiple processes (or threads) as compared to the traditional technique of having multiple processes compete to receive datagrams on the same socket.
原文简译:该选项允许完全相同地址重复绑定;需要重复绑定的每个套接字都要设置该选项,包括第一个;为了防止端口劫持,重复绑定必须是统一有效用户ID下的进程;在TCP服务器中,多个线程同时监听相同地址,则每个线程的accpet()调用会进行负载均衡,将请求进行平均分发;这与传统的套接字相比提供了更好的负载均衡,原方式比如单一线程accpet()来分发请求,或者多个accept()从一个套接字上竞争请求;
关于绑定,没有什么疑问,SO_REUSEADDR中列出表格中的地址都能绑定成功;
if ((!reuse || !sk2->sk_reuse || sk2->sk_state == TCP_LISTEN) && (!reuseport || !sk2->sk_reuseport || rcu_access_pointer(sk->sk_reuseport_cb) || (sk2->sk_state != TCP_TIME_WAIT && !uid_eq(uid, sock_i_uid(sk2))))) { if (inet_rcv_saddr_equal(sk, sk2, true)) break; }
源码解析,分以下四种情况:
未启用地址重用 && 未启用端口重用:检查冲突;
启用了地址重用 && 未启用端口重用:状态是LISTEN才检查冲突;
未启用地址重用 && 启用了端口重用:状态不是TIME_WAIT并且不是同一有效用户ID时,检查冲突;也就是说,假若是TIME_WAIT,则不需要检查;假如不是TIME_WAIT,但是有效用户ID相同,也不需要检查;
启用了地址重用 && 启用了端口重用:状态是LISTEN时,可能需要检查,需要继续判断端口重用,这时候只当有效用户ID不相同的时候,才需要检查;也就是说,可以相同用户ID的进程可以同时LISTEN多个相同的地址+端口;