• 技术文章 >后端开发 >php教程

    PHP+Socket系列之IO多路复用及实现web服务器

    藏色散人藏色散人2023-02-02 20:27:10转载85

    本篇文章给大家带来了关于php+socket的相关知识,其中主要介绍了IO多路复用,以及php+socket如何实现web服务器?感兴趣的朋友下面一起来看一下,希望对大家有帮助。

    php原生socket之IO多路复用以及实现web服务器

    多路复用

    前文 通过原生 socket 实现了简单的服务端与客户端通信,但当有多个客户端连接时,服务端仅能处理第一个客户端的请求,而无法对后续客户端服务

    服务端未正确处理截图

    产生这种情况的原因是因为IO模型是阻塞的,同一时刻只能由一个客户端进行访问,解决此问题主要有两种解决方案:

    同时多路复用又分为两个不同的模型,即 selectepoll,常见的软件中,Apache 使用了 select 模型,nginx 则使用 epoll 模型。在 php 中内置了 select 模型,对应的函数为 socket_select,多路复用是实现 http 服务器的基础

    语法

    在前文中我们介绍了 php 原生 socket 内置了 socket_select 函数实现了 select 模型,其语法如下:

    socket_select(
        array &$read,
        array &$write,
        array &$except,
        int $seconds [,
        int $microseconds = 0]): int|false

    参数

    优化

    我们在 上篇文章 简单实现了 socket 服务端监听与客户端的连接,接下来我们在服务端监听代码的基础上通过多路复用优化代码:

    <?php
    
    // 创建套接字
    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    
    // 设置 ip 被释放后立即可使用
    socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, true);
    
    // 绑定ip与端口
    socket_bind($socket, 0, 8888);
    
    // 开始监听
    socket_listen($socket);
    
    $sockets[] = $socket;
    
    while (true) {
        $tmp_sockets = $sockets;
        socket_select($tmp_sockets, $write, $except, null);
    
        foreach ($tmp_sockets as $sock) {
            // 如果当前套接字等于 socket_create 创建的套接字,说明是有新的连接或有新的断开连接
            if ($sock == $socket) {
                $conn_sock = socket_accept($socket);
                $sockets[] = $conn_sock;
                socket_getpeername($conn_sock, $ip, $port);
                echo '请求ip: ' . $ip . '端口: ' . $port . PHP_EOL;
            } else { // 否则说明是之前连接的客户端发来消息
                $msg = socket_read($sock, 10240);
                socket_write($sock, strtoupper($msg));
                echo $msg;
            }
        }
    }

    在本示例中 socket_select 函数会阻塞当前进程,当 $tmp_sockets 数组内的 socket 资源有新的客户端连接或断开或收到新消息时,会将 $tmp_sockets 数组修改为当前活跃的 socket 资源,随后通过遍历该数组处理业务逻辑

    优化结果截图

    使用socket实现简易http服务器

    http 协议是在 socket 的基础上规定了指定的数据格式,所以我们只需在 socket_write 时按照格式发送数据,浏览器就可正常响应请求

    <?php
    
    // 创建套接字
    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    
    // 设置 ip 被释放后立即可使用
    socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, true);
    
    // 绑定ip与端口
    socket_bind($socket, 0, 8888);
    
    // 开始监听
    socket_listen($socket);
    
    $sockets[] = $socket;
    
    while (true) {
        $tmp_sockets = $sockets;
        socket_select($tmp_sockets, $write, $except, null);
    
        foreach ($tmp_sockets as $sock) {
            if ($sock == $socket) {
                $conn_sock = socket_accept($socket);
                $sockets[] = $conn_sock;
            } else {
                $msg = socket_read($sock, 10240);
                var_dump($msg);
                if ($msg == '') return;
    
                $output = '<h1>this is php worker</h1>';
                $len = strlen($output);
    
                $response = "HTTP/1.1 200 OK\r\n";
                $response .= "content-type: text/html\r\n";
                $response .= "server: php socket\r\n";
                $response .= "Content-Length: {$len}\r\n\r\n";
    
                $response .= $output;
    
                socket_write($sock, $response);
            }
        }
    }

    在服务端运行此示例,随后在浏览器访问 ip:8888 ,可以看到如下:

    525569ef28e66ce2d2e0956b96f9d18.jpg

    同时服务端会输出如下内容:

    GET / HTTP/1.1
    Host: 124.222.**.**:8888
    Connection: keep-alive
    Cache-Control: max-age=0
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    Accept-Encoding: gzip, deflate
    Accept-Language: zh-CN,zh;q=0.9
    Cookie: jenkins-timestamper-offset=-28800000; _ga=GA1.1.1403944751.1652010033; _ga_2GM6102E19=GS1.1.1652802985.7.1.1652803014.0

    该内容即为用户端请求原始数据,可解析此数据并根据请求做出响应,比如使用 file_get_content 读取指定文件内容返回给浏览器

    推荐学习:《PHP视频教程

    以上就是PHP+Socket系列之IO多路复用及实现web服务器的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:learnku,如有侵犯,请联系admin@php.cn删除
    专题推荐:php socket
    上一篇:PHP+Socket系列之实现客户端与服务端数据传输 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • 浅析node中如何优雅使用Socket.IO模块• 教你用laravel-websockets搞个“低配”广播系统• PHP socket学习:带你做个简单的socket服务器• PHP+Socket系列之实现客户端与服务端数据传输
    1/1

    PHP中文网