swoole入门教程05-Swoole的自定义协议功能的使用

环境说明: 系统:Ubuntu14.04 (安装教程包括CentOS6.5)

PHP版本:PHP-5.5.10

swoole版本:1.7.8-alpha


1.为什么要提供自定义协议

熟悉TCP通信的朋友都会知道,TCP是一个流式协议。客户端向服务器发送的一段数据,可能并不会被服务器一次就完整的收到;客户端向服务器发送的多段数据,可能服务器一次就收到了全部的数据。而实际应用中,我们希望在服务器端能一次接收一段完整的数据,不多也不少。传统的TCP服务器中,往往需要由程序员维护一个缓存区,先将读到的数据放进缓存区中,然后再通过预先设定好的协议内容,来区分一段完整数据的开头、长度和结尾,并将一段完整的数据交给逻辑部分处理。这就是自定义协议的功能。

而在Swoole中,已经在底层实现了一个数据缓存区,并内置好了几个常用的协议类型,直接在底层做好了数据的拆分,保证了在onReceive回调函数中,一定能收到一个(或数个)完整的数据段。数据缓存区的大小可以通过配置选项package_max_length来控制。下面我就将讲解如何使用这些内置协议。

2.EOF标记型协议

第一个比较常用的协议就是EOF标记协议。协议的内容是通过规定一个一定不会出现在正常数据中的字符或者字符串,用这个来标记一段完整数据的结尾。这样,只要发现这个结尾,就可以认定之前的数据已经结束,可以开始接收一个新的数据段了。

在Swoole中,可以通过open_eof_checkpackage_eof两个配置项来开启。其中,open_eof_check指定开启了EOF检测,package_eof指定了具体的EOF标记。通过这两个选项,Swoole底层就会自动根据EOF标记来缓存和拆分收到的数据包。示例如下:

$this->serv->set(array(
    ‘package_max_length‘ => 8192,
    ‘open_eof_check‘=> true,
    ‘package_eof‘ => "\r\n"
));

就这样,swoole就已经开启了EOF标记协议的解析。那么让我们来测试一下效果:

服务器这边:

// Server
public function onReceive( swoole_server $serv, $fd, $from_id, $data ) {
    echo "Get Message From Client {$fd}:{$data}\n";
}

客户端这边:

$msg_eof = "This is a Msg\r\n";

$i = 0;
while( $i < 100 ) {
    $this->client->send( $msg_eof );
    $i ++;
}

然后运行一下,你会发现:哎不对啊,为什么还是一次收到了好多数据啊!

这是因为,在Swoole中,采用的不是遍历识别的方法,而只是简单的检查每一次接收到的数据的末尾是不是定义好的EOF标记。因此,在开启EOF检测后,onReceive回调中还是可能会一次收到多个数据包。

这要怎么办?你会发现,虽然是多个数据包,但是实际上收到的是N个完整的数据片段,那就只需要根据EOF把每个包再拆出来,一个个处理就好啦。

修改后的服务器端代码如下:

public function onReceive( swoole_server $serv, $fd, $from_id, $data ) {
    $data_list = explode("\r\n", $data);
    foreach ($data_list as $msg) {
        if( !empty($msg) ) {
            echo "Get Message From Client {$fd}:{$msg}\n";
        }

    }
}

再次运行,妥了~

点此查看完整实例

另外,如果担心这样运行多段数据会长时间占用Worker,可以采用把数据+fd转发给Task进程的做法。如何转发请读者自己尝试实现。

3.固定包头类型协议

固定包头协议是在实际应用中最常用的协议。该协议的内容是规定一个固定长度的包头,在包头的固定位置有一个指定好的字段存放了后续数据的实际长度。这样,服务器端可以先读取固定长度的数据,从中提取出长度,然后再读取指定长度的数据,即可获取一段完整的数据。

在Swoole中,同样提供了固定包头的协议格式。需要注意的是,Swoole只允许二进制形式的包头,因此,需要使用pack、unpack来打包、解包。

通过设置open_length_check选项,即可打开固定包头协议解析功能。此外还有package_length_offsetpackage_body_offsetpackage_length_type三个配置项用于控制解析功能。package_length_offset规定了包头中第几个字节开始是长度字段,package_body_offset规定了包头的长度,package_length_type规定了长度字段的类型。

具体设置如下:

$this->serv->set(array(
    ‘package_max_length‘ => 8192,
    ‘open_length_check‘=> true,
    ‘package_length_offset‘ => 0,
    ‘package_body_offset‘ => 4,
    ‘package_length_type‘ => ‘N‘
));

具体如何设置这些参数请参考文档

OK,废话不多讲,直接上实例:

服务器端:

public function onReceive( swoole_server $serv, $fd, $from_id, $data ) {
    $length = unpack("N" , $data)[1];
    echo "Length = {$length}\n";
    $msg = substr($data,-$length);
    echo "Get Message From Client {$fd}:{$msg}\n";
}

客户端:

$msg_length = pack("N" , strlen($msg_normal) ). $msg_normal;

$i = 0;
while( $i < 100 ) {
    $this->client->send( $msg_length );
    $i ++;
}

直接运行,Perfect!

点此查看完整实例

点此查看其他相关源码

4.特别篇:Http协议-Swoole内置的http_server

从Swoole-1.7.7-stable开始,Swoole在内部封装并实现了一个Http服务器。是的,没错,再也不用在PHP层缓存和解析http协议了,Swoole直接内置Http服务器了。

创建一个swoole_http_server的代码如下:

$http = new swoole_http_server("127.0.0.1", 9501);
$http->on(‘request‘, function (swoole_http_request $request, swoole_http_response $response) {
    $response->end("<h1>Hello Swoole.</h1>");
});
$http->start();

只需创建一个swoole_http_server对象并设置onRequest回调函数,即可实现一个http服务器。

在onRequest回调中,共有两个参数。参数$request存放了来自客户端的请求,包括Http请求的头部信息、Http请求相关的服务器信息、Http请求的GET和POST参数以及HTTP请求携带的COOKIE信息。参数$response用于发送数据给客户端,可以通过该参数设置HTTP响应的Header信息、cookie信息和状态码。

此外,swoole_http_server还提供WebSocket功能,使用此功能需要设置onMessage回调函数,如下:

$http_server->on(‘message‘, function(swoole_http_request $request, swoole_http_response $response) {
    echo $request->message;
    $response->message(json_encode(array("data1", "data2")));
})

通过$request->message获取WebSocket发送来的消息,再通过$response->message()回复消息即可。

点此查看完整实例

点此查看swoole_http_server相关文档

(最后做个小广告,经过尝试,已经初步将php的Yaf框架移植到了swoole_http_server上,经过测试,swoole-yaf的性能远远超过了nginx+php-fpm+yaf的性能。

项目地址:https://github.com/LinkedDestiny/swoole-yaf

我将继续不断完善这个项目,力争能够真正用于线上项目)

时间: 2024-10-29 19:10:58

swoole入门教程05-Swoole的自定义协议功能的使用的相关文章

Angular系列----AngularJS入门教程05:双向绑定(转载)

在这一步你会增加一个让用户控制手机列表显示顺序的特性.动态排序可以这样实现,添加一个新的模型属性,把它和迭代器集成起来,然后让数据绑定完成剩下的事情. 请重置工作目录: git checkout -f step-4 你应该发现除了搜索框之外,你的应用多了一个下来菜单,它可以允许控制电话排列的顺序. 步骤3和步骤4之间最重要的不同在下面列出.你可以在GitHub里看到完整的差别. 模板 app/index.html Search: <input ng-model="query"&g

(译)Windsor入门教程---第五部分 添加日志功能

介绍 现在我们已经有了基础的框架了,是时候添加内容了,那么我们首先应该考虑的就是在应用程序中添加日志功能.我们会使用Windsor来配置,在这一部分,你将学习Windsor之外的其他功能. Logging Facility 在上一部分说过,Windsor有很多自带的可选的功能组件,他们扩展了Windsor的即用性.在这一部分,我们将在程序中添加日志功能. Logging Facility提供了一些常用的日志框架比如Log4net.Nlog,以及mvc内置的日志框架Trace.Logging Fa

swoole入门教程01-环境搭建及扩展安装

写在前面的废话 <swoole源码分析>已经写了13章,整个swoole的核心架构基本都分析的差不多了.于是心里一直以来想整理swoole的文档并写一份教程的想法就再度浮了出来.实话说,我接触swoole乃至接触PHP都仅有9个月的时间,而自7月份以来一直在公司做Android开发,也有没有了使用swoole的机会.所以,现在我只能写出一份入门级教程,帮助刚刚接触swoole的人理解和使用swoole写一些简单的例子,从而初步掌握-swoole的用法. Git地址:https://github

【CC2530入门教程-05】CC2530的串行接口原理与应用

第5课  CC2530的串行接口原理与应用 广东职业技术学院  欧浩源 一.并行通信与串行通信 微控制器与外设之间的数据通信,根据连线结构和传送方式的不同,可以分为两种:并行通信和串行通信. 并行通信:指数据的各位同时发送或接收,每个数据位使用单独的一条导线.传输速度快.效率高,但需要的数据线较多,成本高. 串行通信:指数据一位接一位地顺 序发送或接收.需要的数据线少,成本低,但传输速度慢,效率低. 二.CC2530的串口通信模块 CC2530有两个串行通信接口USART0和USART1,它们能

esri-leaflet入门教程(3)-自定义底图

by 李远祥 在前面的篇章我们已经了解到了怎么样使用FlatUI.leaflet和esri-leaflet 去搭建简单的地图应用界面了.如果有着手写代码的朋友可能已经发现第一个Hello World 程序,使用的就是esri 的 Topographic 样式底图.底图这东西从来都是个好东西,放在互联网领域就是一个免费而好用的资源,而在专业GIS领域,它就是一个权威和标准,所有的地图和数据资源都必须叠加在底图之上. 那么,问题来了.esri-leaflet提供的是自家的底图,其服务器放在美国,而且

Node.js入门教程——如何实现文件上传功能

作者:zhanhailiang 日期:2014-11-16 本文将介绍如何使用Node.js实现文件上传功能. 1. 初始化项目信息:npm init [root@~/wade/nodejs/nodejs-upload-image-demo]# npm init This utility will walk you through creating a package.json file. It only covers the most common items, and tries to gu

Android基础入门教程——7.6.2 基于TCP协议的Socket通信(1)

Android基础入门教程--7.6.2 基于TCP协议的Socket通信(1) 标签(空格分隔): Android基础入门教程 本节引言: 上一节的概念课枯燥无味是吧,不过总有点收获是吧,本节开始我们来研究基于TCP协议的Socket 通信,先来了解下Socket的概念,以及Socket通信的模型,实现Socket的步骤,以及作为Socket服务 端与客户端的两位各做要做什么事情!好的,我们由浅入深来扣这个Socket吧! 1.什么是Socket? 2.Socket通信模型: Socket通信

Netty 5 自定义协议 教程

网上好多连接好多demo都不是netty5的,都是以前的版本,况且还有好多运行时老报错.入门级demo就不写了,估计都是那些老套路.好多公司都会有最佳实践,今天就说说如何自定义协议,一般自定义协议都是公司内部各个部门定义的,当然了我写的比较简单. 注意: 本教程是采用netty-all-5.0.0.Alpha2.jar,netty5的版本,不是网上很多的例子都是采用以前老大版本. 自定义协议: 协议 {   协议头(header)   消息体(body) } header格式 {   固定头,

BootStrap入门教程 (一) :手脚架Scaffolding(全局样式(Global Style),格网系统(Grid System),流式格网(Fluid grid System),自定义(Customing),布局(Layouts))

2011年,twitter的“一小撮”工程师为了提高他们内部的分析和管理能力,用业余时间为他们的产品构建了一套易用.优雅.灵活.可扩展的前端工具集--BootStrap.Bootstrap由MARK OTTO和Jacob Thornton所设计和建立,在github上开源之后,迅速成为该站上最多人watch&fork的项目.大量工程师踊跃为该项目贡献代码,社区惊人地活跃,代码版本进化非常快速,官方文档质量极其高(可以说是优雅),同时涌现了许多基于Bootstrap建设的网站:界面清新.简洁;要素