zoukankan      html  css  js  c++  java
  • Swoole从入门到入土(2)——TCP服务器[初步接触]

    我们知道Swoole弥补了PHP没办法实现长连接的短板,在接下来的话题中,我们会从TCP服务器、HTTP服务器、WebSocket服务器、协程、管道、中间件等话题,一个个进行讨论。

    1、开篇

    我们以Swoole一个最简单的例子作为开篇:

    //创建Server对象,监听 127.0.0.1:9501 端口
    $server = new SwooleServer('0.0.0.0', 9501);
    
    //监听连接进入事件
    $server->on('Connect', function ($server, $fd) {
        echo "Client: Connect.
    ";
    });
    
    //监听数据接收事件
    $server->on('Receive', function ($server, $fd, $from_id, $data) {
        $server->send($fd, "Server: " . $data);
    });
    
    //监听连接关闭事件
    $server->on('Close', function ($server, $fd) {
        echo "Client: Close.
    ";
    });
    
    //启动服务器
    $server->start(); 

    把这段代码,保存为文件swoole.php,接着我们用php命令运行:php swoole.php。这时我们就可以看到一个简单的TCP服务器在侦听中。

    代码的大致作用是:

    1) 服务端发现客户端有连接的时候,就会输出文字“Client:Connect”

    2) 一旦收到客户端的消息,就会向客户端发送“Server:消息原文”

    3) 连接断开时,输出文字“Client:Close”

    2、验证

    现在为了让大家更直观的看到连接情况,我们用SockeTool工具进行实验:

    1) php swoole.php启动服务端。

    2) SocketTool创建TCP客户端,填入服务端对应的IP和端口:

     3) 测试发送数据,并查看接收信息:

     3、相关函数详解

    1) 构造函数

    SwooleServer(string $host = '0.0.0.0', int $port = 0, int $mode = SWOOLE_PROCESS, int $sockType = SWOOLE_SOCK_TCP): SwooleServer

    $host:监听的IP地址,可支持IPV4也可支持IPV6,0.0.0.0为IPV4的所有地址,::(相当于0:0:0:0:0:0:0:0)为IPV6的所有地址。

    $port:监听的端口,如果端口小于1024则需要root权限。

    $mode:SWOOLE_PROCESS(默认,多进程模式),SWOOLE_BASE(基要模式)。

    $socketType:SWOOLE_SOCK_TCP(默认,IPV4 TCP)、SWOOLE_SOCK_TCP6、SWOOLE_SOCK_UDP、SWOOLE_SOCK_UDP6

    2) 函数on:调用事件

    SwooleServer->on(string $event, mixed $callback): void

    $event:事件名称,不区分大小写

    $callback:回调函数,各个事件的回调函数的参数格式详见事件说明。回调函数可以是函数名的字符串,类静态方法,对象方法数组,匿名函数。

    3) 函数start:启动服务器,监听所有指定的端口

    SwooleServer->start(): bool

    本函数没有参数,但有以下事项必须了解:

    · 启动成功后会创建 worker_num+2 个进程。Master 进程 +Manager 进程 +worker_num 个 Worker 进程。
    · 启动失败会立即返回 false
    · 启动成功后将进入事件循环,等待客户端连接请求。start 方法之后的代码不会执行
    · 服务器关闭后,start 函数返回 true,并继续向下执行
    · 设置了 task_worker_num 属生值会增加相应数量的 Task 进程
    · 方法列表中 start 之前的方法仅可在 start 调用前使用,在 start 之后的方法仅可在 onWorkerStart、onReceive 等事件回调函数中使用

    4) 函数send:向客户端发送数据

    SwooleServer->send(int $fd, string $data, int $serverSocket  = -1): bool

    $fd:客户端文件描述符,每个客户端分配一个描述符,可以理解为客户端的ID。

    $data:发送的数据,TCP 协议最大不得超过 2M,可修改 buffer_output_size 改变允许发送的最大包长度。

     $serverSocket:向 UnixSocket DGRAM 对端发送数据时需要此项参数,TCP 客户端不需要填写。

     5) 函数getClientInfo:获取连接信息

    SwooleServer->getClientInfo(int $fd, int $extraData, bool $ignoreError = false): bool|array

    $fd:客户端文件描述符,每个客户端分配一个描述符,可以理解为客户端的ID。

    $extraData:扩展信息,保留参数,目前无任何效果。

    $ignoreError:是否忽略错误,如果设置为 true,即使连接关闭也会返回连接的信息。

    注意:当使用 dispatch_mode = 1(轮循模式)或3(抢占模式) 配置时,考虑到这种数据包分发策略用于无状态服务,当连接断开后相关信息会直接从内存中删除,所以 Server->getClientInfo 是获取不到相关连接信息的。

    /***** 连接信息数据示例 *****/
    $fd_info = $server->getClientInfo($fd);
    var_dump($fd_info);
    
    array(7) {
      ["reactor_id"]=>
      int(3)
      ["server_fd"]=>
      int(14)
      ["server_port"]=>
      int(9501)
      ["remote_port"]=>
      int(19889)
      ["remote_ip"]=>
      string(9) "127.0.0.1"
      ["connect_time"]=>
      int(1390212495)
      ["last_time"]=>
      int(1390212760)
    }

    ------------    常识科普时间   ------------ 

    讨论到这里,我们先科普一下几个名字:

    Master 进程、Reactor 线程、Worker 进程、Task 进程、Manager 进程的区别与联系

    Master进程:php swoole.php启动的进程。

    Reactor线程:(注意)这是一个线程,是在Master进程中创建的线程,负责处理网络IO,收发数据,不执行任何PHP代码。

    Worker进程:接受由Reactor线程投递过来的数据,处理后生成响应数据,再交给Reactor线程。多进程模式运行。可以是异步非阻塞,也可以是同步阻塞。

    TaskWorker进程:接受由Worker进程投递的任务(通过SwooleServer->task/taskwait/taskCo/taskWaitMulti 方法投递),处理完成之后将结果返回给Worker进程(使用使用 SwooleServer->finish)。以多进程方式运行,完全是同步阻塞模式。

    Manager进程:负责创建 / 回收 worker/task 进程。

     ------------    常识科普时间结束   ------------ 

    4、相关事件详解

    1) 事件onConnect:有新的连接进入时,在 worker 进程中回调。

    function onConnect(SwooleServer $server, int $fd, int $reactorId);

    $server:服务器server对象。

    $fd:连接(客户端)文件描述符,可理解为标识客户端的ID,每个客户端分配一个唯一的描述符。

    $reactorId:所在的reactor线程的ID。

    注意:onConnect、onReceive和onClose三个事件有可能会并发执行,从而带来异常。

               当dispatch_mode为1(轮循模式)或3(抢占模式)时,数据包可能会被投递到不同的进程。连接相关的 PHP 对象数据,无法实现在 onConnect 回调初始化数据,onClose 清理数据。

    (:关于属性以及属性的设置下一篇会进行介绍:)

    2) 事件onReceive:接收到数据时回调此函数,发生在 worker 进程中。

    function onReceive(SwooleServer $server, int $fd, int $reactorId, string $data);

    $server:服务器server对象。

    $fd:连接(客户端)文件描述符,可理解为标识客户端的ID,每个客户端分配一个唯一的描述符。

    $reactorId:所在的reactor线程的ID。

    $data:收到的数据内容,可以是文本,也可以是二进制。

    注意:TCP数据包存在粘包问题,使用底层提供的 open_eof_check/open_length_check/open_http_protocol 等配置可以保证数据包的完整性

              不使用底层的协议处理,在 onReceive 后 PHP 代码中自行对数据分析,合并 / 拆分数据包。

    (:关于TCP粘包问题的处理,以后的章节会进行介绍:)

    3) 事件onClose:TCP 客户端连接关闭后,在 worker 进程中回调此函数。

    function onClose(SwooleServer $server, int $fd, int $reactorId);

    $server:服务器server对象。

    $fd:连接(客户端)文件描述符,可理解为标识客户端的ID,每个客户端分配一个唯一的描述符。

    $reactorId:所在的reactor线程的ID。当服务器主动关闭连接时,底层会设置此参数为 -1,可以通过判断 $reactorId < 0 来分辨关闭是由服务器端还是客户端发起的。只有在 PHP 代码中主动调用 close 方法被视为主动关闭。心跳检测是由心跳检测线程通知关闭的,关闭时 onClose 的 $reactorId 参数不为 -1。

    注意:

    -onClose 回调函数如果发生了致命错误,会导致连接泄漏。通过 netstat 命令会看到大量 CLOSE_WAIT 状态的 TCP 连接 。无论由客户端发起 close 还是服务器端主动调用 $server->close() 关闭连接,都会触发此事件。因此只要连接关闭,就一定会回调此函数

    -onClose 中依然可以调用 getClientInfo 方法获取到连接信息,在 onClose 回调函数执行完毕后才会调用 close 关闭 TCP 连接。这里回调 onClose 时表示客户端连接已经关闭,所以无需执行 $server->close($fd)。代码中执行 $server->close($fd) 会抛出 PHP 错误警告。

    (:这一节又有一个问题“心跳检测”,在以后的章节敬请期待啦:)

    对于TCP服务端的初步接触,这一节就先到这里啦。大家下周见:)

    ---------------------------  我是可爱的分割线  ----------------------------

    最后博主借地宣传一下,漳州编程小组招新了,这是一个面向漳州青少年信息学/软件设计的学习小组,有意向的同学点击链接,联系我吧。

  • 相关阅读:
    MySQL-[--001--]-MySQL涉及的算法题
    Python3-2020-测试开发-25- 操作数据库(MySQL)
    Python3-2020-测试开发-24- os模块及os.path
    Python3-接口自动化-7-读取Excel封装方法
    Charles-2020-抓取Https包
    Python3-2020-测试开发-23- 文件操作
    Django模板(Template)系统
    Django视图系统
    Django路由系统
    Django框架
  • 原文地址:https://www.cnblogs.com/ddcoder/p/13495317.html
Copyright © 2011-2022 走看看