美文网首页
tcp粘包 go和swoole

tcp粘包 go和swoole

作者: mafa1993 | 来源:发表于2021-02-20 16:05 被阅读0次

tcp 粘包及处理 go 和swoole

tcp粘包形成的原因

  1. tcp在发送和接收时会有个缓存,当短时间内发送大量小数据包时,会将数据线缓存起来,等达到一定数量才会发送,接收时也是。如果短时间发送大量数据,则会发生截断问题

tcp粘包处理

  1. 发送数据时会先拼接上数据长度,例如,用两个字节来存放数据长度,然后连接数据,接收时先取出长度,然后根据长度取出对应数据

客户端代码

func main() {
    conn,_ := net.Dial("tcp","127.0.0.1:3333")
    defer conn.Close()
    
    msg := "处理tcp粘包"
    msgLen := len(msg) 
    length := int16(msgLen) // int 转换成int16
    
    pkg := new(bytes.Buffer) //创建写io
    binary.Write(pkg,binary.BigEndian,length)
    data := append(pkg.Bytes(),[]byte(msg)...) // 长度和消息拼接
    fmt.Println("data",string(data))
    
    //发送
    conn.Write(data)
    
    var data [1024]byte
    n,_ := conn.Read(data[:])
    fmt.Println("msg:",string(data[:n]))
}

服务端代码

// 服务端

func main() {
    listen,err := net.Listen("tcp","127.0.0.1:3333")
    if err != nil {
        fmt.Println("err",err)
        return 
    }
    for {
        // 接收客户端向服务端建立的连接
    conn,err := listen.Accept()  // 可以与客户端段建立连接,如果没有连接就挂起阻塞
        
        if err != nil {
            fmt.Println("err",err)
            return
        }
        //处理客户连接
        go handler(conn) // 可以利用协程处理,提高效率        
    }
}

func handler(c net.Conn) {
    defer c.Close()
    reader := bufio.NewReader(c)
    for {
        // 接收
        
        msg,err := unpack(reader)
        if err != nil {
            fmt.Println("err",err)
            break
        }
        
        fmt.Println("接收到数据:",string(data[:n]))
        
        // 向客户端发送数据
        c.Write([]byte("服务端"))
    }
}

// 数据解析
func unpack(reader *bufio.Reader) (string,error) {
    // 对字符串截取,长度数据
    lenByte,_ := reader.Peek(2)  //读取两个字节的数据
    lengthBuff := bytes.NewBuffer(lenByte) // 建立缓冲区对数据进行读取
    var length int16
    err := binary.Read(lengthBuff,binary.BigEndian,&length)
    /*
    func Read(r io.Reader, order ByteOrder, data interface{}) error
    从r中读取binary编码的数据并赋给data,data必须是一个指向定长值的指针或者定长值的切片。从r读取的字节使用order指定的字节序解码并写入data的字段里当写入结构体是,名字中有'_'的字段会被跳过,这些字段可用于填充(内存空间)。 
    第二个参数为要转换成的进制
    **/
    
    // 读取数据,读取的为2+msgLen,就是包长度和内容一起读取
    if int16(reader.Buffered()) < length + 2 {
        return "",errors.New("数据异常")
    }
    
    //  真正读取
    pack := make([]byte,int(2+length)) // 创建一个切片,用于存储读取的数据,利用切片的长度去限定读取的长度??
    _,err = reader.Read(pack)
    if err != nil {
        return "",err
    }
    
    fmt.Println("获取的数据",string(pack))
    return string(pack),nil
}

swoole的话自带pack函数可以轻松的进行处理

//main.go 服务端 粘包部分服务端代码
func main() {
    
}

//swoole
<?php
$client = new Swoole\Client(SWOOLE_SOCK_TCP);
if(!$client->connect('127.0.0.1',3333,-1)){
    exit("connect failed, error {$client->errCode}\n");
}

$context = "abcd";
$len = pack("n",strlen($context));
$send = $len.$context;
$client->send($send);

echo $clent->recv();

$client->close();

//swoole 作为服务端的代码
<?php
$server = new Swoole\Server('0.0.0.0',9501);

$server->set([
    'open_length_check' => true,
    'package_max_length' => 1*1024*1024,
    'package_length_type' => 'n',
    'package_length_offset' => 0,
    'package_body_offset' => 2
]);

$server->on('connect',function($server,$fd){});

$server->on('receive',function($serv,$fd,$from_id,$data){
    $serv->send($fd,"server")
});

$server->on('close',function(){});

$server->start();

相关文章

网友评论

      本文标题:tcp粘包 go和swoole

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