zoukankan      html  css  js  c++  java
  • 端口重用

    前言

    服务器重启进程时总会提示端口已经被绑定的报错,直到重试好几次才能重启成功。
    这是因为端口尚未完全关闭的情况,这时如果不设置端口重用,则无法完成绑定,因为端口还处于被别的套接口绑定的状态之中。

    SO_REUSEADDR

    简介

    1. 允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将此端口用做他们的本地端口的连接仍存在。这通常是重启监听服务器时出现,若不设置此选项,则bind时将出错。
    2. 允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。对于TCP,我们根本不可能启动捆绑相同IP地址和相同端口号的多个服务器。
    3. 允许单个进程捆绑同一端口到多个套接口上,只要每个捆绑指定不同的本地IP地址即可。这一般不用于TCP服务器。
    4. 允许完全重复地捆绑:当一个IP地址和端口绑定到某个套接口上时,还允许此IP地址和端口捆绑到另一个套接口上。一般来说,这个特性仅在支持多播的系统上才有,而且只对UDP套接口而言(TCP不支持多播)。

    Python中的用法

    1. 测试源码
    import socket
    
    serveripaddr = '127.0.0.1'
    tcp_listen_addr = (serveripaddr, 12345)
    set_reuse_addr = False # True:允许重用,不会报错.False:默认不支持重用,会报错
    
    sock1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    if set_reuse_addr:
        sock1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock1.bind(tcp_listen_addr)
    sock1.listen(1)
    
    sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    if set_reuse_addr:
        sock2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock2.bind(tcp_listen_addr)
    sock2.listen(1)
    
    1. 不允许端口重用有如下报错:
    Traceback (most recent call last):
      File "C:	est.py", line 17, in <module>
        sock2.bind(tcp_listen_addr)
    OSError: [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。
    

    golang用法

    已经看了go源码,没能琢磨出不修改源码的方案,大家有办法搞定,记得给我说说额。
    我这边是通过修改go源码,编译出来的可执行程序默认就支持端口重用。

    1. 修改源码:gosrc etsockopt_windows.go,按照如下方法加入一段代码。
    func setDefaultSockopts(s syscall.Handle, family, sotype int, ipv6only bool) error {
    	if family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
    		// Allow both IP versions even if the OS default
    		// is otherwise. Note that some operating systems
    		// never admit this option.
    		syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, boolint(ipv6only))
    	}
    	// 加入代码,Start
    	if family == syscall.AF_INET && sotype == syscall.SOCK_STREAM {
    		syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
    	}
    	// 加入代码,End
    	if (sotype == syscall.SOCK_DGRAM || sotype == syscall.SOCK_RAW) && family != syscall.AF_UNIX && family != syscall.AF_INET6 {
    		// Allow broadcast.
    		return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1))
    	}
    	return nil
    }
    
    1. 修改源码:gosrc etsockopt_linux.go,按照如下方法加入一段代码。
    func setDefaultSockopts(s, family, sotype int, ipv6only bool) error {
    	if family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
    		// Allow both IP versions even if the OS default
    		// is otherwise. Note that some operating systems
    		// never admit this option.
    		syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, boolint(ipv6only))
    	}
    	// 加入代码,Start
    	if family == syscall.AF_INET && sotype == syscall.SOCK_STREAM {
    		syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
    	}
    	// 加入代码,End
    	if (sotype == syscall.SOCK_DGRAM || sotype == syscall.SOCK_RAW) && family != syscall.AF_UNIX {
    		// Allow broadcast.
    		return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1))
    	}
    	return nil
    }
    
    1. 修改完源码,编译时需要带上-a强制重新编译所以包,不然还是会使用缓存的.a文件进行编译,例如:go build -a test.go

    其他学习

    关于socket有如下5个重要元素,只要其中一个不同,那系统就能区别不同的socket连接。只要5个完全相同,则后面建立绑定的代码会报报错。
    因此实际项目中,可以由不同进程对同一个端口不同IP进行绑定。例如可以同时绑定“127.0.0.0:12345”和“192.168.1.10:12345”,操作系统知道只是两个不同的绑定。

    SOCKET 本方IP 本方Port 目的IP 目的Port 协议
    sokcet1 127.0.0.1 8000 192.168.1.1 9000 Tcp
    socket2 127.0.0.1 8000 192.168.1.1 10000 Tcp

    总结

    运用端口重用对于我来说最大的方便就是重启进程快了很多,不用一遍遍尝试绑定端口,都不知道啥时候可以成功。
    还有就是通过学习,认识到建立监听的5个元素,只要其中一个不同,就能实例化多个socket连接。

  • 相关阅读:
    一张图片入门Python
    4.1. 如何在Windows环境下开发Python
    你必须知道的EF知识和经验
    XUnit的使用
    如何使用NUnit
    Entity Framework 不支持DefaultValue
    Have You Ever Wondered About the Difference Between NOT NULL and DEFAULT?
    Validation failed for one or more entities. See 'EntityValidationErrors' property for more details
    Entity Framework 与多线程
    sqlite中的自增主键
  • 原文地址:https://www.cnblogs.com/janbar/p/13965098.html
Copyright © 2011-2022 走看看