国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

workerman 自定義的協(xié)議如何解決粘包拆包

這篇具有很好參考價(jià)值的文章主要介紹了workerman 自定義的協(xié)議如何解決粘包拆包。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

前言:

? ? ? ? 由于最近在使用 workerman 實(shí)現(xiàn) Unity3D 聯(lián)機(jī)游戲的服務(wù)端,雖然也可以通過(guò) TCP 協(xié)議直接通信,但是在實(shí)際測(cè)試的過(guò)程中發(fā)現(xiàn)了一些小問(wèn)題。

? ? ? ? 比如雙方的數(shù)據(jù)包都是字符串的方式嗎,還有就因?yàn)槭亲址托枰懈?,而有時(shí)候在客戶端或服務(wù)端接收時(shí)都會(huì)出現(xiàn)報(bào)錯(cuò)。經(jīng)過(guò)打印日志發(fā)現(xiàn),兩端接收到的包都有出現(xiàn)不是事先約定好的格式,這也就是 TCP 的粘包拆包現(xiàn)象。這個(gè)的解決方法很簡(jiǎn)單,網(wǎng)上也有很多,但是這里是想用自己實(shí)現(xiàn)的協(xié)議解決,暫且放到后面來(lái)說(shuō)。

問(wèn)題解答:

? ? ? ? 關(guān)于網(wǎng)游的通信數(shù)據(jù)包格式的約定,我在網(wǎng)上也看過(guò)一些。如果不是用弱類型語(yǔ)言做服務(wù)端腳本,其實(shí)別人常用的是字節(jié)數(shù)組。但是 PHP 在接收到字節(jié)數(shù)組時(shí),其實(shí)就是字符串,但前提時(shí)該字節(jié)數(shù)組沒(méi)有一些特定轉(zhuǎn)換的。就拿 C# 來(lái)說(shuō),在解決粘包等問(wèn)題會(huì)在字節(jié)數(shù)組前加入字節(jié)長(zhǎng)度 (BitConverter.GetBytes (len))。但是這個(gè)傳遞到 PHP 服務(wù)端接收時(shí),字符串前 4 個(gè)字節(jié)就是顯示不出來(lái),用過(guò)很多方法進(jìn)行轉(zhuǎn)換都取不出來(lái)。 后來(lái)也想過(guò)用 Protobuf 數(shù)據(jù)方式,雖然 PHP 可以對(duì)數(shù)據(jù)可以轉(zhuǎn)換,但是客戶端 C# 我還不太熟就放棄了。

? ? ? ? 還一個(gè)問(wèn)題是,其實(shí)別人做網(wǎng)游服務(wù)端實(shí)現(xiàn)幀同步大部分都是 UDP 協(xié)議,同時(shí)也有 TCP 和 UDP 共用。但是如果只是小型多人在線游戲,用 PHP 做服務(wù)端,TCP 協(xié)議通信也完全可以的。接下來(lái)就回到 workerman 的自定義協(xié)議和粘包拆包問(wèn)題吧。

自定義協(xié)議:

? ? ? ? workerman 對(duì) PHP 的幾個(gè) socket 函數(shù)進(jìn)行了封裝 (關(guān)于 socket 函數(shù),如果愿意折騰,php 也可以寫一個(gè)文件傳輸?shù)男」ぞ叩?,基于 TCP 之上也自帶了幾個(gè)應(yīng)用層協(xié)議,比如 Http, Websocket, Frame 等。也預(yù)留了用戶自行定義協(xié)議的路口,只需要實(shí)現(xiàn)他的 ProtocolInterface 接口,以下就簡(jiǎn)單介紹以下接口需要實(shí)現(xiàn)的幾個(gè)方法。

1.? Input 方法

? ? ? ? 在這個(gè)方法里,可以在服務(wù)端接收前對(duì)數(shù)據(jù)包進(jìn)行解包,檢查包長(zhǎng)度,過(guò)濾等。返回 0 就將數(shù)據(jù)包放入接收端的緩沖內(nèi)繼續(xù)等待,返回指定長(zhǎng)度則表示取出緩沖區(qū)內(nèi)長(zhǎng)度。如果異常也可以返回 false 直接關(guān)閉該客戶端連接。

2.?encode 方法

? ? ? ? ?該方法是服務(wù)端在發(fā)送數(shù)據(jù)包到客戶端前,對(duì)數(shù)據(jù)包格式的處理,也就是封包,這個(gè)就要前后端約定好了。

3.?decode 方法

? ? ? ? 這個(gè)方法也就是解包,就是從緩沖區(qū)里取出指定長(zhǎng)度到 onMessage 接收前要進(jìn)行處理的地方,比如進(jìn)行邏輯調(diào)配等等。

粘包拆包產(chǎn)生現(xiàn)象:

? ? ? ? 由于 TCP 是基于流的,且因?yàn)槭莻鬏攲樱谏蠈拥膽?yīng)用通過(guò) socket 套接字 (理解為接口) 通信時(shí),他不知道傳遞過(guò)來(lái)的數(shù)據(jù)包開(kāi)頭結(jié)尾在哪。只是根據(jù) TCP 的一套擁塞算法機(jī)型粘合或拆解的發(fā)送。所以從字面上看,粘包就是幾個(gè)數(shù)據(jù)包一起發(fā)送,原本應(yīng)該是兩個(gè)包,客戶端只收到了一個(gè)包。而拆包是將一個(gè)數(shù)據(jù)包拆成了幾個(gè)包,本應(yīng)該是接收一個(gè)數(shù)據(jù)包,卻只收到了一個(gè)。所以如果不解決這個(gè),前面提到了按約定字符串傳輸,就可能解包時(shí)報(bào)錯(cuò)的情況。

粘包拆包解決方法:

1. 首部加數(shù)據(jù)包長(zhǎng)度

<?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 Workerman\Protocols;

use Workerman\Connection\TcpConnection;

/**
 * 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)
    {
        // 數(shù)據(jù)包前4個(gè)字節(jié)
        $bodyLen = intval(substr($buffer, 0 , 4));
        $totalLen = strlen($buffer);

        if ($totalLen < 4) {
            return 0;
        }

        if ($bodyLen <= 0) {
            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)
    {
        // 對(duì)數(shù)據(jù)包長(zhǎng)度向左補(bǔ)零
        $bodyLen = strlen($buffer);
        $headerStr = str_pad($bodyLen, 4, 0, STR_PAD_LEFT);

        return $headerStr . $buffer;
    }
}

2. 特定字符分割

<?php

namespace Workerman\Protocols;

use Workerman\Connection\ConnectionInterface;

/**
 * Text Protocol.
 */
class Tank
{
    /**
     * Check the integrity of the package.
     *
     * @param string        $buffer
     * @param ConnectionInterface $connection
     * @return int
     */
    public static function input($buffer, ConnectionInterface $connection)
    {
        
        if (isset($connection->maxPackageSize) && \strlen($buffer) >= $connection->maxPackageSize) {
            $connection->close();
            return 0;
        }
        
        $pos = \strpos($buffer, "#");
        
        if ($pos === false) {
            return 0;
        }
        
        // 返回當(dāng)前包長(zhǎng)
        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, "#");
    }
}

粘包拆包測(cè)試:

? ? ? ? 這里就只演示特定字符串分割的解決方法,因?yàn)樯厦媸醉?yè) 4 字節(jié)加包長(zhǎng)的還是存在問(wèn)題。就是第一次發(fā)送不帶包長(zhǎng),后面模擬粘包還是拆包都會(huì)停留在緩沖區(qū),下面演示可以參照上面代碼查看。

1. 服務(wù)開(kāi)啟和客戶端連接

workerman 自定義的協(xié)議如何解決粘包拆包

workerman 自定義的協(xié)議如何解決粘包拆包

2. 服務(wù)業(yè)務(wù)端代碼

? ? ? ? 數(shù)據(jù)包格式說(shuō)明一下,字符串以逗號(hào)分割,數(shù)據(jù)包以 #分割,逗號(hào)分割第一組是業(yè)務(wù)方法,如 Login 表示登陸傳遞,Pos 表示坐標(biāo)傳遞,后面帶的就是對(duì)應(yīng)方法需要的參數(shù)了。

<?php

use Workerman\Worker;

require_once __DIR__ . '/vendor/autoload.php';

// #### create socket and listen 1234 port ####
$worker = new Worker('tank://0.0.0.0:1234');

// 4 processes
//$worker->count = 4;

$worker->onWorkerStart = function ($connection) {
    echo "游戲協(xié)議服務(wù)啟動(dòng)……";
};

// Emitted when new connection come
$worker->onConnect = function ($connection) {
    echo "New Connection\n";
    $connection->send("address: " . $connection->getRemoteIp() . " " . $connection->getRemotePort());
};

// Emitted when data received
$worker->onMessage = function ($connection, $data) use ($worker, $stream) {

    echo "接收的數(shù)據(jù):" . $data . "\n";

    // 簡(jiǎn)單實(shí)現(xiàn)接口分發(fā)
    $arr = explode(",", $data);

    if (!is_array($arr) || !count($arr)) {
        $connection->close("數(shù)據(jù)格式錯(cuò)誤", 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 closed\n";
};


// 接收緩沖區(qū)溢出回調(diào)
$worker->onBufferFull = function ($connection) {
    echo "清理緩沖區(qū)吧";
};

Worker::runAll();

?>

3. 粘包測(cè)試

? ? 只需要在客戶端模擬兩個(gè)數(shù)據(jù)包連在一起,但是要以 #分隔,看看服務(wù)端接收的時(shí)候是一幾個(gè)包進(jìn)行處理的。

workerman 自定義的協(xié)議如何解決粘包拆包

4. 拆包測(cè)試

? ? ? ? 拆包模擬只需要將一個(gè)數(shù)據(jù)包分成兩次發(fā)送,看看服務(wù)端接收的時(shí)候能不能顯示或者說(shuō)能不能按約定好的格式正確顯示。

workerman 自定義的協(xié)議如何解決粘包拆包文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-491491.html

到了這里,關(guān)于workerman 自定義的協(xié)議如何解決粘包拆包的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Unity-TCP-網(wǎng)絡(luò)聊天功能(一):?API、客戶端服務(wù)器、數(shù)據(jù)格式、粘包拆包

    Unity-TCP-網(wǎng)絡(luò)聊天功能(一):?API、客戶端服務(wù)器、數(shù)據(jù)格式、粘包拆包

    TCP是面向連接的。因此需要?jiǎng)?chuàng)建監(jiān)聽(tīng)器,監(jiān)聽(tīng)客戶端的連接。當(dāng)連接成功后,會(huì)返回一個(gè)TcpClient對(duì)象。通過(guò)TcpClient可以接收和發(fā)送數(shù)據(jù)。 VS創(chuàng)建C# .net控制臺(tái)應(yīng)用 項(xiàng)目中創(chuàng)建文件夾Net,Net 下添加TCPServer.cs類,用來(lái)創(chuàng)建TCPListener和Accept客戶端連接,實(shí)例化一個(gè)TCPServcer放在Main函數(shù)

    2024年02月07日
    瀏覽(129)
  • Netty自定義應(yīng)用層協(xié)議逃不開(kāi)的粘包和拆包處理

    Netty自定義應(yīng)用層協(xié)議逃不開(kāi)的粘包和拆包處理

    導(dǎo)致一次發(fā)送的數(shù)據(jù)被分成多個(gè)數(shù)據(jù)包進(jìn)行傳輸,或者多次發(fā)送的數(shù)據(jù)被粘成一個(gè)數(shù)據(jù)包進(jìn)行傳輸 使用TCP進(jìn)行數(shù)據(jù)傳輸時(shí),TCP是一種有序的字節(jié)流,其中是一個(gè)一個(gè)的數(shù)據(jù)報(bào)文發(fā)送到系統(tǒng)的緩沖區(qū)中。因此在發(fā)送端和接收端之間無(wú)法保證數(shù)據(jù)的分割和邊界。這就可能導(dǎo)致數(shù)據(jù)

    2023年04月23日
    瀏覽(31)
  • 網(wǎng)絡(luò)編程:TCP粘包問(wèn)題——各層粘包/拆包、Nagle 算法、Go實(shí)現(xiàn)長(zhǎng)度字段協(xié)議解決TCP粘包、使用TCP的應(yīng)用層協(xié)議設(shè)計(jì)

    網(wǎng)絡(luò)編程:TCP粘包問(wèn)題——各層粘包/拆包、Nagle 算法、Go實(shí)現(xiàn)長(zhǎng)度字段協(xié)議解決TCP粘包、使用TCP的應(yīng)用層協(xié)議設(shè)計(jì)

    1.1 TCP介紹 如上圖,TCP具有面向連接、可靠、基于字節(jié)流三大特點(diǎn)。 字節(jié)流可以理解為一個(gè)雙向的通道里流淌的數(shù)據(jù),這個(gè)數(shù)據(jù)其實(shí)就是我們常說(shuō)的二進(jìn)制數(shù)據(jù),簡(jiǎn)單來(lái)說(shuō)就是一大堆 01 串。純裸TCP收發(fā)的這些 01 串之間是沒(méi)有任何邊界的,你根本不知道到哪個(gè)地方才算一條完

    2024年02月04日
    瀏覽(24)
  • C++ Qt TCP協(xié)議,處理粘包、拆包問(wèn)題,加上數(shù)據(jù)頭來(lái)處理

    目錄 前言: 場(chǎng)景: 原因: 解決: 方案2具體細(xì)節(jié): 純C++服務(wù)端處理如下: Qt客戶端處理如下: ? ? ? ? tcp協(xié)議里面,除了心跳檢測(cè)是關(guān)于長(zhǎng)連接操作的處理,這個(gè)在前一篇已經(jīng)提到過(guò)了,這一篇將會(huì)對(duì)tcp本身的一個(gè)問(wèn)題,進(jìn)行處理:那就是做網(wǎng)絡(luò)通信大概率會(huì)遇到的問(wèn)題

    2024年02月04日
    瀏覽(22)
  • 解決TCP粘包/拆包問(wèn)題的方法及示例

    TCP粘包和拆包是網(wǎng)絡(luò)編程中常見(jiàn)的問(wèn)題,特別是在數(shù)據(jù)傳輸?shù)倪^(guò)程中,可能會(huì)發(fā)生將多個(gè)數(shù)據(jù)包粘在一起或?qū)⒁粋€(gè)數(shù)據(jù)包拆成多個(gè)數(shù)據(jù)包的情況,這可能會(huì)導(dǎo)致應(yīng)用程序無(wú)法正確解析數(shù)據(jù),從而造成數(shù)據(jù)錯(cuò)誤或系統(tǒng)故障。本文將介紹TCP粘包和拆包的原因、解決方案以及兩個(gè)示

    2024年02月10日
    瀏覽(17)
  • TCP粘包和拆包問(wèn)題及其解決方法

    TCP粘包和拆包問(wèn)題及其解決方法

    含義: TCP 傳輸協(xié)議是面向流的,沒(méi)有數(shù)據(jù)包界限,也就是說(shuō)消息無(wú)邊界??蛻舳讼蚍?wù)端發(fā)送數(shù)據(jù)時(shí),可能將一個(gè)完整的報(bào)文拆分成多個(gè)小報(bào)文進(jìn)行發(fā)送,也可能將多個(gè)報(bào)文合并成一個(gè)大的報(bào)文進(jìn)行發(fā)送。(TCP協(xié)議的底層,并不了解上層業(yè)務(wù)的具體定義,它會(huì)根據(jù)TCP緩沖區(qū)

    2023年04月21日
    瀏覽(19)
  • TCP的粘包、拆包、解決方案以及Go語(yǔ)言實(shí)現(xiàn)

    TCP的粘包、拆包、解決方案以及Go語(yǔ)言實(shí)現(xiàn)

    TCP的粘包和拆包問(wèn)題往往出現(xiàn)在基于TCP協(xié)議的通訊中,比如RPC框架 在使用TCP進(jìn)行數(shù)據(jù)傳輸時(shí),由于TCP是基于字節(jié)流的協(xié)議,而不是基于消息的協(xié)議,可能會(huì)出現(xiàn)粘包(多個(gè)消息粘在一起)和拆包(一個(gè)消息被拆分成多個(gè)部分)的問(wèn)題。這些問(wèn)題可能會(huì)導(dǎo)致數(shù)據(jù)解析錯(cuò)誤或數(shù)據(jù)

    2024年02月15日
    瀏覽(27)
  • Netty-LengthFieldBasedFrameDecoder-解決拆包粘包問(wèn)題的解碼器

    Netty-LengthFieldBasedFrameDecoder-解決拆包粘包問(wèn)題的解碼器

    maxFrameLength:指定解碼器所能處理的數(shù)據(jù)包的最大長(zhǎng)度,超過(guò)該長(zhǎng)度則拋出 TooLongFrameException 異常; lengthFieldOffset:指定 長(zhǎng)度字段 的起始位置; lengthFieldLength:指定 長(zhǎng)度字段 的長(zhǎng)度:目前支持1(byte)、2(short)、3(3個(gè)byte)、4(int)、8(Long) lengthAdjustment:指定 長(zhǎng)度字段 所表示的消息

    2024年02月12日
    瀏覽(21)
  • 說(shuō)說(shuō) TCP的粘包、拆包

    說(shuō)說(shuō) TCP的粘包、拆包

    拆包和粘包是在socket編程中經(jīng)常出現(xiàn)的情況, 在socket通訊過(guò)程中,如果通訊的一端一次性連續(xù)發(fā)送多條數(shù)據(jù)包,tcp協(xié)議會(huì)將 多個(gè)數(shù)據(jù)包打包 成一個(gè)tcp報(bào)文發(fā)送出去,這就是所謂的 粘包 。 如果通訊的一端發(fā)送的數(shù)據(jù)包超過(guò)一次tcp報(bào)文所能傳輸?shù)淖畲笾禃r(shí),就會(huì)將 一個(gè)數(shù)據(jù)包

    2024年02月09日
    瀏覽(24)
  • Netty粘包與拆包

    文章首發(fā)地址 TCP是一個(gè)“流”協(xié)議。所謂流,就是沒(méi)有界限的一長(zhǎng)串二進(jìn)制數(shù)據(jù)。 拆包和粘包 :TCP作為傳輸層協(xié)議,并不了解上層業(yè)務(wù)數(shù)據(jù)的具體含義,它會(huì)根據(jù)TCP緩沖區(qū)的實(shí)際情況進(jìn)行數(shù)據(jù)包的劃分,所以在業(yè)務(wù)上認(rèn)為是一個(gè)完整包的,可能會(huì)被TCP拆分成多個(gè)包進(jìn)行發(fā)送

    2024年02月16日
    瀏覽(38)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包