socket常见选项之SO_REUSEADDR,SO_REUSEPORT
目录
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地址捆绑
time-wait
TIME_WAIT状态有两个存在的理由:
- (1)可靠地实现TCP全双工连接的终止
- (2)允许老的重复分节在网络中消逝
如图所示,在主机A的4次挥手过程中,如果最后的数据丢失,则主机B会认为A未能收到自己发送的FIN消息,因此重传。这时,收到FIN消息的主机A将重启time-wait计时器。因此,如果网络状态不佳,time-wait状态将持续
(1)如果服务器最后发送的ACK因为某种原因丢失了,那么客户一定会重新发送FIN,这样因为有TIME_WAIT的存在,服务器会重新发送ACK给客户,如果没有TIME_WAIT,那么无论客户有没有收到ACK,服务器都已经关掉连接了,此时客户重新发送FIN,服务器将不会发送ACK,而是RST,从而使客户端报错。也就是说,TIME_WAIT有助于可靠地实现TCP全双工连接的终止。
(2)如果没有TIME_WAIT,我们可以在最后一个ACK还未到达客户的时候,就建立一个新的连接。那么此时,如果客户收到了这个ACK的话,就乱套了,必须保证这个ACK完全死掉之后,才能建立新的连接。也就是说,TIME_WAIT允许老的重复分节在网络中消逝。
回到我们的问题,由于我并不是正常地经过四次断开的方式中断连接,所以并不会存在最后一个ACK的问题。所以,这样是安全的。不过,最终的服务器版本,还是不要设置为端口可复用的
<?php
$address = '0.0.0.0';
$port = $argv[1] ?? 8071;
$listen = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (false === $listen) errhandle(__LINE__);
//ctrl+c重启时 能立马重启,要在bind,listen之前
if (true !== socket_set_option($listen, SOL_SOCKET, SO_REUSEADDR, 1)) errhandle(__LINE__);;
if (true !== socket_bind($listen, '0.0.0.0', $port)) errhandle(__LINE__);;
if (true !== socket_listen($listen, 5)) errhandle(__LINE__);; //待连接队列长度
//socket_set_nonblock($listen);
echo "Server linsten on:{$address}:$port" . PHP_EOL;
while (true) {
//连接socket,处理连接的接入
$sock_client = socket_accept($listen);
if (false === $sock_client) {
errhandle(__LINE__,false);
continue;
}
processClientConn($sock_client);
}
//处理已经连入的连接
function processClientConn($sock_client)
{
if (socket_getpeername($sock_client, $clinet_addr, $client_port)) {
echo "New client " . intval($sock_client) . " come from {$clinet_addr}:$client_port" . PHP_EOL;
sayWelcome($sock_client);
}
while (true) {
//接收到不少于len
$len = socket_recv($sock_client, $buf, 2048, 0);
if ($len === false) {
echo "no data" . PHP_EOL;
continue;
} elseif ($len === 0) {
errhandle(__LINE__,false);
socket_shutdown($sock_client);
break;
} else {
echo "recv:{" . $buf . "}len=" . $len . PHP_EOL;
if ($buf == 'quit') {
socket_shutdown($sock_client);
break;
}
}
}
}
function errhandle($line_num,$exit=true)
{
echo $line_num.":".socket_last_error() . ":" . socket_strerror(socket_last_error()) . PHP_EOL;
if($exit){
exit();
}
}
function sayWelcome($client)
{
$buf = date("H:i:s") . " welcome to server! you id:" . intval($client) . PHP_EOL;
socket_write($client, $buf, strlen($buf));
}
SO_REUSEPORT
目前常见的网络编程模型就是多进程或多线程,根据accpet的位置,分为如下场景
2种场景
- (1)单进程或线程创建socket,并进行listen和accept,接收到连接后创建进程和线程处理连接
- (2)单进程或线程创建socket,并进行listen,预先创建好多个工作进程或线程accept()在同一个服务器套接字
这两种模型解充分发挥了多核CPU的优势,虽然可以做到线程和CPU核绑定,但都会存在:
- 单一listener工作进程或线程在高速的连接接入处理时会成为瓶颈
- 多个线程之间竞争获取服务套接字
- 缓存行跳跃
- 很难做到CPU之间的负载均衡
- 随着核数的扩展,性能并没有随着提升
SO_REUSEPORT解决了什么问题
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,并且保证唤醒的均衡性。
https://www.cnblogs.com/Anker/p/7076537.html
http://www.blogjava.net/yongboy/archive/2015/02/12/422893.html
没用reuseport的
socket_fork_no_reuseport.php
socket常见选项之SO_REUSEADDR,SO_REUSEPORT的更多相关文章
- 套接字选项 之 SO_REUSEADDR && SO_REUSEPORT
说明 本文下面内容基本上是截取自stackoverflow,针对这两个选项,在另外一篇文章中做了总结,请移步<Linux TCP套接字选项 之 SO_REUSEADDR && S ...
- Linux TCP套接字选项 之 SO_REUSEADDR && SO_REUSEPORT
说明 前面从stackoverflow上找了一篇讲这两个选项的文章,文章内容很长,读到最后对Linux中的这两个选项还是有些迷茫,所以重新写一篇文章来做一个总结: 本文只总结TCP单播部分,并且只讨论 ...
- Java Socket常见异常处理 和 网络编程需要注意的问题
在java网络编程Socket通信中,通常会遇到以下异常情况: 第1个异常是 java.net.BindException:Address already in use: JVM_Bind. 该异常发 ...
- Socket常见错误代码与描述
最近程序 出现 几次 Socket 错误, 为了便于 差错.. 搜了一些 贴在这里.. 出现网络联机错误Socket error #11001表示您的计算机无法连上服务器,请检查您的Proxy设定以及 ...
- python socket 常见方法及 简单服务/客户端
socket 常见方法: 补充说明:what is file descriptor? 文件描述符是什么? 参考(http://stackoverflow.com/questions/8191905/w ...
- docker-创建容器常见选项
1. docker run创建容器常见选项 1.1 创建容器 选项 描述 -i,-interactive 交互式 -t,-tty 分配一个伪终端 -d,-detach 运行容器到后台 -e,-env ...
- UNIX网络编程——套接字选项(SO_REUSEADDR)
1.一般来说,一个端口释放后会等待两分钟之后才能再被使用,SO_REUSEADDR是让端口释放后立即就可以被再次使用. SO_REUSEADDR用于对TCP套接字处于TIME_WAIT状态下的sock ...
- Socket/ServerSocket 选项
在网络编程中,Socket/ServerSocket有一些选项用来自定义一些行为,现在分享一下. Socket选项 1.TCP_NODELAY 在Socket发送数据时,默认情况下,数据会先进 ...
- socket - option编程:SO_REUSEADDR
网友vmstat多次提出了这个问题:SO_REUSEADDR有什么用处和怎么使用.而且很多网友在编写网络程序时也会遇到这个问题.所以特意写了这么一篇文章,希望能够解答一些人的疑难. 其实这个问题在Ri ...
随机推荐
- 【计算机视觉】目标检测之ECCV2016 - SSD Single Shot MultiBox Detector
本文转载自: http://www.cnblogs.com/lillylin/p/6207292.html SSD论文阅读(Wei Liu--[ECCV2016]SSD Single Shot Mul ...
- Ubuntu18.04 安装MySQL
安装 #命令1 sudo apt-get update #命令2 sudo apt-get install mysql-server 初始化 sudo mysql_secure_installatio ...
- Java多态的6大特性|乐字节
大家好,我是乐字节的小乐,前几天讲完了Java继承,接下来我们会讲述Java多态. 以上就是本次学习的6大任务.我们依次来看. 一. Object类 Object类是所有Java类的根基类. 如果在类 ...
- LeetCode 665. 非递减数列(Non-decreasing Array)
665. 非递减数列 665. Non-decreasing Array 题目描述 给定一个长度为 n 的整数数组,你的任务是判断在最多改变 1 个元素的情况下,该数组能否变成一个非递减数列. 我们是 ...
- STM32之中断函数
本文做中断函数的索引,帮助我们找到中断函数名.中断函数参数以及中断服务函数他们的来源,以便我们编程. 1)如果一个工程只有一个中断,则我们可以进行两个步骤就可以了: 使能中断通道 编写中断服务函数 2 ...
- Python进阶:metaclass谈
metaclass 的超越变形特性有什么用? 来看yaml的实例: import yaml class Monster(yaml.YAMLObject): yaml_tag = u'!Monster' ...
- 07 UML类图
移步: https://www.cnblogs.com/coolstream/p/9572846.html
- flannel overlay网络浅析
Flannel基于UDP的网络实现 container-1的route表信息如下(b1): default via 100.96.1.1 dev eth0 100.96.1.0/24 dev eth0 ...
- 『Python基础』第7节:基本运算符
一. 基本运算符 运算按种类可以分为: 算数运算.比较运算.逻辑运算.赋值运算.成员运算.身份运算.位运算. 今天我们只学习算数运算.比较运算.逻辑运算.赋值运算.成员运算 1.1 算数运算 以下假设 ...
- 微软商店一直安装不上Intel Media SDK DFP
具体表现为一直安装失败,但是下载进度条一直在,无法去除. 此方法来自 https://answers.microsoft.com/en-us/windows/forum/all/error-code- ...