函数:setsockopt

  • SO_REUSEADDR:地址复用
  • SO_REUSEPORT:端口复用

一般来说,一个{addr,port}只能被一个套接字绑定,即无法重用。
不同的套接字只能绑定到不同的{addr,port}上!

示例

  1. // sockfd_one, sockfd_two都要设置端口复用
  2. // 在sockfd_one绑定bind之前,设置其端口复用
  3. int opt = 1;
  4. setsockopt( sockfd_one, SOL_SOCKET,SO_REUSEADDR, (const void *)&opt, sizeof(opt) );
  5. err_log = bind(sockfd_one, (struct sockaddr*)&my_addr, sizeof(my_addr));
  6. // 在sockfd_two绑定bind之前,设置其端口复用
  7. opt = 1;
  8. setsockopt( sockfd_two, SOL_SOCKET,SO_REUSEADDR,(const void *)&opt, sizeof(opt) );
  9. err_log = bind(sockfd_two, (struct sockaddr*)&my_addr, sizeof(my_addr));

SO_REUSEADDR

功能如下:

  • 若监听服务器进入TIME_WAIT状态,可立即重启
  • 同一端口启动同一服务器的多个实例,需要每个实例套接字绑定不同的ip地址,一般需要多个网卡支持
  • 支持完全重复的捆绑:

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

对于监听线程来说,可重用套接字被称为监听桶(listener bucket),即每个套接字都是一个桶。
以event模型为例,假设目前有3个子进程,每个子进程中都有一个监听线程和多个工作线程。

  • 端口未重用情况

地址/端口复用技术 - 图1
在某一时刻,该监听套接字仅能由某一进程持有,当该进程接收到请求后,才让出监听权。

  • 端口重用
    • 2个监听桶

地址/端口复用技术 - 图2

  • 3个监听桶

地址/端口复用技术 - 图3
3个监听桶下,各子进程均不用让出监听权,可以无限监听。

似乎看上去非常美好,性能好。不仅减轻了“监听权”(互斥锁)的争用,避免了“饥饿”;还能更高效的监听,实现负载均衡,从而减轻监听线程的压力。
但由于监听过程需要消耗CPU,若是单核CPU,无法体现出端口复用的优势,反而会由于切换监听线程而降低性能。
故若要使用端口复用,需要考虑如下:

  • 是否将监听进程/线程隔离在各自CPU中
  • 重用次数
  • CPU核数