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

1、开篇

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

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

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

$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:调用事件

Swoole\Server->on(string $event, mixed $callback): void

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

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

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

Swoole\Server->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:向客户端发送数据

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

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

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

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

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

Swoole\Server->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进程投递的任务(通过Swoole\Server->task/taskwait/taskCo/taskWaitMulti 方法投递),处理完成之后将结果返回给Worker进程(使用使用 Swoole\Server->finish)。以多进程方式运行,完全是同步阻塞模式。

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

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

4、相关事件详解

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

function onConnect(Swoole\Server $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(Swoole\Server $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(Swoole\Server $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服务端的初步接触,这一节就先到这里啦。大家下周见:)

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

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

Swoole从入门到入土(2)——TCP服务器[初步接触]的更多相关文章

  1. 【Swoole】简单安装与创建TCP服务器

    pecl install swoole PHP的异步.并行.高性能网络通信引擎,使用纯C语言编写,提供了php语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,异步Redis,数据 ...

  2. php的异步非阻塞swoole模块使用(一)实现简易tcp服务器--服务端

    绑定tcp服务器的地址 $swserver = new swoole_server("127.0.0.1",9501); 设置tcp服务器装机容量(太危言耸听了-其实就是设置属性) ...

  3. HTTP从入门到入土(3)——TCP三次握手

    TCP三次握手 客户端与服务器之间互相发送HTTP请求响应之前需要先进行TCP连接,因为HTTP是一个无连接.无状态协议,不存在连接的概念,只有请求和响应的概念.而请求和响应实际上只是数据包,他们需要 ...

  4. php的异步非阻塞swoole模块使用(一)实现简易tcp服务器--客户端

    //实例化一个swoole客户端 $swclient = new swoole_client(SWOOLE_SOCK_TCP); //建立连接---如果连接无效则退出 )){ echo "连 ...

  5. Swoole学习(七)Swoole之异步TCP服务器的创建

    环境:Centos6.4,PHP环境:PHP7 <?php //创建TCP服务器 /** * $host 是swoole需要监听的ip,如果要监听本地,不对外服务,那么就是127.0.0.1;如 ...

  6. Swoole学习(二)Swoole之TCP服务器的创建

    环境:Centos6.4,PHP环境:PHP7 <?php //创建TCP服务器 /** * $host 是swoole需要监听的ip,如果要监听本地,不对外服务,那么就是127.0.0.1;如 ...

  7. swoole 创建tcp服务器

    server.php <?php /** * 创建tcp服务器 * Date: 2019/1/15 */ $serv = new swoole_server('127.0.0.1', 9501) ...

  8. Swoole系列(三):建立TCP服务器并发送数据测试

    <?php // 建立tcp服务器下 $host = '0.0.0.0'; $port = 9501; $serv = new swoole_server($host,$port); $serv ...

  9. 18-ESP8266 SDK开发基础入门篇--TCP 服务器 RTOS版,串口透传,TCP客户端控制LED

    https://www.cnblogs.com/yangfengwu/p/11112015.html 先规定一下协议 aa 55 02 01 F1 4C 控制LED点亮  F1 4C为CRC高位和低位 ...

  10. 17-ESP8266 SDK开发基础入门篇--TCP服务器 RTOS版,小试牛刀

    https://www.cnblogs.com/yangfengwu/p/11105466.html 现在开始写... lwip即可以用socket 的API  也可以用 netconn  的API实 ...

随机推荐

  1. [转帖]一篇来自网络的关于“enqueue”events的简短参考

    https://www.cnblogs.com/lhdz_bj/p/8716701.html 仅供自己和各位同学参考: Enqueue Type Description enq: AD - alloc ...

  2. [转帖]TiKV & TiFlash 加速复杂业务查询

    https://tidb.net/book/tidb-monthly/2022/2022-07/usercase/tikv-tiflash 背景​ 在互联网公司或传统公司的 CRM 系统中,最常用的功 ...

  3. [转帖]10--k8s之数据持久化

    https://www.cnblogs.com/caodan01/p/15136217.html 目录 一.emptDir 二.hostPath 三.pv 和 pvc 1.环境准备 2.创建pv 3. ...

  4. [转帖]Redis核心技术与实战

    https://www.cnblogs.com/strick/p/14851429.html 最近在读一篇关于Redis的专栏,叫做<Redis核心技术与实战>,作者在Redis方面研究颇 ...

  5. [转帖]写给想了解"集成电路"的朋友

    https://zhuanlan.zhihu.com/p/602627000 寒假和朋友小聚,每当就专业问题展开谈话,很容易形成"一边热火朝天,一边大脑宕机"的局面.俗话说隔行如隔 ...

  6. 谈JVM参数GC线程数ParallelGCThreads合理性设置

    作者:京东零售 刘乐 导读:本篇文章聚焦JVM参数GC线程数的合理配置,从ParallelGCThreads参数含义.参数设置,到参数实验以及修改意见进行解析. 1. ParallelGCThread ...

  7. 解决input标签自动填充内容的问题

    autocomplete="new-password" 巧妙解决input标签自动填充问题 如果是同域名网站,并且曾经在该网站下登录过账号密码,并且选择了记住账号密码. chrom ...

  8. 跟着文档学Fabric:获取通道配置

    原文在这里. 1. 获取通道配置 peer channel fetch config config_block.pb -o $ORDERER_CONTAINER -c $CH_NAME --tls - ...

  9. TienChin 创建菜单页面

    上一节当中我们只是给后台添加了对应的菜单,实际上对应的页面还没有存在这节主要就是创建出来页面: 促销活动: activity 统计分析: analysis 商机管理: business 渠道管理: c ...

  10. 强化学习从基础到进阶-案例与实践[4.1]:深度Q网络-DQN项目实战CartPole-v0

    强化学习从基础到进阶-案例与实践[4.1]:深度Q网络-DQN项目实战CartPole-v0 1.定义算法 相比于Q learning,DQN本质上是为了适应更为复杂的环境,并且经过不断的改良迭代,到 ...