理论:
SO_REUSEADDR:
主要应用场所:服务器主动断开连接后会出现TIME_WAIT状态,时间长达2MSL,此时的ADDRESS(IP + PORT)仍然生效,服务器重启后默认绑定原先的ADDRESS(IP + PORT),会出现address already in use 的错误。如果启用SO_REUSEADDR就能解决这个问题。
SO_REUSEPORT:
端口可以重复调用,自动创建线程(类似多线程)。
实例讲解:
这是一个正常的TcpServer。
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
一、验证SO_REUSEADDR功能:
1.正常的TcpServer启动后关闭状态,会有时间长达2MSL的TIME_WAIT,此时重启服务器会出现bind error:
使用ss命令查看端口状态:
ss -antp | grep 80
注意:第二条信息为系统自动连接,可忽略。
修改代码如下:
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
int one = 1;
setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &one,sizeof(one));
启动服务器后关闭,再次启动发现可以启动服务器。用ss 命令查看状态会发现没有TIME_WAIT,如下图:
结论:SO_REUSEADDR可以解决服务器主动断开连接后时间长达2MSL的TIME_WAIT状态。
二、验证SO_REUSEPORT功能:
两个服务器绑定同一ADDRESS(IP + PORT):出现bind error:
修改代码如下:
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
int one = 1;
setsockopt(serv_sock, SOL_SOCKET, SO_REUSEPORT, &one,sizeof(one));
运行结果:发现成功。
三、验证SO_REUSEADDR和SO_REUSEPORT 是否存在功能交叉:
1.如果换成SO_REUSEADDR 能否让两个服务器绑定同一ADDRESS(IP + PORT)。
修改代码如下:
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
int one = 1;
setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &one,sizeof(one));
if (serv_sock == -1) {
error_handling("socket() error");
}
运行结果:
答案:不能成功。
原因:SO_REUSEADDR 只能解决在TIME_WAIT状态下的ADDRESS(IP + PORT)重用。
关闭服务器后,如图:
说明:SO_REUSEPORT不能结束TIME_WAIT状态。
结论:两个功能不存在交叉功能。
一些好奇的想法
Q1:在仅仅开启SO_REUSEPORT权限时,两个服务器占用同一ADDRESS,如果客户端连接这个ADDRESS,会发生什么情况?哪个服务器在提供服务?
1.开启两个服务器,使用一个客户端进行连接。
连接结果如下:
通过进程PID可以发现,最新占用端口的服务器连接了客户端,那么之前的服务器能否用到呢?
2.创建多个客户端尝试:
通过关闭第一次客户端后,原先与客户端1连接的server2进入TIME_WAIT状态,所以客户端第二次连接就连接到了server1.
发现我写的服务器只能服务一个客户端,显然不符合我的最初疑问,于是换成可以接受多个客户端的服务器,首先加上SO_REUSEADDR和SO_REUSEPORT权限:
int option = 1;
setsockopt(httpd, SOL_SOCKET, SO_REUSEPORT|SO_REUSEADDR, (void *)&option, optlen);
使用四个客户端进行连接,结果如下图:
可以发现,两个服务器轮流进行提供服务,估计是哪个环节进行了负载均衡处理,但是我的代码里并没有写负载均衡,鉴于笔者知识有限,可以挖一下坑,后续填一下。或者知道的小伙伴留一下言!
总结
SO_REUSEADDR主要用来解决TIME_WAIT,SO_REUSEPORT解决端口可以重复调用问题,两者没有交集和冲突可以根据实际情况进行自由选择。
中间有不严谨的地方还请大家指出!