SO_REUSEADDR和SO_REUSEPORT作用

本文内容主要是对SO_REUSEADDR和SO_REUSEPORT异同 的理解和总结,重点记录linux下SO_REUSEADDR和SO_REUSEPORT的作用和区别

背景

SO_REUSEADDR和SO_REUSEPORT主要是影响socket绑定ip和port的成功与否。先简单说几点绑定规则
规则1:socket可以指定绑定到一个特定的ip和port,例如绑定到192.168.0.11:9000上;
规则2:同时也支持通配绑定方式,即绑定到本地"any address"(例如一个socket绑定为 0.0.0.0:21,那么它同时绑定了所有的本地地址);
规则3:默认情况下,任意两个socket都无法绑定到相同的源IP地址和源端口。

在了解了上述背景后下面简单说明一下linux中SO_REUSEADDR和SO_REUSEPORT对绑定的影响。由于大多数平台对SO_REUSEADDR和SO_REUSEPORT的实现都是BSD上的衍生版本,因此的先介绍BSD中这两个参数的作用。

SO_REUSEADDR

SO_REUSEADDR的作用主要包括两点
1、改变了通配绑定时处理源地址冲突的处理方式,其具体的表现方式为:未设置SO_REUSEADDR时,socketA先绑定到0.0.0.0:21,后socketB绑定192.168.0.1:21将失败,不符合规则3。但在设置SO_REUSEADDR后socketB将绑定成功。并且这个设置对于socketA(通配绑定)和socketB(特定绑定)的绑定是顺序无关的。下表总结了BSD在各个情况下的绑定情况

SO_REUSEADDR 先绑定socketA 后绑定socketB 绑定socketB的结果
无关 192.168.0.1:21 192.168.0.1:21 Error (EADDRINUSE)
无关 192.168.0.1:21 10.0.0.1:21 OK
无关 10.0.0.1:21 192.168.0.1:21 OK
disable 0.0.0.0:21 192.168.1.0:21 Error (EADDRINUSE)
disable 192.168.1.0:21 0.0.0.0:21 Error (EADDRINUSE)
enable 0.0.0.0:21 192.168.1.0:21 OK
enable 192.168.1.0:21 0.0.0.0:21 OK
无关 0.0.0.0:21 0.0.0.0:21 Error (EADDRINUSE)

对于linux,要使这个设置达到预期效果,对于绑定的顺序的有要求的,即在设置了SO_REUSEADDR,须先进行特定绑定,后进行通配绑定,后者才能成功;如果先进行通配绑定,后面的绑定(端口相同情况下)地址只要和通配绑定中的一个相同都将失败。

2、改变了系统对处于TIME_WAIT状态的socket的看待方式,要理解这个句话,首先先简单介绍以下什么是处于TIME_WAIT状态的socket?

socket通常都有发送缓冲区,当调用send()函数成功后,只是将数据放到了缓冲区,并不意味着所有数据真正被发送出去。对于TCP socket,在加入缓冲区和真正被发送之间的时延会相当长。这就导致当close一个TCP socket的时候,可能在发送缓冲区中保存着等待发送的数据。为了确保TCP的可靠传输,TCP的实现是close一个TCP socket时,如果它仍然有数据等待发送,那么该socket会进入TIME_WAIT状态。这种状态将持续到数据被全部发送或者发生超时(这个超时时间通常被称为Linger Time,大多数系统默认为2分钟)。

在未设置SO_REUSEADDR时,内核将一个处于TIME_WAIT状态的socketA仍然看成是一个绑定了指定ip和port的有效socket,因此此时如果另外一个socketB试图绑定相同的ip和port都将失败(不满足规则3),直到socketA被真正释放后,才能够绑定成功。如果socketB设置SO_REUSEADDR(仅仅只需要socketB进行设置),这种情况下socketB的绑定调用将成功返回,但真正生效需要在socketA被真正释放后。(这个地方的理解可能有点问题,待后续验证一下)。总结一下:内核在处理一个设置了SO_REUSEADDR的socket绑定时,如果其绑定的ip和port和一个处于TIME_WAIT状态的socket冲突时,内核将忽略这种冲突,即改变了系统对处于TIME_WAIT状态的socket的看待方式。

SO_REUSEPORT

SO_REUSEPORT作用就比较明显直观,即打破了上面的规则3
1、允许将多个socket绑定到相同的地址和端口,前提每个socket绑定前都需设置SO_REUSEPORT。如果第一个绑定的socket未设置SO_REUSEPORT,那么其他的socket无论有没有设置SO_REUSEPORT都无法绑定到该地址和端口直到第一个socket释放了绑定。

2、attention:SO_REUSEPORT并不表示SO_REUSEADDR,即不具备上述SO_REUSEADDR的第二点作用(对TIME_WAIT状态的socket处理方式)。因此当有个socketA未设置SO_REUSEPORT绑定后处在TIME_WAIT状态时,如果socketB仅设置了SO_REUSEPORT在绑定和socketA相同的ip和端口时将会失败。解决方案
(1)、socketB设置SO_REUSEADDR 或者socketB即设置SO_REUSEADDR也设置SO_REUSEPORT
(2)、两个socket上都设置SO_REUSEPORT

Linux 内核3.9加入了SO_REUSEPORT。除上述功能外其额外实现了
1、为了阻止port 劫持Port hijacking,限制所有使用相同ip和port的socket都必须拥有相同的有效用户id(effective user ID)。
2、linux内核在处理SO_REUSEPORT socket的集合时,进行了简单的负载均衡操作,即对于UDP socket,内核尝试平均的转发数据报,对于TCP监听socket,内核尝试将新的客户连接请求(由accept返回)平均的交给共享同一地址和端口的socket(监听socket)。

理解可能有误,可参考原文http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t

你可能感兴趣的:(SO_REUSEADDR和SO_REUSEPORT作用)