SO_REUSEADDR与SO_REUSEPORT平台差异性与测试

  前些天,与另外一个项目组的同事聊天的时候,谈到他遇到的一个有意思的BUG。在window上启动服务器,然后客户端连接的时候收到一些奇怪的消息,查证了,原来是他自己的另一个工具也在相同的地址上监听,客户端连接到了后面这个工具程序上。我问他,是相同的IP和端口?他说是的,因为服务器代码和工具程序都设置了SO_REUSEADDR这个socket选项,所以可以在同样的地址上监听。

  可是,在我的认知里面, SO_REUSEADDR这个选项并不是说让两个程序在相同地址(相同的IP 和 端口)上监听,而是说可以让处于time_wait状态的socket可以快速复用,搜了一下,看到的这篇文章,也是这么说的:

  SO_REUSEADDR allows your server to bind to an address which is in a  TIME_WAIT state. It does not allow more than one server to bind to   the same address.

  看了一下Linux manual,关于这个选项是这么描述的:

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.

  manual并没有提到time_wait的事情,但是明确指出,如果一个socket处于listen状态,那么同样的端口(port)是不能再次被绑定的(binding),不能binding,自然也不能再次listen,因此是不可能两个程序在相同的地址(IP PORT)上监听的。

  于是自己用python在写了一个小的测试程序:

  服务端代码:

 1 # -*- coding: utf-8 -*-
 2 import socket, sys
 3 import time
 4
 5 def main():
 6     HOST, PORT = sys.argv[1], 8888
 7
 8     listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 9     listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
10     # print listen_socket.getsockopt(socket.SOL_SOCKET, socket.SO_EXECLUSIVEADDRUS)
11
12     listen_socket.bind((HOST, PORT))
13     listen_socket.listen(10)
14
15     print ‘Serving on host %s port %s ...‘ %(HOST, PORT)
16     while True:
17         client_connection, client_address = listen_socket.accept()
18         request = client_connection.recv(1024)
19         print ‘client ‘, request
20
21         for i in range(5):
22             http_response = """23             hello
24             """
25             client_connection.sendall(http_response)
26             time.sleep(3)
27         client_connection.close()
28
29 if __name__ == ‘__main__‘:
30     main()

tcp_server.py

  客户端代码:

 1 import socket, sys
 2
 3 def main():
 4     server_address = ("localhost" if len(sys.argv) == 1 else sys.argv[1],8888)
 5     s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 6     s.connect(server_address)
 7     print s.getpeername()
 8     s.send(‘I AM CLIENT‘)
 9     while True:
10         data = s.recv(1024)
11         print " %s received %s" % (s.getpeername(),data)
12         if not data:
13             print "closing socket ",s.getpeername()
14             s.close()
15
16 if __name__ == ‘__main__‘:
17     main()

tcp_client.py

  服务端代码设置了SO_REUSEADDR,在Linux下, 确实不能在相同的地址(IP, Port)上监听, 但是在windows上,却又是可以的。于是想到,这个选项可能与平台相关。

平台差异性

  网上搜了一下,结果发现了这篇文章《SO_REUSEADDR和SO_REUSEPORT异同》,该文章翻译自stackoverflow上的这个问答《socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t》,关于SO_REUSEADDR和SO_REUSEPORT这两个选项在不同平台上的表现介绍得很清楚。不过,中文翻译水平不怎么好,像是用机器翻译的,可以的话还是尽量看原文。

  本文记录一下这个问答的要点,并用上面的小程序在各个平台(Linux, Mac, Windows)上进行测试。注意,本文只关注TCP、单播,事实上原问答还包括UDP、多播知识,感兴趣的读者可以自行阅读。

  第零:一条tcp连接是一个五元祖: {<protocol>, <src addr>, <src port>, <dest addr>, <dest port>}

    第一:SO_REUSEPORT和SO_REUSEADDR在不同的操作系统上行为是不一样的

  第二:默认情况下,任意两个socket都无法绑定到相同的源IP地址和源端口, 0.0.0.0 (即INADDR_ANY )和所有其他地址冲突

  第三:BSD系统下

    SO_REUSEADDR 使得0.0.0.0 与 其他地址不冲突

    SO_REUSEPORT允许你将多个socket绑定到相同的地址和端口, 但第一个启动的socket必须设置SO_REUSEPORT

  第四:MacOS IOS 表现同 BSD

  第五:Linux

    SO_REUSEADDR 只要有socket处于listen状态, 就不能在同样的地址和端口上listen, 0.0.0.0 与其他所有地址冲突

    只要监听前设置了SO_REUSEPORT(在Linux3.9版本之后可用) ,就可以在相同的(ip port)上监听

    对于SO_REUSEPORT:为了阻止"port 劫持"(Port hijacking)有一个特别的限制,所有希望共享源地址和端口的socket都必须拥有相同的有效用户id(effective user ID);对于TCP监听socket,内核尝试将新的客户连接请求(由accept返回)平均的交给共享同一地址和端口的socket(监听socket)

  第六:Android同Linux

  第七:Windows

    只有SO_REUSEADDR选项,没有SO_REUSEPORT。

    设置SO_REUSEADDR 等价于BSD上设定了SO_REUSEPORT和SO_REUSEADDR,而且不管之前的端口是否设定了SO_REUSEADDR(存疑)

     上述选项存在风险:因为允许一个应用程序从别的应用程序上"偷取"已连接的端口。因此在windows上加入了另一个socket选项: SO_EXECLUSIVEADDRUSE。设置了SO_EXECLUSIVEADDRUSE的socket确保一旦绑定成功,那么被绑定的源端口和地址就只属于这一个socket,其它的socket不能绑定,甚至他们使用了SO_REUSEADDR也没用。

测试

  在后文涉及到的三个平台(Linux 、MacOS、Windows),都涉及到三个IP:127.0.0.1, 0.0.0.0,10.0.0.x(局域网IP)。使用的脚本如上(tcp_server.py, tcp_client.py),运行的时候需要简单修改tcp_server.py中第9、10行的注释,以便测试不同选项下的效果。

MAC

  由于没有BSD系统,而且前文提到MacOS和BSD系统的表现是一样的,因此在这里实在MAC上测试

  在不使用SO_REUSEADDR (此时未使用SO_REUSEPORT)时:

  

  注意:first指第一条监听的socket,second指第二条希望在同样的端口(port)上监听的连接。兼容指第二条连接可以成功监听,不兼容则指第二条连接不能成功监听。下同

  在使用SO_REUSEADDR(此时未使用SO_REUSEPORT)时:

  

  在使用SO_REUSEADDR情况下,如果第一个scoket在0.0.0.0上监听,第二个scoket在127.0.0.1上监听。那么客户端使用127.0.0.1连接的时候会连接到第二个socket;使用10.0.0.x则会连接到第一个socket

  使用SO_REUSEPORT(同时使用了SO_REUSEADDR):

  

  如果两个socket都在127.0.0.1上监听,客户端也通过127.0.0.1去连接,那么客户端连接都会发被第二个socket accept, 笔者并发实验了几十次都是这样, 但并没有找到明确的官方文档说明是否是这样。

Linux

  在不使用SO_REUSEADDR (此时未使用SO_REUSEPORT)时:

  

  在使用SO_REUSEADDR(此时未使用SO_REUSEPORT)时:

  

  从上面两个测试可以看到,在linux下,是否使用SO_REUSEADDR并不影响两个socket的监听

  使用SO_REUSEPORT(同时使用了SO_REUSEADDR):

  

  如果两个socket都在127.0.0.1上监听,客户端也通过127.0.0.1去连接, 那么客户端连接会被操作系统分发到两个socket上,具体如下

  客户端并发10次连接: for ((a=1;a<=10;a++)) ; do (python tcp_client.py 127.0.0.1 &); done

  第一个socket accept了六次, 第二个socket accept了10次。

Windows

  前面已经提到,windows下面只有SO_REUSEADDR选项,但其功能类似bsd系统下的SO_REUSEADDR与SO_REUSEPORT

  在不使用SO_REUSEADDR时:

  

  比如都在127.0.0.1 上监听时,第二个socket会报错: socket.error: [Errno 10048] 通常每个套接字地址(协议/网络地址/端口)

  使用SO_REUSEADDR时:

  

  此时,如果两个socket都在127.0.0.1上监听,客户端也通过127.0.0.1去连接,那么多次实验的结果都是第一个socket accept。

  

  在上面提到,windows第一个socket可以不使用SO_REUSEADDR,只要第二个socket使用了SO_REUSEADDR,就可以在相同的地址(IP:PORT)上监听。但是我自己试验了一把,并不成功:socket.error: [Errno 10013]

  上面也提到,如果第一个socket使用了SO_EXECLUSIVEADDRUSE选项,那么第二个连接即使使用了SO_REUSEADDR也无济于事,那么是否SO_EXECLUSIVEADDRUSE是默认开启的呢?但是在Python2.7中,socket并没有这个属性

  查了一下MSDN,有附图清晰了说明了在window下SO_REUSEADDR与SO_EXECLUSIVEADDRUSE的关系,如下:

  

  但为什么使用Python的时候 效果不一样呢,这个就没细究了

总结

  本文测试了一下socket中SO_REUSEADDR与SO_REUSEPORT在各个平台下的差异性,一些结论只是实验结果,并没有查到官方权威定论,如果有差错,还请指正!

references

http://www.unixguide.net/network/socketfaq/4.11.shtml

http://man7.org/linux/man-pages/man7/socket.7.html

http://blog.chinaunix.net/uid-28587158-id-4006500.html

https://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t

https://msdn.microsoft.com/en-us/library/windows/desktop/cc150667(v=vs.85).aspx

时间: 2024-10-13 11:23:54

SO_REUSEADDR与SO_REUSEPORT平台差异性与测试的相关文章

Android平台下渗透测试工具大集合

Android平台下渗透测试工具大集合 分享一个google的项目,各种Android下的渗透测试工具. Ad Network Detector (1.2): http://market.android.com/details?id=com.lookout.addetector App Backup & Restore (1.0.5): http://market.android.com/details?id=mobi.infolife.appbackup App Cache Cleaner (

SO_REUSEADDR 和 SO_REUSEPORT

大部分内容来自stackoverflow上的回答:Socket options SO_REUSEADDR and SO_REUSEPORT, how do they differ? Do they mean the same across all major operating systems?     由于现有的操作系统上的socket都来自BSD socket,且每种操作系统都后续进行了相应的改变.下面先说 BSD socket中的行为,在最后一节单独介绍linux系统下的socket特定行

SO_REUSEADDR和SO_REUSEPORT异同

文章内容来源于stackoverflow上的回答,写的很详细http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t      虽然不同的系统上socket的实现方式有一些差异,但都来源于对BSD socket的实现,因此在讨论其它系统之前了解BSD socket的实现是非常有益的.首先我们需要了解一些基本知识

51CTO平台小强系列测试视频全场5-6折,心动不如行,错过又的等N久!

借助学院平台周年庆,一直持续关注小强测试视频的小伙伴们,你们的福利又来了,耐心的看下去,惊喜无限! 6月29日-7月1日,51CTO平台小强测试视频全场5-6折(其中安卓端5折,PC端6折)你还在犹豫什么? 小强测试集结号针对本次活动的额外福利(额外福利有效期延迟至7-15号): 购买课程花费超过500金币的同学,同时享受如下福利: 1.免费赠送<LoadRunner性能测试巧匠训练营>书籍(共三本,送完为止).请截图购买记录发送至[email protected]统计. 2.享受加入51CT

微服务架构 - 离线部署k8s平台并部署测试实例

一般在公司部署或者真实环境部署k8s平台,很有可能是内网环境,也即意味着是无法连接互联网的环境,这时就需要离线部署k8s平台.在此整理离线部署k8s的步骤,分享给大家,有什么不足之处,欢迎指正. 1.准备环境 这次离线部署k8s的版本为v1.10.1,同时docker的版本为17.12.0-ce,不过本文章不介绍如何离线部署docker,如果大家要看的话,可以看本人之前写的文章<CentOS7离线部署docker> 本人准备的环境是3台虚拟机,也即1台master节点,2个node节点,ip及

Visual Studio平台安装及测试

一.VS安装 图1.1 图1.2 二.单元测试练习 题目:课本22~25页单元测试练习 1.创建一个c#类(具体如下:打开VS2010,然后点击VS界面上左上角的文件按钮,然后点击文件—新建—项目,就会出现一个下面的窗口.) 图2.1 2.接着上一步的确定后,系统就会自动创建一个c#的窗口界面 图2.2 3.将课本P22页的代码编入测试                                                                                 

Android_通过Bugtags平台,方便测试人员提交bug及整个bug系统的管理

Bugtags 是什么? Bugtags 是一款缺陷发现及管理工具.当您的 App 集成了 Bugtags SDK 后,测试人员就可直接在 App 里所见即所得的提交 Bug. SDK 会自动截屏,并与设备信息.控制台日志.操作步骤等数据实时同步到 Bugtags 云端,团队成员都可在云端高效的跟踪及解决 Bug. Bugtags 的优势是什么? 1. 宿主应用里所见即所得的提交 Bug,体验流畅,方便快捷: 2. 自动获取 Bug 产生时的界面截图: 3. 直接在宿主应用中标签化描述问题,所提

第四次团队作业:网络订餐平台实现与测试

本产品为基于javascript的网络订餐平台,由于时间与能力的有限,本产品只能实现一个大概的模型,功能比较简单.代码地址:https://github.com/wz1115a/WM 简易快速使用指南与软件测试本产品主要实现:用户注册与登录商家商品界面浏览生成支付页面与个人中心 进入主页 登录与注册页面 可以选择商家与餐品 点击进入付款页面 代码实现界面代码我是参考了不少网站的格式,学习借鉴最后整合出来主要界面: 1 <li><a href="http://localhost:

网络大厂AI平台Coral公开测试协助开发人员实现自己的创意

网络大厂发表本地AI平台Coral的公开测试版,内含开发AI装置必要的软.硬件及内容,以让开发人员可于本地端的装置上建立.训练及执行神经网络,协助开发人员实现自己的创意,打造出原型并进入生产阶段.Coral平台包含了网络大厂在去年发表的Edge张量处理器(Tensor Processing Unit,TPU),这是个专为机器学习应用所打造的特殊应用集成电路(ASIC):以及作为模块系统(SoM)的Coral开发板(Coral Dev Board),在此一开发板上安装NXP iMX8M SoC.E