热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SO_REUSEADDR与SO_REUSEPORT平台差异性与测试

目录平台差异性测试MACLinuxWindows总结references正文前些天,与另外一个项目组的同事聊天的时候,谈到他遇到的一个有意思的BUG。在

 

正文

  前些天,与另外一个项目组的同事聊天的时候,谈到他遇到的一个有意思的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_REUSEADDRIndicates that the rules used in validating addresses suppliedin a bind(2) call should allow reuse of local addresses. ForAF_INET sockets this means that a socket may bind, except whenthere is an active listening socket bound to the address.When the listening socket is bound to INADDR_ANY with aspecific port then it is not possible to bind to this port forany local address. Argument is an integer boolean flag.

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

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

  服务端代码:


tcp_server.py

  客户端代码:


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连接是一个五元祖: {, , , , }

     第一: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次连接&#xff1a; for ((a&#61;1;a<&#61;10;a&#43;&#43;)) ; do (python tcp_client.py 127.0.0.1 &); done

  第一个socket accept了六次&#xff0c; 第二个socket accept了10次。



Windows

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

  在不使用SO_REUSEADDR时&#xff1a;



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

  使用SO_REUSEADDR时&#xff1a;




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


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

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

  查了一下MSDN&#xff0c;有附图清晰了说明了在window下SO_REUSEADDR与SO_EXECLUSIVEADDRUSE的关系&#xff0c;如下&#xff1a;

  

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



总结


回到顶部

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


推荐阅读
  • Python 伦理黑客技术:深入探讨后门攻击(第三部分)
    在《Python 伦理黑客技术:深入探讨后门攻击(第三部分)》中,作者详细分析了后门攻击中的Socket问题。由于TCP协议基于流,难以确定消息批次的结束点,这给后门攻击的实现带来了挑战。为了解决这一问题,文章提出了一系列有效的技术方案,包括使用特定的分隔符和长度前缀,以确保数据包的准确传输和解析。这些方法不仅提高了攻击的隐蔽性和可靠性,还为安全研究人员提供了宝贵的参考。 ... [详细]
  • 本文介绍了一个基本的同步Socket程序,演示了如何实现客户端与服务器之间的简单消息传递。此外,文章还概述了Socket的基本工作流程,并计划在未来探讨同步与异步Socket的区别。 ... [详细]
  • C#中实现高效UDP数据传输技术
    C#中实现高效UDP数据传输技术 ... [详细]
  • MongoDB高可用架构:深入解析Replica Set机制
    MongoDB的高可用架构主要依赖于其Replica Set机制。Replica Set通过多个mongod节点的协同工作,实现了数据的冗余存储和故障自动切换,确保了系统的高可用性和数据的一致性。本文将深入解析Replica Set的工作原理及其在实际应用中的配置和优化方法,帮助读者更好地理解和实施MongoDB的高可用架构。 ... [详细]
  • 利用Selenium框架解决SSO单点登录接口无法返回Token的问题
    针对接口自动化测试中遇到的SSO单点登录系统不支持通过API接口返回Token的问题,本文提供了一种解决方案,即通过UI自动化工具Selenium模拟用户登录过程,从浏览器的localStorage或sessionStorage中提取Token。 ... [详细]
  • PHP网站部署指南:从零开始搭建PHP网站
    本文提供了详细的步骤指导,帮助开发者在不同环境下成功部署PHP网站,包括在IIS和Apache服务器上的具体操作。 ... [详细]
  • 本文将指导你如何通过自定义配置,使 Windows Terminal 中的 PowerShell 7 更加高效且美观。我们将移除默认的广告和提示符,设置快捷键,并添加实用的别名和功能。 ... [详细]
  • ECharts图表绘制函数集
    本文档提供了使用ECharts库创建柱状图、饼图和双折线图的JavaScript函数。每个函数都详细列出了参数说明,并通过示例展示了如何调用这些函数以生成不同类型的图表。 ... [详细]
  • 本文探讨了Lua中元表和元方法的使用,通过具体的代码示例展示了如何利用这些特性来实现类似C语言中的运算符重载功能。 ... [详细]
  • 拖拉切割直线 ... [详细]
  • 本文详细介绍如何在Spring Boot项目中集成和使用JPA,涵盖JPA的基本概念、Spring Data JPA的功能以及具体的操作步骤,帮助开发者快速掌握这一强大的持久化技术。 ... [详细]
  • 本文探讨了在使用 STL 容器(如 map、vector 和 list)插入自定义类对象或指针时,构造函数和析构函数的调用情况,以及可能引发的问题。 ... [详细]
  • 13、单向链表
    头文件:LinkList.hLinkList.cmain.cVS2 ... [详细]
  • 本文探讨了SQLAlchemy ORM框架中如何利用外键和关系(relationship)来建立表间联系,简化复杂的查询操作。通过示例代码详细解释了relationship的定义、使用方法及其与外键的相互作用。 ... [详细]
  • 本文介绍了如何使用C# Winform开发局域网内的文件传输功能,详细描述了从用户界面到后端网络通信的具体实现。 ... [详细]
author-avatar
陈炘宇_573
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有