通常來講,一個端口釋放後會等待兩分鐘以後才能再被使用,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