53-套接字选项(SO_REUSEADDR)

SO_REUSEADDR 是非常常用的选项之一,它有四个功能,我待会再写,接下来先看实验。

1. 程序路径

代码托管在 gitos 上,请使用下面的命令获取:

git clone https://git.oschina.net/ivan_allen/unp.git

如果你已经 clone 过这个代码了,请使用 git pull 更新一下。本节程序所使用的程序路径是 unp/program/options/opt

2. 关于 opt 程序

opt 程序是对之前的 echo 服务器(多进程服务器 + select 客户端)版本的改写。主要是加了对常用选项的设置,这些选项都是使用 setsockopt 进行设置的。

opt 程序可能在一直更新,你需要使用 git pull 命令随时更新代码。

你可以使用 ./opt --help 查看 opt 程序的选项及其含义。

3. 实验步骤及分析

3.1 实验步骤

  • 在 flower 主机上启动 opt 服务器
$ ./opt -s -h flower
  • 在 sun 主机上启动 opt 客户端
$ ./opt -h flower
  • 杀死 flower 主机上的 opt 服务器(按 CTRL C)

  • 再次启动 flower 主机上的 opt 服务器

$ ./opt -s -h flower

正常情况下,我们会看到下面这一幕:


53-套接字选项(SO_REUSEADDR)_第1张图片
图1 服务器启动报错

3.2 指定 SO_REUSEADDR 的代码

这个功能的代码封装在了 reuseAddr 函数中,使用的时候加上 common.h 头文件即可:

void reuseAddr(int sockfd, int onoff) {
  int ret;
  ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &onoff, sizeof(onoff));
  if (ret < 0) {
    ERR_EXIT("reuseAddr");
  }
}

注意,一定要在 bind 监听套接字前设置此选项,否则会因为时序问题导致没有效果。

3.3 分析

从图 1 中我们看到,第二次启动服务器时 bind 函数出错,原因是地址已经被使用。然后我使用 netstat 命令查看连接,发现 192.168.166.47:8000 处于 TIME_WAIT 状态,192.168.166.47 是 flower 主机的 ip 地址,8000 是 opt 服务器启动时默认使用的端口。

我们知道,TIME_WAIT 状态一般持续 2MSL,在我的机器上约为 1 分钟左右。这也意味着,在这 1 分钟时间里,opt 服务器都无法启动。解决此问题的办法就是给服务器的监听套接字指定 SO_REUSEADDR 选项。

我们再来模拟一次,这次指定 SO_REUSEADDR 选项,只要给 opt 加上 --reuse 选项就行了。


53-套接字选项(SO_REUSEADDR)_第2张图片
图2 尽管旧的连接处于 TIME_WAIT,服务器仍然可以绑定

4. SO_REUSEADDR 的功能

上面的实验介绍的是 SO_REUSEADDR 的第一个功能,还有其它三个功能。这里将这四个功能简要总结:

  • 1) 允许绑定处于 TIME_WAIT 状态的套接字地址

  • 2) 允许在同一个端口上启动同一个服务器的多个实例,只要每个实例绑定的 ip 地址不同即可。

如果捆绑相同的 ip 地址和端口号,即使指定 SO_REUSEADDR 也没有用。我们将这种绑定相同 ip 地址和端口号称为 completely duplicate binding,即完全重复的捆绑,这在 TCP 协议中是不允许的。

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

  • 4) 针对 UDP 套接字,允许完全重复的捆绑(completely duplicate binding),即 ip 地址和端口号都重复。

对于这种情况,假设同一主机运行同一个应用程序的多个副本,当一个 UDP 数据报需要由这些重复捆绑套接字中的一个接收时,规则为:如果该数据报的目的地址是一个广播或多播地址,那就给每个匹配的套接字递送一个该数据报的副本;但是如果该数据报的目的地址是单播地址,那么它只递送给单个套接字。在单播数据报的情况下,如果有多个套接字匹配该数据报,那么该选择由哪个套接字接收它取决于系统实现。

后面,我们会具体讨论广播和多播。我们之前写的程序,都是单播。

5. 总结

  • 掌握 SO_REUSEADDR 的功能

UNP 推荐,所有的 TCP 服务器都应该指定这个套接字选项。所以我们编写服务器时,应当在一开始就把这个选项打开。

你可能感兴趣的:(linux,Linux,网络编程修炼指南-内功心法)