您好,登錄后才能下訂單哦!
這篇文章主要講解了“php socket編程中客戶端異常關(guān)閉導(dǎo)致服務(wù)端中斷的問題怎么解決”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“php socket編程中客戶端異常關(guān)閉導(dǎo)致服務(wù)端中斷的問題怎么解決”吧!
原因如下:
1.socket_recv支持多種flag,用于不同場景
2.socket_recv可以檢測socket關(guān)閉的情況(例如對端關(guān)閉了socket)
返回值:$return_value=socket_recv(...)
含義: >0 表示接收到的字節(jié)數(shù);
===0, 發(fā)生了錯誤,socket closed;
===false,無數(shù)據(jù),socket not closed。
socket_read不能判斷socket是否已經(jīng)斷開。
測試流程:
啟動server端,再啟動client端:可以正常通信。
kill掉client端,結(jié)果server端只能讀取到空字符串。
測試代碼如下:
file: bug1_server.php
<?php /** * file: bug1_server.php * socket server * 基于php socket函數(shù)族 * IO模型:同步阻塞 * 粘包處理:固定長度 * 連接數(shù):1個socket連接 * * 測試目標(biāo):模擬client crash時,server無法判斷socket是否斷開 * 測試結(jié)果:kill殺掉client進(jìn)程后,server進(jìn)程socket_last_error()返回為0,無法判斷socket是否關(guān)閉 * * @author davidyanxw * @date 2018.04.27 */ set_time_limit(0); //創(chuàng)建服務(wù)端的socket套接流,net協(xié)議為IPv4,protocol協(xié)議為TCP $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // reuse address socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1); /*綁定接收的套接流主機(jī)和端口,與客戶端相對應(yīng)*/ if (socket_bind($socket, '127.0.0.1', 8801) == false) { echo 'server bind fail:' . socket_strerror(socket_last_error()); } //監(jiān)聽套接流 if (socket_listen($socket, 4) == false) { echo 'server listen fail:' . socket_strerror(socket_last_error()); } $accept_resource = socket_accept($socket); if($accept_resource === false) { echo "accept connection failed".PHP_EOL; exit; } // 讀寫超時時間:0.8s socket_set_option($accept_resource, SOL_SOCKET, SO_RCVTIMEO, array("sec" => 0, "usec" => 800000)); socket_set_option($accept_resource, SOL_SOCKET, SO_SNDTIMEO, array("sec" => 0, "usec" => 800000)); // stream固定長度 $len = 100; //讓服務(wù)器不停獲取客戶端傳過來的信息 while (true) { $string_read = socket_read($accept_resource, $len); if($string_read === false) { echo "socket error:" . socket_last_error() . ",error msg:" . socket_strerror(socket_last_error()) . PHP_EOL; break; } elseif($string_read == '') { if(in_array(socket_last_error(), [SOCKET_EPIPE, SOCKET_ECONNRESET])) { echo "socket error:".socket_last_error().",error msg:".socket_strerror(socket_last_error()).PHP_EOL; break; } if(in_array(socket_last_error(), [SOCKET_EAGAIN])) { // EAGAIN, retry later usleep(500); continue; } echo "server receive empty:" . socket_last_error() . ",error msg:" . socket_strerror(socket_last_error()) . PHP_EOL; } else { $string = trim($string_read); echo 'server receive success,msg:['.$string.'],time:' . microtime(true) . PHP_EOL; } } ; // 先shutdown,后close @socket_shutdown($accept_resource); socket_close($accept_resource); @socket_shutdown($socket); socket_close($socket); /** * 生成php隨機(jī)串 * @param $length * @return string */ function randomkeys($length){ $output=''; for ($a = 0; $a<$length; $a++) { $output .= chr(mt_rand(33, 126)); } return $output; } ?>
file:bug1_client.php
<?php /** * file:bug1_client.php * socket client * 基于php socket函數(shù)族 * IO模型:同步阻塞 * 粘包處理:固定長度 * 連接數(shù):1個socket連接 * * @author davidyanxw * @date 2018.04.27 */ set_time_limit(0); //創(chuàng)建一個socket套接流 $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); //接收套接流的最大超時時間(800ms) //發(fā)送套接流的最大超時時間(800ms) socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec" => 0, "usec" => 800000)); socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array("sec" => 0, "usec" => 800000)); $len = 100; //連接服務(wù)端的套接流,這一步就是使客戶端與服務(wù)器端的套接流建立聯(lián)系 if (socket_connect($socket, '127.0.0.1', 8801) == false) { echo 'connect fail massege:' . socket_strerror(socket_last_error()); } else { while(1){ $ori_msg = 'Hello, server!'.randomkeys(8); $message_write = str_pad($ori_msg, $len); //向服務(wù)端寫入字符串信息 $sent = @socket_write($socket, $message_write, $len); if ($sent === false) { if(in_array(socket_last_error(), [SOCKET_EPIPE, SOCKET_ECONNRESET])) { echo "socket error:".socket_last_error().",error msg:".socket_strerror(socket_last_error()).PHP_EOL; break; } echo "socket error:".socket_last_error().",error msg:".socket_strerror(socket_last_error()).PHP_EOL; } else{ echo 'client write success,msg:['.$ori_msg.'],time:' . microtime(true).PHP_EOL; } // break; } } @socket_shutdown($socket); socket_close($socket); /** * 生成php隨機(jī)串 * @param $length * @return string */ function randomkeys($length){ $output=''; for ($a = 0; $a<$length; $a++) { $output .= chr(mt_rand(33, 126)); } return $output; } ?>
正確的代碼是:(file: debug1_server.php)
<?php /** * file: debug1_server.php * socket server * 基于php socket函數(shù)族 * IO模型:同步阻塞 * 粘包處理:固定長度 * 連接數(shù):1個socket連接 * * 測試目標(biāo):模擬client crash時,server無法判斷socket是否斷開 * 測試結(jié)果:kill殺掉client進(jìn)程后,server進(jìn)程socket_last_error()返回為0,無法判斷socket是否關(guān)閉 * * @author davidyanxw * @date 2018.04.27 */ set_time_limit(0); //創(chuàng)建服務(wù)端的socket套接流,net協(xié)議為IPv4,protocol協(xié)議為TCP $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // reuse address socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1); /*綁定接收的套接流主機(jī)和端口,與客戶端相對應(yīng)*/ if (socket_bind($socket, '127.0.0.1', 8801) == false) { echo 'server bind fail:' . socket_strerror(socket_last_error()); } //監(jiān)聽套接流 if (socket_listen($socket, 4) == false) { echo 'server listen fail:' . socket_strerror(socket_last_error()); } $accept_resource = socket_accept($socket); if($accept_resource === false) { echo "accept connection failed".PHP_EOL; exit; } // 讀寫超時時間:0.8s socket_set_option($accept_resource, SOL_SOCKET, SO_RCVTIMEO, array("sec" => 0, "usec" => 800000)); socket_set_option($accept_resource, SOL_SOCKET, SO_SNDTIMEO, array("sec" => 0, "usec" => 800000)); // stream固定長度 $len = 100; //讓服務(wù)器不停獲取客戶端傳過來的信息 while (true) { /* 使用socket_recv */ $len_read = socket_recv($accept_resource, $string_read, $len, 0); if ($len_read === false) { // no data echo "no data".PHP_EOL; continue; } elseif($len_read === 0 ) { // socket closed echo "socket error:" . socket_last_error() . ",error msg:" . socket_strerror(socket_last_error()) . PHP_EOL; break; } else { $string = trim($string_read); echo 'server receive success,msg:['.$string.'],time:' . microtime(true) . PHP_EOL; } } ; // 先shutdown,后close @socket_shutdown($accept_resource); socket_close($accept_resource); @socket_shutdown($socket); socket_close($socket); /** * 生成php隨機(jī)串 * @param $length * @return string */ function randomkeys($length){ $output=''; for ($a = 0; $a<$length; $a++) { $output .= chr(mt_rand(33, 126)); } return $output; } ?>
感謝各位的閱讀,以上就是“php socket編程中客戶端異常關(guān)閉導(dǎo)致服務(wù)端中斷的問題怎么解決”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對php socket編程中客戶端異常關(guān)閉導(dǎo)致服務(wù)端中斷的問題怎么解決這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。