socket 选项
1、SO_REUSEADDR

一般来说,一个端口释放后会等待两分钟之后才能再被使用,SO_REUSEADDR 是让端口释放后立即就可以被再次使用。

SO_REUSEADDR 用于对 TCP 套接字处于 TIME_WAIT 状态下的 socket,才可以重复绑定使用。

server 程序总是应该在调用 bind() 之前设置 SO_REUSEADDR 套接字选项 TCP,先调用 close() 的一方会进入 TIME_WAIT 状态。

SO_REUSEADDR 提供如下四个功能:

允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将此端口用做他们的本地端口的连接仍存在。这通常是重启监听服务器时出现,若不设置此选项,则 bind 时将出错。
允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地 IP 地址即可。对于 TCP,我们根本不可能启动捆绑相同 IP 地址和相同端口号的多个服务器。
允许单个进程捆绑同一端口到多个套接口上,只要每个捆绑指定不同的本地 IP 地址即可,这一般不用于 TCP 服务器。
SO_REUSEADDR 允许完全重复的捆绑:

当一个 IP 地址和端口绑定到某个套接口上时,还允许此 IP 地址和端口捆绑到另一个套接口上。一般来说,这个特性仅在支持多播的系统上才有,而且只对 UDP 套接口而言(TCP 不支持多播)。
SO_REUSEPORT 选项有如下语义:

此选项允许完全重复捆绑,但仅在想捆绑相同 IP 地址和端口的套接口都指定了此套接口选项才行。

如果被捆绑的 IP 地址是一个多播地址,则 SO_REUSEADDR 和 SO_REUSEPORT 等效。

使用这两个套接口选项的建议:

在所有 TCP 服务器中,在调用 bind 之前设置 SO_REUSEADDR 套接口选项;
当编写一个同一时刻在同一主机上可运行多次的多播应用程序时,设置 SO_REUSEADDR 选项,并将本组的多播地址作为本地 IP 地址捆绑。
2、SO_REUSEPORT

目前常见的网络编程模型就是多进程或多线程,根据accpet的位置,分为如下场景2种场景

单进程或线程创建 socket,并进行 listen 和 accept,接收到连接后创建进程和线程处理连接
单进程或线程创建 socket,并进行 listen,预先创建好多个工作进程或线程 accept() 在同一个服务器套接字
这两种模型解充分发挥了多核 CPU 的优势,虽然可以做到线程和 CPU 核绑定,但都会存在:

单一 listener 工作进程或线程在高速的连接接入处理时会成为瓶颈
多个线程之间竞争获取服务套接字
缓存行跳跃
很难做到 CPU 之间的负载均衡
随着核数的扩展,性能并没有随着提升
SO_REUSEPORT 支持多个进程或者线程绑定到同一端口,提高服务器程序的性能,解决的问题:

允许多个套接字 bind()/listen() 同一个 TCP/UDP 端口
每一个线程拥有自己的服务器套接字
在服务器套接字上没有了锁的竞争
内核层面实现负载均衡
安全层面,监听同一个端口的套接字只能位于同一个用户下面
其核心的实现主要有三点:

扩展 socket option,增加 SO_REUSEPORT 选项,用来设置 reuseport
修改 bind 系统调用实现,以便支持可以绑定到相同的 IP 和端口
修改处理新建连接的实现,查找 listener 的时候,能够支持在监听相同 IP 和端口的多个 sock 之间均衡选择
有了 SO_RESUEPORT 后,每个进程可以自己创建 socket、bind、listen、accept 相同的地址和端口,各自是独立平等的
让多进程监听同一个端口,各个进程中 accept socket fd 不一样,有新连接建立时,内核只会唤醒一个进程来 accept,并且保证唤醒的均衡性