分享workerman自定义协议解决粘包拆包问题的方法

workerman自定义的协议如何解决粘包拆包?下面本篇文章给大家介绍一下workerman自定义协议解决粘包拆包问题的方法,希望对大家有所帮助。

分享workerman自定义协议解决粘包拆包问题的方法

前言:

        由于最近在使用 workerman 实现 Unity3D 联机游戏的服务端,虽然也可以通过 TCP 协议直接通信,但是在实际测试的过程中发现了一些小问题。【相关推荐:《workerman教程》】

        比如双方的数据包都是字符串的方式吗,还有就因为是字符串就需要切割,而有时候在客户端或服务端接收时都会出现报错。经过打印日志发现,两端接收到的包都有出现不是事先约定好的格式,这也就是 TCP 的粘包拆包现象。这个的解决方法很简单,网上也有很多,但是这里是想用自己实现的协议解决,暂且放到后面来说。

问题解答:

        关于网游的通信数据包格式的约定,我在网上也看过一些。如果不是用弱类型语言做服务端脚本,其实别人常用的是字节数组。但是 PHP 在接收到字节数组时,其实就是字符串,但前提时该字节数组没有一些特定转换的。就拿 C# 来说,在解决粘包等问题会在字节数组前加入字节长度 (BitConverter.GetBytes (len))。但是这个传递到 PHP 服务端接收时,字符串前 4 个字节就是显示不出来,用过很多方法进行转换都取不出来。 后来也想过用 Protobuf 数据方式,虽然 PHP 可以对数据可以转换,但是客户端 C# 我还不太熟就放弃了。

        还一个问题是,其实别人做网游服务端实现帧同步大部分都是 UDP 协议,同时也有 TCP 和 UDP 共用。但是如果只是小型多人在线游戏,用 PHP 做服务端,TCP 协议通信也完全可以的。接下来就回到 workerman 的自定义协议和粘包拆包问题吧。

自定义协议:

        workerman 对 PHP 的几个 socket 函数进行了封装 (关于 socket 函数,如果愿意折腾,php 也可以写一个文件传输的小工具的),基于 TCP 之上也自带了几个应用层协议,比如 Http, Websocket, Frame 等。也预留了用户自行定义协议的路口,只需要实现他的 ProtocolInterface 接口,以下就简单介绍以下接口需要实现的几个方法。

1.  Input 方法

        在这个方法里,可以在服务端接收前对数据包进行解包,检查包长度,过滤等。返回 0 就将数据包放入接收端的缓冲内继续等待,返回指定长度则表示取出缓冲区内长度。如果异常也可以返回 false 直接关闭该客户端连接。

2. encode 方法

         该方法是服务端在发送数据包到客户端前,对数据包格式的处理,也就是封包,这个就要前后端约定好了。

3. decode 方法

        这个方法也就是解包,就是从缓冲区里取出指定长度到 onMessage 接收前要进行处理的地方,比如进行逻辑调配等等。

粘包拆包产生现象:

        由于 TCP 是基于流的,且因为是传输层,在上层的应用通过 socket 套接字 (理解为接口) 通信时,他不知道传递过来的数据包开头结尾在哪。只是根据 TCP 的一套拥塞算法机型粘合或拆解的发送。所以从字面上看,粘包就是几个数据包一起发送,原本应该是两个包,客户端只收到了一个包。而拆包是将一个数据包拆成了几个包,本应该是接收一个数据包,却只收到了一个。所以如果不解决这个,前面提到了按约定字符串传输,就可能解包时报错的情况。

包阅AI 包阅AI

论文对照翻译,改写润色,专业术语详解,选题评估,开题报告分析,评审校对,一站式解决论文烦恼!

包阅AI 84 查看详情 包阅AI

粘包拆包解决方法:

1. 首部加数据包长度

<?php/** * This file is part of game. * * Licensed under The MIT License * For full copyright and license information, please see the MIT-LICENSE.txt * Redistributions of files must retain the above copyright notice. * * @author    beiqiaosu * @link      http://www.zerofc.cn */namespace WorkermanProtocols;use WorkermanConnectionTcpConnection;/** * Frame Protocol. */class Game{    /**     * Check the integrity of the package.     *     * @param string        $buffer     * @param TcpConnection $connection     * @return int     */    public static function input($buffer, TcpConnection $connection)    {        // 数据包前4个字节        $bodyLen = intval(substr($buffer, 0 , 4));        $totalLen = strlen($buffer);        if ($totalLen < 4) {            return 0;        }        if ($bodyLen  strlen(substr($buffer, 4))) {            return 0;        }        return $bodyLen + 4;    }    /**     * Decode.     *     * @param string $buffer     * @return string     */    public static function decode($buffer)    {        return substr($buffer, 4);    }    /**     * Encode.     *     * @param string $buffer     * @return string     */    public static function encode($buffer)    {        // 对数据包长度向左补零        $bodyLen = strlen($buffer);        $headerStr = str_pad($bodyLen, 4, 0, STR_PAD_LEFT);        return $headerStr . $buffer;    }}

2. 特定字符分割

maxPackageSize) && strlen($buffer) >= $connection->maxPackageSize) {            $connection->close();            return 0;        }                $pos = strpos($buffer, "#");                if ($pos === false) {            return 0;        }                // 返回当前包长        return $pos + 1;    }    /**     * Encode.     *     * @param string $buffer     * @return string     */    public static function encode($buffer)    {        return $buffer . "#";    }    /**     * Decode.     *     * @param string $buffer     * @return string     */    public static function decode($buffer)    {        return rtrim($buffer, "#");    }}

粘包拆包测试:

        这里就只演示特定字符串分割的解决方法,因为上面首页 4 字节加包长的还是存在问题。就是第一次发送不带包长,后面模拟粘包还是拆包都会停留在缓冲区,下面演示可以参照上面代码查看。

1. 服务开启和客户端连接

分享workerman自定义协议解决粘包拆包问题的方法

分享workerman自定义协议解决粘包拆包问题的方法

2. 服务业务端代码

        数据包格式说明一下,字符串以逗号分割,数据包以 #分割,逗号分割第一组是业务方法,如 Login 表示登陆传递,Pos 表示坐标传递,后面带的就是对应方法需要的参数了。

count = 4;$worker->onWorkerStart = function ($connection) {    echo "游戏协议服务启动……";};// Emitted when new connection come$worker->onConnect = function ($connection) {    echo "New Connectionn";    $connection->send("address: " . $connection->getRemoteIp() . " " . $connection->getRemotePort());};// Emitted when data received$worker->onMessage = function ($connection, $data) use ($worker, $stream) {    echo "接收的数据:" . $data . "n";    // 简单实现接口分发    $arr = explode(",", $data);    if (!is_array($arr) || !count($arr)) {        $connection->close("数据格式错误", true);    }    $func = strtoupper($arr[0]);    $client = $connection->getRemoteAddress();    switch($func) {        case "LOGIN":            $sendData = "Login1";            break;        case "POS":            $positionX = $arr[1] ?? 0;            $positionY = $arr[2] ?? 0;            $positionZ = $arr[3] ?? 0;            $sendData = "POS,$client,$positionX,$positionY,$positionZ";            break;    }    $connection->send($sendData);};// Emitted when connection is closed$worker->onClose = function ($connection) {    echo "Connection closedn";};// 接收缓冲区溢出回调$worker->onBufferFull = function ($connection) {    echo "清理缓冲区吧";};Worker::runAll();?>

3. 粘包测试

    只需要在客户端模拟两个数据包连在一起,但是要以 #分隔,看看服务端接收的时候是一几个包进行处理的。

分享workerman自定义协议解决粘包拆包问题的方法

4. 拆包测试

        拆包模拟只需要将一个数据包分成两次发送,看看服务端接收的时候能不能显示或者说能不能按约定好的格式正确显示。

分享workerman自定义协议解决粘包拆包问题的方法

更多编程相关知识,请访问:编程教学!!

以上就是分享workerman自定义协议解决粘包拆包问题的方法的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/339379.html

赞 (0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
如何解决MailChimpAPI集成难题?Composer助你轻松搞定!
上一篇 2025年11月5日 17:38:31
Linux如何解压文件
下一篇 2025年11月5日 17:38:44

相关推荐

  • 基于Workerman开发高可用的航空订票系统

    基于Workerman开发高可用的航空订票系统 近年来,随着航空业的快速发展,航空订票系统的重要性与日俱增。一个高效、高可用的航空订票系统可以提供便捷、安全的预订服务,满足旅客和航空公司的需求。 在这篇文章中,我们将介绍如何利用Workerman框架开发一个高可用的航空订票系统。Workerman是…

    2026年9月22日
    100
  • 基于Workerman实现高并发的在线支付系统

    基于Workerman实现高并发的在线支付系统 引言:如今,随着互联网的快速发展,线上支付已经成为大众生活中不可或缺的一部分。为了满足用户日益增长的在线支付需求,开发一款高并发的在线支付系统是至关重要的。本文将介绍如何基于Workerman框架来实现一个高并发的在线支付系统,并提供相应代码示例。 一…

    2026年9月21日
    200
  • 使用Workerman构建高性能的游戏排行榜服务

    使用Workerman构建高性能的游戏排行榜服务 在当今的游戏世界中,游戏排行榜服务是非常重要的功能之一。游戏排行榜不仅能够给玩家提供一个公平竞争的平台,还能够增加游戏的可玩性和互动性。然而,构建一个高性能的游戏排行榜服务并非易事。本文将介绍如何使用Workerman这个高性能PHP框架来构建一个游…

    2026年9月21日
    100
  • Workerman服务启动失败的排查步骤

    workerman服务启动失败的排查步骤如下:1. 检查配置文件,确保无语法错误;2. 查看系统日志,寻找错误线索;3. 检查端口占用情况,确保端口未被占用;4. 调整文件权限,确保workerman有足够权限;5. 检查php环境,确保版本兼容且扩展已安装。 关于Workerman服务启动失败的排…

    2026年9月21日
    200
  • Workerman与WebAssembly(Wasm)的交互实践

    workerman和wasm结合使用是为了在高性能服务器环境中引入wasm的沙箱化和跨平台能力,实现更灵活、安全和高效的服务端应用。1) wasm模块的编译与加载:使用编译工具链将wasm模块编译成二进制文件并在workerman中加载。2) wasm模块的调用:通过php扩展或外部程序(如exec…

    2026年9月20日
    000
  • Docker容器化部署Workerman

    使用docker容器化workerman可以提高部署效率和资源利用率。1. 创建dockerfile,定义镜像构建过程。2. 编写workerman工作脚本。3. 使用docker网络功能配置外部访问。4. 通过docker的健康检查和重启策略管理进程。5. 优化性能,调整workerman进程数和…

    2026年9月20日
    000
  • 在Workerman中使用Composer依赖库

    在workerman中可以使用composer依赖库来扩展应用功能,但需要考虑异步编程特性。1. 创建composer.json文件并指定所需库,如monolog。2. 运行composer install命令安装库。3. 在worker进程中初始化和使用库,如monolog记录日志。4. 评估库的…

    2026年9月20日
    000
  • Workerman怎么处理业务逻辑?Workerman中间件使用方法?

    Workerman通过事件回调处理业务逻辑,中间件则用于优雅地拦截和处理请求。在请求处理流程中,中间件按注册顺序依次执行,形成处理链条,每个中间件可对请求进行验证、记录日志或拦截,例如权限验证未通过时直接返回401响应,阻止后续执行。中间件通过请求对象传递数据,如在前置中间件中注入用户信息,后续中间…

    2026年9月11日
    100
  • Workerman怎么进行性能监控?Workerman监控面板使用?

    Workerman性能监控可通过自带监控页面、Prometheus+Grafana等工具实现,重点关注CPU、内存、请求时间等指标,结合状态页面和第三方方案可有效分析性能与排查内存泄漏。 Workerman的性能监控,简单来说,可以通过一些工具和方法来实时了解你的应用运行状态,比如CPU占用、内存使…

    2026年9月11日
    200
  • Workerman如何监控状态?Workerman状态查看命令?

    Workerman的运行状态可通过自带的status命令查看,执行php your_start_file.php status可获取各Worker进程的连接数、内存占用、请求量等关键指标,是运维服务的核心工具。 Workerman的核心运行状态,我们通常是通过它自带的 status 命令来快速了解的…

    2026年9月11日
    000
  • Workerman如何监控性能?Workerman性能指标查看?

    要监控%ignore_a_1%性能,需结合系统工具、内置status命令和专业监控系统。首先通过top、htop查看CPU和内存使用,free -h检查内存,netstat分析连接状态;重点关注TIME_WAIT等异常。利用php your_start.php status获取各子进程PID、连接数…

    2026年9月11日
    200
  • Workerman怎么进行资源隔离?Workerman资源限制设置?

    Workerman通过多进程模型实现操作系统级资源隔离,每个Worker进程独立运行,互不影响,主进程可监控并重启异常进程以保障服务稳定。为防止资源滥用,可通过PHP的memory_limit限制单个进程内存,结合Worker::$max_request参数定期重启进程以释放内存;CPU资源则依赖操…

    2026年9月11日
    100
  • Workerman怎么进行代码调试?Workerman断点调试技巧?

    Workerman调试需结合日志、变量输出和Xdebug断点。日志可用Worker::log()或重定向输出;多进程调试建议设$worker->count=1或结合xdebug_break()与PID条件触发;推荐辅以Monolog日志体系、单元测试、服务监控和代码审查提升效率。 Worker…

    2026年9月11日
    100
  • Workerman怎么进行连接池管理?Workerman数据库连接池?

    Workerman通过每个Worker进程在启动时建立并复用单一数据库连接,利用进程隔离实现连接持久化,避免频繁创建销毁带来的性能损耗与数据库压力。该模式在onWorkerStart中初始化连接,存储于进程全局变量供后续请求复用,从而提升性能。为应对连接断开,推荐采用惰性重连策略:执行SQL失败后判…

    2026年9月11日
    300
  • Workerman如何实现服务降级?Workerman容错处理机制?

    Workerman通过超时控制、限流、熔断、多进程隔离、异步非阻塞I/O、消息队列解耦及异常捕获等机制实现服务降级与容错,核心在于提前应对依赖不稳定和高并发压力。 Workerman实现服务降级,核心在于在业务逻辑中植入各种“安全网”,例如熔断、限流和超时控制,以应对外部依赖服务不可用或自身负载过高…

    2026年9月11日
    200
  • Workerman支持哪些操作系统?WorkermanWindows能用吗?

    Workerman在Linux下性能最优,支持epoll、多进程和信号处理,适合高并发生产环境;Windows虽可运行,但使用select机制,性能受限,主要用于开发调试。 Workerman主要设计用于Linux环境,但在Windows上也能运行,不过功能和性能会受到限制。简单来说,Linux是它…

    2026年9月11日
    200
  • Workerman怎么进行单元测试?Workerman测试用例编写?

    Workerman单元测试需解耦业务逻辑与框架,通过模拟TcpConnection、Worker等组件,利用PHPUnit进行独立测试,解决持久化状态、异步事件和网络I/O带来的挑战,确保测试的高效与可维护性。 Workerman进行单元测试,核心在于将其业务逻辑与框架本身解耦,并利用PHPUnit…

    2026年9月11日
    100
  • Workerman如何记录日志?Workerman日志文件位置?

    Workerman日志通过Worker::$logFile配置,建议明确指定路径并确保写入权限,避免默认/tmp目录;应用日志应使用error_log或Monolog等专业库分离记录;需通过logrotate实现日志轮转,防止文件过大,生产环境推荐结合Monolog与集中式日志系统提升管理效率。 W…

    2026年9月11日
    200
  • Workerman怎么进行链路追踪?Workerman分布式追踪?

    链路追踪需为请求分配唯一Trace ID并跨服务传递,Workerman因长连接特性需通过自定义协议或上下文管理传递ID,可选用SkyWalking等现成库或手动实现,结合采样与异步上报降低性能影响。 链路追踪,简单来说,就是搞清楚一个请求在你的分布式系统里都经历了哪些服务,每个服务花了多少时间。对…

    2026年9月10日
    100
  • Swoole的onWorkerError事件在什么情况下触发

    onWorkerError事件在Swoole工作进程发生致命错误时触发,用于捕获PHP致命错误、内存耗尽、段错误等导致进程异常终止的情况。1. 触发条件包括PHP致命错误(如调用未定义函数)、内存超限崩溃、C层代码引发的段错误、子进程非正常退出及协程中未被捕获的致命异常。2. 使用场景主要为记录日志…

    2026年9月9日
    100

发表回复

登录后才能评论
关注微信