美文网首页
8.tcp粘包处理2

8.tcp粘包处理2

作者: 一个人的北京_ | 来源:发表于2019-06-08 15:28 被阅读0次

    在上一节中由于EOF切割需要遍历整个数据包的内容,查找EOF,因此会消耗大量CPU资源。假设每个数据包为2M,每秒10000个请求,这可能会产生20G条CPU字符匹配指令。
    因此我们使用 固定包头+包体协议 来处理粘包

    //客户端代码
    $client = new swoole_client(SWOOLE_SOCK_TCP,SWOOLE_SOCK_SYNC);
    
    $client->set([
        'open_length_check'     => 1,
        'package_max_length'    => 2*1024*1024,  //允许包的最大长度2MB
        'package_length_type'   => 'N',   //N:无符号、网络字节序、4字节 (常用)
        'package_length_offset' => 0,     //整个包头加包体计算长度
        'package_body_offset'   =>'4',     //包体从第4字节开始计算长度
        'socket_buffer_size' => 2* 1024 *1024, //配置客户端连接的缓存区长度
    ]);
    
    $client->connect('127.0.0.1',9800);
    
    //发送数据,需要做粘包处理
    //为了处理粘包,我们和服务端约定一个分隔符(比如\r\n)
    /*for($i=0;$i<10;$i++){
        $client->send("123456\r\n");
    }*/
    
    //发送2MB的数据
    $body = json_encode(str_repeat('a',2*1024*1024));  
    //$body = 'zhangsan';
    //php的pack函数 把数据装入一个二进制字符串,N表示 unsigned long(总是32位, big endian 字节顺序),32位就是4字节,和服务器的约定一样
    $data = pack('N',strlen($body)).$body;
    
    $client->send($data);
    
    //接收数据
    var_dump($client->recv());
    
    //关闭,关闭不能这么草率,需要做应答
    //如果后台发送3次数据,客户端就得接收3次,所以需要做好应答,否则还没接受完数据,就关闭连接,后面的数据接收不到了,同时服务器会显示收到错误的数据
    //参考连接:[https://wiki.swoole.com/wiki/page/313.html](https://wiki.swoole.com/wiki/page/313.html)
    
    //$client->close();
    
    #----------------------------------------
    
    //服务端代码
    $server = new Swoole\Server("0.0.0.0",9800);
    
    //设置进程数,必须为正正数,会产生2+worker_num个数个进程
    $server->set([
        'open_length_check'     => 1,
        'package_max_length'    => 2*1024*1024,  //允许包的最大长度2MB
        'package_length_type'   => 'N',   //N:无符号、网络字节序、4字节 (常用)
        'package_length_offset' => 0,     //整个包头加包体计算长度
        'package_body_offset'   =>'4',     //包体从第4字节开始计算长度
        'buffer_output_size'   => 3*1024*1024     //设置输出缓冲区的大小
    ]);
    
    
    //事件监听
    //1。监听连接
    $server->on('connect',function($server, $fd){
        echo "已连接到服务器:{$fd}".PHP_EOL;
    });
    
    //2。接收到客户端消息
    $server->on('receive',function($server, $fd, $from_id, $data){
        //var_dump('我是服务端接收到的数据长度为:'.strlen($data));
    
        //解包,并且截取数据包
        //$info = unpack('N',$data);
        //var_dump($info);
        //var_dump(substr($data,4));
    
    
        //给客户端返回消息,(客户端发送过来时也是)发送消息时需要确认数据到达,然后再次发送数据,否则发送多条数据,占满了缓冲区之后,后面的数据会溢出,从而被丢弃
        $server->send($fd,$data);
        $server->send($fd,$data);
        $server->send($fd,$data);
    
    });
    
    
    //3。连接关闭
    $server->on('close',function(){
        echo "已关闭连接".PHP_EOL;
    });
    
    //开启服务
    $server->start();
    
    //WARNING   swProtocol_recv_check_length: package is too big, remote_addr=127.0.0.1:41693, length=825373496
    //报错是因为我们没有按规定(包头+包体)格式发送数据包,或者是发送的数据大小超过了服务器的限制,这里我们限制了2MB
    
    
    //  WARNING swFactoryProcess_finish (ERRNO 1202): The length of data [2097158] exceeds the output buffer size[2097152], please use the sendfile, chunked transfer mode or adjust the buffer_output_size
    //报错是因为发送数据超过输出缓冲区的大小buffer_output_size
    //单位为字节,默认为2M,如设置32 * 1024 *1024表示,单次Server->send最大允许发送32M字节的数据
    //调用Server->send, Http\Server->end/write,WebSocket\Server->push 等发送数据指令时,单次最大发送的数据不得超过buffer_output_size配置。
    //注意此函数不应当调整过大,避免拥塞的数据过多,导致吃光机器内存
    //开启大量Worker进程时,将会占用worker_num * buffer_output_size字节的内存
    

    说明:
    ackage_length_type 长度值的类型
    长度值的类型,接受一个字符参数,与php的pack函数一致。目前swoole支持10种类型:
    c:有符号、1字节
    C:无符号、1字节
    s:有符号、主机字节序、2字节
    S:无符号、主机字节序、2字节
    n:无符号、网络字节序、2字节 (常用)
    N:无符号、网络字节序、4字节 (常用)
    l:有符号、主机字节序、4字节(小写L)
    L:无符号、主机字节序、4字节(大写L)
    v:无符号、小端字节序、2字节
    V:无符号、小端字节序、4字节

    对于任何的可靠的消息发送来讲,一定要有一个消息的确认机制、重试机制(IM(websockt))

    相关文章

      网友评论

          本文标题:8.tcp粘包处理2

          本文链接:https://www.haomeiwen.com/subject/abuixctx.html