通常来讲,一个端口释放后会等待两分钟以后才能再被使用,SO_REUSEADDR是让端口释放后当即就能够被再次使用php
SO_REUSEADDR
用于对TCP套接字处于TIME_WAIT状态下的socket,才能够重复绑定使用
server程序老是应该在调用bind()以前设置SO_REUSEADDR套接字选项
TCP,先调用close()的一方会进入TIME_WAIT状态html
此选项容许彻底重复捆绑,但仅在想捆绑相同IP地址和端口的套接口都指定了此套接口选项才行。java
若是被捆绑的IP地址是一个多播地址,则SO_REUSEADDR和SO_REUSEPORT等效。git
使用这两个套接口选项的建议:编程
如图所示,在主机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)); }
目前常见的网络编程模型就是多进程或多线程,根据accpet的位置,分为以下场景
2种场景多线程
这两种模型解充分发挥了多核CPU的优点,虽然能够作到线程和CPU核绑定,但都会存在:
SO_REUSEPORT支持多个进程或者线程绑定到同一端口,提升服务器程序的性能,解决的问题:
http://www.javashuo.com/article/p-rnpyfyiz-hm.html
http://www.blogjava.net/yongboy/archive/2015/02/12/422893.html
没用reuseport的
socket_fork_no_reuseport.php