Socket编程TCP协议粘包问题

Socket编程中,基于TCP协议的通信有时候会发生粘包问题,原因大家自行百度,已经搜到这种问题应该了解粘包产生的原因哈

TCP协议是可靠的字节流式协议,字节流可以理解为是水流,数据在网络中像水流一样传输,所以纯粹发送字符串一旦TCP底层发生粘包情况,数据表意将产生错误,比如你想发送,我今天发现一特有意思的事 这句话TCP可能给你拆成我今天发,现一特有意,思的,事儿,只是举个简单例子哈,
我们怎么处理这种问题呢,有多种方案

  1. 一个是根据EOF拆分,这个EOF就是自定义的一个特殊符号,比如我们现在就约定\r\nEOF,碰到这个我就认为你当前发送的消息结束掉了,这个是没问题的哈,只不过有的应用场景,比如我们聊QQ跟人的时候,想一次性打好多话给对面,然后换下行,比如下图这种情况,这种情况让根据换行截断就不合适了吧,因为毕竟是一个消息,如果你多个换行,本来一句话,根据\r\n的约定,会被切成N句话,所以这个要看应用场景
    在这里插入图片描述
  2. 第二种就是我们约定一个数据格式,比如我们自定个规矩(逼格说的高一点叫协议),我们的协议规定,消息一定要有个包头+包体,包头(两个字节)存放消息长度,包体存放字符串的消息,好了,我们现在建立Socket通信以后把我们的数据传递给对端,对端按照我们约定的协议,先把头两个字节给摘出来,看看消息长度有多少,然后按长度向后截取,这样就是一个完整的消息包了,即使我们发送的包几十个都粘在一起,对端依旧可以按照我们的约定把数据包解出来,下面进行代码示范
<?php

// 这里我引用了 symfony/var-dumper 的一个包哈,写Laravel写惯了,里面的dump函数和dd函数特别好用
// 不想引用的同学下面的dump函数自行替换成var_dump哈
require_once __DIR__ . '/vendor/autoload.php';

$user = "Vencenty";
$time = "2020-09-30 21:40:00";

$userMessageLength = strlen($user);
$timeLength = strlen($time);

$userMessage = pack('n', $userMessageLength) . $user;
$timeMessage = pack('n', $timeLength) . $time;


// 假装目前发生了粘包情况,Socket另一端收到的消息是这样似的
$mixedMessage = $userMessage . $timeMessage;

// ---我们协议约定消息包分为包头+包体,包头两字节保存消息长度,包体就是具体消息内容,下面我们依照我们的协议进行拆包
$binaryPackageLength = substr($mixedMessage, 0, 2);
// 获取包长度
$packageLength = unpack('n', $binaryPackageLength);
dump($packageLength); // 我们发现他是一个数组,下标从1开始,那么我们直接获取
$packageLength = $packageLength[1]; // 直接获取包长度

$message = substr($mixedMessage, 2, $packageLength);

// 好,我们目前拆出一条消息
dump($message); // 结果是 "Vencenty"

// 拆第二条消息同理,我们要获取第二条消息的包长,应该是 2(二字节的包长) + 包体长度
$start = 2 + $packageLength;

// 第二个消息包的长度
$secondBinaryPackageLength = substr($mixedMessage, $start, 2);
// 第二个包的包头,我们把内容解出来
$secondPackageLength = unpack('n', $secondBinaryPackageLength);
dump($secondPackageLength); // 19个字节
$secondPackageLength = $secondPackageLength[1];

// 成功截取出字符串来
$secondMessage = substr($mixedMessage, 2 + $start, $secondPackageLength);
dump($secondMessage); // 2020-09-30 21:40:00

当然上面只是一个简单地代码示例哈,其实还存在问题,比如外人万一知道了你的协议,或者猜到了你的协议,他伪造数据包对你进行攻击,包头里面携带的数据长度是100,然后包体里面的字符串消息有1000,这样你拿到消息解码也会出现解析错误的问题,还有一个情况是合包的情况,TCP发送的消息过来以后,发现本来包长100,结果你只收到80个字符串,那么你需要把数据缓存下来,然后把后面发来的数据合并,这个做到具体业务的时候请大家根据自身业务进行处理

好了,最近一直在加班没空写东西,有错误希望大家指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值