发新帖

php socket编程:使用socket_recv而不是socket_read

零下一度 2022-2-19 951

socket_recv和socket_read都可以用于读取socket数据,不过二者有差别,推荐使用socket_recv。

原因如下:

1.socket_recv支持多种flag,用于不同场景

2.socket_recv可以检测socket关闭的情况(例如对端关闭了socket)

返回值:$return_value=socket_recv(...)

含义: >0 表示接收到的字节数;

===0, 发生了错误,socket closed;

===false,无数据,socket not closed。

socket_read不能判断socket是否已经断开。

测试流程:

启动server端,再启动client端:可以正常通信。

kill掉client端,结果server端只能读取到空字符串。

测试代码如下:

file: bug1_server.php

<?php
/**
 * file: bug1_server.php
 * socket server
 * 基于php socket函数族
 * IO模型:同步阻塞
 * 粘包处理:固定长度
 * 连接数:1个socket连接
 *
 * 测试目标:模拟client crash时,server无法判断socket是否断开
 * 测试结果:kill杀掉client进程后,server进程socket_last_error()返回为0,无法判断socket是否关闭
 *
 * @author davidyanxw
 * @date 2018.04.27
 */
set_time_limit(0);
//创建服务端的socket套接流,net协议为IPv4,protocol协议为TCP
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
// reuse address
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
/*绑定接收的套接流主机和端口,与客户端相对应*/
if (socket_bind($socket, '127.0.0.1', 8801) == false) {
    echo 'server bind fail:' . socket_strerror(socket_last_error());
}
//监听套接流
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;
//让服务器不停获取客户端传过来的信息
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随机串
 * @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函数族
 * IO模型:同步阻塞
 * 粘包处理:固定长度
 * 连接数:1个socket连接
 *
 * @author davidyanxw
 * @date 2018.04.27
 */
set_time_limit(0);
//创建一个socket套接流
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
//接收套接流的最大超时时间(800ms)
//发送套接流的最大超时时间(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;
//连接服务端的套接流,这一步就是使客户端与服务器端的套接流建立联系
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);
        //向服务端写入字符串信息
        $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随机串
 * @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:bug1_client.php
 * socket client
 * 基于php socket函数族
 * IO模型:同步阻塞
 * 粘包处理:固定长度
 * 连接数:1个socket连接
 *
 * @author davidyanxw
 * @date 2018.04.27
 */
set_time_limit(0);
//创建一个socket套接流
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
//接收套接流的最大超时时间(800ms)
//发送套接流的最大超时时间(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;
//连接服务端的套接流,这一步就是使客户端与服务器端的套接流建立联系
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);
        //向服务端写入字符串信息
        $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随机串
 * @param $length
 * @return string
 */
function randomkeys($length){
    $output='';
    for ($a = 0; $a<$length; $a++) {
        $output .= chr(mt_rand(33, 126));
    }
    return $output;
}
?>

















最新回复 (0)
返回
零下一度
主题数
931
帖子数
0
注册排名
1