swoole (TCP, packet processing sticky, socket buffer treatment)

Synchronization code
  • 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(); //
asynchronous
  • 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(); //接收消息没有接收
Heartbeat
swoole会在主进程独立起一个心跳线程,通过定时轮询所有的连接,来判断连接的生死,所以swoole的心跳不会堵塞任何业务逻辑。

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

A little more than twice the recommended heartbeat_idle_time of heartbeat_check_interval.
This is twice the purpose of fault-tolerant, allowing the package and lost a little more considering the delay of the network.

TCP and UDP understanding

Here Insert Picture Description
1, TCP connection-oriented (e.g., dial-up connection is established first call); the UDP is connectionless, i.e. without establishing a connection before sending data
2, TCP provides reliable service. In other words, the data transfer connection of TCP, error-free, not lost, not repeat, and arrive out of order; UDP best effort, that does not guarantee reliable delivery
3, tcp by check and retransmission control, serial number identification sliding window, reliable transmission of the acknowledgment. The retransmission control packet loss, also a mess on the order of sub-sequence control.
3, UDP has better real-time performance, higher efficiency than TCP, suitable for high-speed transmission and higher real-time communication or broadcast communication.
4, TCP more demanding on system resources, UDP less demanding on system resources.
TCP and UDP difference
TCP Communications Features

  1. TCP is not streaming protocol message boundaries, a client sends data to the server, the server may be divided into multiple received. The client sends a plurality of data to the server. The server may receive all at once.
  2. . Guarantee transmission reliability, sequentially.
  3. TCP has a congestion control, so packets may be delayed send.

Refers to a plurality of stick package TCP data packet sent by the sender to the recipient receives stick into a packet, from the receive buffer to see, after a first packet data immediately before the end of a data packet.

The reason sticky TCP packets appear?

Sender: The sender needs such as the buffer is full before sending out, causing the stick package
recipient: The recipient receives the packet buffer is not timely, resulting in multiple packet reception

Usually we think of intuitive, client data directly to the transmission network, data is read from the network to the end, but this is not true.

socket buffer concept of the buffer, each TCP socket has a transmission buffer and a receive buffer core. Client send operation is only to copy the data to the buffer, that is send completed, the data does not mean that has been sent to the server, and after it is sent by the TCP protocol from the buffer to the server. At this time, the receive buffer is TCP server cached data onto the network, then the server only to read data from the buffer.

So, we got in onReceive data and there is no way to ensure the integrity of the packet, swoole_server may receive multiple requests simultaneously package, you may receive only a portion of the data request packet.
Here Insert Picture Description
Stick package solutions to problems
https://www.cnblogs.com/JsonM/articles/9283037.html

swoole a solution

  • 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 second solution (packet header + body)

  • server.php
    open_length_check: Open packet length detection characteristics
    package_length_type: Type length field, with a fixed 4-byte header 2 bytes or length of the bag body.
    package_length_offset: from the first few bytes in length, such as the header length of 120 bytes, the byte length value 10, here is filled 9 (counting from 0)
    package_body_offset: calculated from the first few bytes length , such as the header length of 120 bytes, the byte length value 10, the length of the packet 1000. If the header contains a length, where 0 is filled, if the header is not included here fill 120
    package_max_length: maximum permissible packet length. Because in front of a full request packet is received, all the data needs to be stored in memory, it needs to be done to protect. Avoid memory footprint is too large.
<?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();//接收信息

Will appear above information is not sent, the client or the server to shut down, resulting in the remaining information can not be sent, the best solution is to send a confirmation message, then close

Acknowledgment mechanism (to ensure that information is sent successfully, retry mechanism) (to be determined)

Guess you like

Origin blog.csdn.net/qq_33332184/article/details/90760050