SO_REUSEADDR套接字选项

##features
SO_REUSEADDR套接字选项能起到以下4个不同的功用。

  • SO_REUSEADDR允许启动一个监听服务器并捆绑其众所周知的端口,即使以前建立的将该端口用作他们的本地端口的连接仍存在。这个条件通常是这样碰到的:
    1. 启动一个监听服务器;
    • 连接请求到达,派生一个子进程来处理这个客户;
    • 监听服务器终止,但子进程继续为现有连接上的客户提供服务;
    • 重启监听服务器。

  默认情况下,当监听服务器在步骤4通过调用socket,bind和listen重新启动时,由于他试图捆绑一个现有连接(即正由早先派生的那个子进程处理着的连接)上的端口,从而bind调用会失败。但是如果该服务器在socket和bind两个调用之间设置了SO_REUSEADDR套接字选项,那么将成功。所有TCP服务器都应该指定本套接字选项,以允许服务器在这种情况下被重新启动。

  • SO_REUSEADDR允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。这对于使用IP别名技术托管多个http服务器的网点来说是很常见的。举例来说,假设本地主机的主ip地址是198.19.10.2,不过他有两个别名:198.69.10.128和198.69.10.129。在其上启动三个http服务器。第一个http服务器以本地通配ip地址INADDR_ANY和本地端口号80(http的众所周知端口)调用bind。第二个http服务器以本地ip地址198.69.10.128和本地端口号80调用bind。这次调用bind将失败,除非在调用前设置了SO_REUSEADDR套接字选项。第三个http服务器以本地ip地址198.69.10.129和本地端口号80调用bind。这次调用bind成功的先决条件同样是预先设置SO_REUSEADDR。假设SO_REUSEADDR均已设置,从而三个服务器都启动了,目的ip地址为198.69.10.128、目的端口号为80的外来tcp请求将被递送给第二个服务器,目的地址为198.69.10.129、目的端口号为80的外来连接请求将被递送给第三个服务器,目的端口号为80的所有其他tcp连接请求都将递送给第一个服务器,这个“默认”服务器处理目的地址为198.69.10.2或该主机已配置的任何其他ip别名的请求。这里通配地址的意思是“没有更好的(即更为明确的)匹配地址”。注意,允许某个给定服务存在多个服务器的情形在服务器设置SO_REUSEADDR套接字选项时是自动处理的(我们建议设置这个选项)

对于TCP,我们绝对不可能启动捆绑相同IP地址和相同端口号的多个服务器:这是完全重复的捆绑。也就是说,我们不可能在启动绑定198.69.10.2和端口80的服务器之后,再启动同样捆绑198.69.10.2和端口80的另一个服务器,即使我们给第二个服务器设置了SO_REUSEADDR套接字也不管用。

  • SO_REUSEADDR 允许单个进程捆绑同一端口到多个套接字上,只要每次捆绑指定不同的本地IP地址即可。

  • SO_REUSEADDR允许完全重复的捆绑:当一个IP地址和端口号已绑定到某个套接字上时,如果传输协议支持,同样的IP地址和端口还可以捆绑到另一个套接字上。一般来说本特性仅支持UDP套接字。

##problem

这就引出了另外的一个问题,就是设置这个属性后允许一个套接字上同时有两个应用程序进行监听,那系统究竟会将数据发送给哪一个程序呢?

系统会将数据首先交给监听IP最确定的应用程序。例如应用程序A在调用监听函数时设置的属性是addr.sin_addr.S_addr = INADDR_ANY;而另外的一个应用程序B则监听的地址为addr.sin_addr.S_addr = inet_addr(PChar(sMainIP)),同时这两个应用程序监听的端口都是相同的,这时系统接收到数据后会首先交给B然后再交给A。因此一般为了程序安全我们会禁止这种情况的发生。