我们知道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. [转帖]聊聊TPS、QPS、CPS概念和区别

    https://cloud.tencent.com/developer/article/1859053 TPS 概念 TPS:是TransactionsPerSecond的缩写,也就是事务数/秒.它是 ...

  2. [转帖]nginx源码层面探究request_time、upstream_response_time、upstream_connect_time与upstream_header_time指标具体含义与区别

    https://www.cnblogs.com/AcAc-t/p/nginx_request_time_upstream_respone_time_analysis.html 背景概述 最近计划着重分 ...

  3. [转帖]TiFlash 简介

    overview TiFlash 是 TiDB HTAP 形态的关键组件,它是 TiKV 的列存扩展,在提供了良好的隔离性的同时,也兼顾了强一致性.列存副本通过 Raft Learner 协议异步复制 ...

  4. [转帖]LVS入门篇(五)之LVS+Keepalived实战

    LVS入门篇(五)之LVS+Keepalived实战 https://www.cnblogs.com/linuxk/p/9365189.html 一.实验架构和环境说明 (1)本次基于VMware W ...

  5. [转帖]Tcpdump抓包命令

    tcpdump和ethereal可以用来获取和分析网络通讯活动,他们都是使用libpcap库来捕获网络封包的. ​在混杂模式下他们可以监控网络适配器的所有通讯活动并捕获网卡所接收的所有帧. ​要想设置 ...

  6. [转帖]Linux中最全shell命令&常用注意事项

    在编辑linux脚本中不断的会用到复杂的脚本编辑,写文章记录. Linux命令大全(手册) – 真正好用的Linux命令在线查询网站 目录 推荐书籍: Linux常用命令 Linux 常用命令 在li ...

  7. [转帖]Intel甘拜下风,挤牙膏比不过兆芯CPU

      https://baijiahao.baidu.com/s?id=1735997557665412214 本文比较长,有万字左右,因此在前面先把小标题集中亮个相. 即使大家一晃而过,我也要让精心拟 ...

  8. web字体小于12px的解决办法

    大家都知道,web端的字体在正常情况下,最小只能够是12px; 但是有些时候,可能需要字体小于12px 那么如何解决这个办法了 可以使用css3的缩放属性scale 如果字体的大小是10px; 那么我 ...

  9. vs版本与version(内部版本号)的关系表table

    为什么要查vs版本与内部verion的对应关系? 因为c++的项目需要对应的vs版本,给大型的c++项目升级vs版本是个耗时的工程,所以一般情况下开发者都会安装多个版本的vs. 对于sln文件,以文本 ...

  10. 使用emmylua调试slua

    使用emmylua调试slua的方法 在lua的入口中加上emmylua的debug代码 在slua的LuaState.loaderDelegate委托中,添加判断如果是load_file_name= ...