热门标签 | 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协议基于流,难以确定消息批次的结束点,这给后门攻击的实现带来了挑战。为了解决这一问题,文章提出了一系列有效的技术方案,包括使用特定的分隔符和长度前缀,以确保数据包的准确传输和解析。这些方法不仅提高了攻击的隐蔽性和可靠性,还为安全研究人员提供了宝贵的参考。 ... [详细]
  • MicrosoftDeploymentToolkit2010部署培训实验手册V1.0目录实验环境说明3实验环境虚拟机使用信息3注意:4实验手册正文说 ... [详细]
  • 在 Ubuntu 中遇到 Samba 服务器故障时,尝试卸载并重新安装 Samba 发现配置文件未重新生成。本文介绍了解决该问题的方法。 ... [详细]
  • Java Socket 关键参数详解与优化建议
    Java Socket 的 API 虽然被广泛使用,但其关键参数的用途却鲜为人知。本文详细解析了 Java Socket 中的重要参数,如 backlog 参数,它用于控制服务器等待连接请求的队列长度。此外,还探讨了其他参数如 SO_TIMEOUT、SO_REUSEADDR 等的配置方法及其对性能的影响,并提供了优化建议,帮助开发者提升网络通信的稳定性和效率。 ... [详细]
  • 在Cisco IOS XR系统中,存在提供服务的服务器和使用这些服务的客户端。本文深入探讨了进程与线程状态转换机制,分析了其在系统性能优化中的关键作用,并提出了改进措施,以提高系统的响应速度和资源利用率。通过详细研究状态转换的各个环节,本文为开发人员和系统管理员提供了实用的指导,旨在提升整体系统效率和稳定性。 ... [详细]
  • 优化后的标题:深入探讨网关安全:将微服务升级为OAuth2资源服务器的最佳实践
    本文深入探讨了如何将微服务升级为OAuth2资源服务器,以订单服务为例,详细介绍了在POM文件中添加 `spring-cloud-starter-oauth2` 依赖,并配置Spring Security以实现对微服务的保护。通过这一过程,不仅增强了系统的安全性,还提高了资源访问的可控性和灵活性。文章还讨论了最佳实践,包括如何配置OAuth2客户端和资源服务器,以及如何处理常见的安全问题和错误。 ... [详细]
  • 利用 Python Socket 实现 ICMP 协议下的网络通信
    在计算机网络课程的2.1实验中,学生需要通过Python Socket编程实现一种基于ICMP协议的网络通信功能。与操作系统自带的Ping命令类似,该实验要求学生开发一个简化的、非标准的ICMP通信程序,以加深对ICMP协议及其在网络通信中的应用的理解。通过这一实验,学生将掌握如何使用Python Socket库来构建和解析ICMP数据包,并实现基本的网络探测功能。 ... [详细]
  • 本文详细介绍了一种利用 ESP8266 01S 模块构建 Web 服务器的成功实践方案。通过具体的代码示例和详细的步骤说明,帮助读者快速掌握该模块的使用方法。在疫情期间,作者重新审视并研究了这一未被充分利用的模块,最终成功实现了 Web 服务器的功能。本文不仅提供了完整的代码实现,还涵盖了调试过程中遇到的常见问题及其解决方法,为初学者提供了宝贵的参考。 ... [详细]
  • 在CICS应用环境中,众多客户端通过网络与CICS服务器进行连接。系统管理员可以通过CICS系统交易CEMT查询当前连接的客户端信息。然而,在非客户端模式下,识别用户连接并解决信息获取错误的问题变得更为复杂。本文将探讨如何在CICS服务器端准确识别非客户端模式的用户连接,并提供有效的解决方案,以确保系统的稳定性和数据的准确性。此外,还将介绍一些常用的诊断工具和技术,帮助管理员快速定位和解决问题。 ... [详细]
  • Presto:高效即席查询引擎的深度解析与应用
    本文深入解析了Presto这一高效的即席查询引擎,详细探讨了其架构设计及其优缺点。Presto通过内存到内存的数据处理方式,显著提升了查询性能,相比传统的MapReduce查询,不仅减少了数据传输的延迟,还提高了查询的准确性和效率。然而,Presto在大规模数据处理和容错机制方面仍存在一定的局限性。本文还介绍了Presto在实际应用中的多种场景,展示了其在大数据分析领域的强大潜力。 ... [详细]
  • 在 Python 中,eval() 函数用于将字符串转换为相应的 Python 表达式。然而,eval() 存在安全风险,因为它会执行任何有效的 Python 代码。相比之下,ast.literal_eval() 只评估有限的表达式,确保安全性。 ... [详细]
  • 本文详细介绍了如何在 Django 项目中使用 Admin 管理后台,包括创建超级用户、启动项目、管理数据模型和修改用户密码等步骤。 ... [详细]
  • 思科IOS XE与ISE集成实现TACACS认证配置
    本文详细介绍了如何在思科IOS XE设备上配置TACACS认证,并通过ISE(Identity Services Engine)进行用户管理和授权。配置包括网络拓扑、设备设置和ISE端的具体步骤。 ... [详细]
  • 如何使用 `org.apache.tomcat.websocket.server.WsServerContainer.findMapping()` 方法及其代码示例解析 ... [详细]
  • 在开发过程中,我最初也依赖于功能全面但操作繁琐的集成开发环境(IDE),如Borland Delphi 和 Microsoft Visual Studio。然而,随着对高效开发的追求,我逐渐转向了更加轻量级和灵活的工具组合。通过 CLIfe,我构建了一个高度定制化的开发环境,不仅提高了代码编写效率,还简化了项目管理流程。这一配置结合了多种强大的命令行工具和插件,使我在日常开发中能够更加得心应手。 ... [详细]
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社区 版权所有