swoole(TCP,粘包处理,socket缓存处理)

同步代码
  • server.php
<?php
/**
 * Created by PhpStorm.
 * User: niuyueyang
 * Date: 2019/3/19
 * Time: 21:37
 */

//tcp协议
$server=new Swoole\Server("0.0.0.0",9800);   //创建server对象

$server->set([
     'worker_num'=>2, //设置进程
]);

//监听事件,连接事件
$server->on('connect',function ($server,$fd){
    echo "新的连接进入:{$fd}".PHP_EOL;
});

//消息发送过来
$server->on('receive',function (swoole_server $server, int $fd, int $reactor_id, string $data){
    echo "消息发送过来:".$fd.PHP_EOL;
    $server->send($fd,'我是服务端');
});

//消息关闭
$server->on('close',function (){
    echo "消息关闭".PHP_EOL;
});
//服务器开启
$server->start();
  • client.php
<?php
/**
 * Created by PhpStorm.
 * User: niuyueyang
 * Date: 2019/3/19
 * Time: 23:05
 */
 $client=new swoole\Client(SWOOLE_SOCK_TCP,SWOOLE_SYNC);
 $client->connect('118.24.109.254',9800) || exit("连接失败");
  //(fd+id)识别身份
 //发数据
 $client->send("我是客户端");
 echo $client->recv(); //接收消息
 //关闭
 $client->close(); //
异步
  • server.php
<?php
//tcp协议
$server=new Swoole\Server("0.0.0.0",9800);   //创建server对象
//heartbeat_idle_time一般设置heartbeat_check_interval两倍多一点
$server->set([
    'worker_num'=>1, //设置进程
    'heartbeat_idle_time'=>10,//连接最大的空闲时间
    'heartbeat_check_interval'=>3 //服务器定时检查
]);
//监听事件,连接事件
$server->on('connect',function ($server,$fd){
    echo "新的连接进入:{$fd}".PHP_EOL;
});
//消息发送过来
$server->on('receive',function (swoole_server $server, int $fd, int $reactor_id, string $data){
    echo "消息发送过来:".$fd.PHP_EOL;

    //服务端
    //$server->send($fd,'我是服务端');
});
//消息关闭
$server->on('close',function (){
    echo "消息关闭".PHP_EOL;
});
//服务器开启
$server->start();
  • client.php
<?php
$client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
//连接事件回调(必须注册所有事件)
$client->on("connect", function(swoole_client $cli){
    $cli->send("GET / HTTP/1.1\r\n\r\n");
    //sleep(5);
});
//异步回调客户端
$client->on("receive", function(swoole_client $cli, $data){
      echo "Receive: $data";
     //$cli->send(str_repeat('A', 100)."\n");

});
$client->on("error", function(swoole_client $cli){
    echo "error\n";
});
$client->on("close", function(swoole_client $cli){
      echo "Connection close\n";
});
$client->connect('127.0.0.1', 9800) || exit("");
//定时器,保持长连接
swoole_timer_tick(9000,function () use($client){
     $client->send('1');
});

echo "写日志";
echo "请求api接口";
UDP
  • server.php
<?php
//udp协议
$server=new Swoole\Server("0.0.0.0",9800,SWOOLE_PROCESS,SWOOLE_SOCK_UDP);   //创建server对象

$server->set([
    'worker_num'=>1, //设置进程
    'heartbeat_idle_time'=>10,//连接最大的空闲时间
    'heartbeat_check_interval'=>3 //服务器定时检查
]);

//客户端服务端没有任何联系
//制定地址跟端口,不关心消息是否发送成功
//心跳检测不能影响到客户端
//udp建立长连接

//监听事件,
$server->on('packet',function ($server,$data,$clientInfo){
        var_dump($data,$clientInfo);
        $server->sendto($clientInfo['address'],$clientInfo['port'],"服务端数据包");
});

//服务器开启
$server->start();
  • client.php
<?php
 $client=new swoole\Client(SWOOLE_SOCK_UDP);
//(fd+id)识别身份
 //发数据
 $client->sendto('127.0.0.1',9800,"我是客户端");

 echo $client->recv(); //接收消息没有接收
心跳检测
swoole会在主进程独立起一个心跳线程,通过定时轮询所有的连接,来判断连接的生死,所以swoole的心跳不会堵塞任何业务逻辑。

设置完成了之后,你会发现设置了定时检测之后,如果客户端没在规定的时间之内发送数据就会关闭。
heartbeat_check_interval: 服务器定时检测在线列表的时间
heartbeat_idle_time:       连接最大的空闲时间 (如果最后一个心跳包的时间与当前时间之差超过这个值,则认为该连接失效)
配置建议

建议 heartbeat_idle_time 为 heartbeat_check_interval 的两倍多一点。
这个两倍是为了进行容错,允许丢一个包而多一点是考虑到网络的延时。

TCP与UDP理解

在这里插入图片描述
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
3、tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。
3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
4、TCP对系统资源要求较多,UDP对系统资源要求较少。
TCP与UDP区别
TCP通信特点

  1. TCP 是流式协议没有消息边界,客户端向服务器端发送一次数据,可能会被服务器端分成多次收到。客户端向服务器端发送多条数据。服务器端可能一次全部收到。
  2. .保证传输的可靠性,顺序。
  3. TCP拥有拥塞控制,所以数据包可能会延后发送。

TCP 粘包是指发送方发送的若干包数据 到 接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。

TCP 出现粘包的原因?

发送方:发送方需要等缓冲区满才发送出去,造成粘包
接收方:接收方不及时接收缓冲区的包,造成多个包接收

通常我们直觉性的认为,客户端直接向网络中传输数据,对端从网络中读取数据,但是这是不正确的。

socket有缓冲区buffer的概念,每个TCP socket在内核中都有一个发送缓冲区和一个接收缓冲区。客户端send操作仅仅是把数据拷贝到buffer中,也就是说send完成了,数据并不代表已经发送到服务端了,之后才由TCP协议从buffer中发送到服务端。此时服务端的接收缓冲区被TCP缓存网络上来的数据,而后server才从buffer中读取数据。

所以,在onReceive中我们拿到的数据并没有办法保证数据包的完整性,swoole_server可能会同时收到多个请求包,也可能只收到一个请求包的一部分数据。
在这里插入图片描述
粘包问题解决方案
https://www.cnblogs.com/JsonM/articles/9283037.html

swoole解决方案一

  • server.php
<?php
$server=new Swoole\Server("0.0.0.0",9800);   //创建server对象
$server->set([
    'worker_num'=>1, //设置进程
    'heartbeat_idle_time'=>10,//连接最大的空闲时间
    'heartbeat_check_interval'=>3, //服务器定时检查
    'open_eof_check' => true, //打开EOF检测 
    'package_eof' => "\r\n", //设置EOF 
]);

//监听事件,连接事件
$server->on('connect',function ($server,$fd){
    echo "新的连接进入:{$fd}".PHP_EOL;
});

//消息发送过来
$server->on('receive',function (swoole_server $server, int $fd, int $reactor_id, string $data){
    //var_dump("消息发送过来:".strlen($data));
    //服务端
    //$server->send($fd,'我是服务端');
    $data=explode("\r\n",$data);
    foreach ($data as $v){
      if(!$v){
              continue;
           }
      var_dump("消息发送过来:".$v);
    }
});
//消息关闭
$server->on('close',function (){
    echo "消息关闭".PHP_EOL;
});
//服务器开启
$server->start();
  • client.php
<?php
 $client=new swoole\Client(SWOOLE_SOCK_TCP);
//(fd+id)识别身份
 //发数据
 $client->connect('127.0.0.1',9800);

 //约定一个分隔符
 //一次性发送多条数据
 for ($i=0;$i<10;$i++){
     $client->send("123456\r\n");
 }

swoole解决方案二(包头+包体)

  • server.php
    open_length_check:打开包长检测特性
    package_length_type:长度字段的类型,固定包头中用一个4字节或2字节表示包体长度。
    package_length_offset:从第几个字节开始是长度,比如包头长度为120字节,第10个字节为长度值,这里填入9(从0开始计数)
    package_body_offset:从第几个字节开始计算长度,比如包头为长度为120字节,第10个字节为长度值,包体长度为1000。如果长度包含包头,这里填入0,如果不包含包头,这里填入120
    package_max_length:最大允许的包长度。因为在一个请求包完整接收前,需要将所有数据保存在内存中,所以需要做保护。避免内存占用过大。
<?php
//tcp协议
$server=new Swoole\Server("0.0.0.0",9800);   //创建server对象
$server->set([
    'worker_num'=>1, //设置进程
    'heartbeat_idle_time'=>10,//连接最大的空闲时间
    'heartbeat_check_interval'=>3, //服务器定时检查
    'open_length_check'=>true,
    'package_length_type'=>'N',
    'package_length_offset'=>0,
    'package_body_offset'=>4,
    'package_max_length'=>1024*1024*3, //包大小为3M
    'buffer_output_size'=>1024*1024*3,//输出缓存区大小
]);
//监听事件,连接事件
$server->on('connect',function ($server,$fd){
    echo "新的连接进入:{$fd}".PHP_EOL;
});
//消息发送过来
$server->on('receive',function (swoole_server $server, int $fd, int $reactor_id, string $data){
    var_dump("消息发送过来:".substr($data,4));
    $server->send($fd,'服务端已收到');
});
$server->on('close',function (){
    echo "消息关闭".PHP_EOL;
});
$server->start();
  • client.php
<?php
 $client=new swoole\Client(SWOOLE_SOCK_TCP);
 $client->connect('127.0.0.1',9800);
 $body='客户端发送';
 $data=pack('N',strlen($body)).$body;//包头+包体
 $client->send($data);//发送
 echo $client->recv();//接收信息

上面会出现信息没有发送完毕,客户端或者服务端就关闭,导致剩余信息无法发送,解决办法最好就是发送一个确认信息,再进行关闭

确认机制(保证信息发送成功,重试机制)(待定)
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值