golang网络socket粘包问题的解决方法

本文实例讲述了golang网络socket粘包问题的解决方法分享给大家供大家参考,具体如下:

看到很多人问这个问题,今天就写了个例子,希望能帮助大家

首先说一下什么是粘包:百度上比较通俗的说法是指TCP协议中,发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾

解决方案如下:

服务端:

package main
import (
    "bytes"
    "encoding/binary"
    "fmt"
    "io"
    "net"
)
func main() {
    // 监听端口
    ln,err := net.Listen("tcp",":6000")
    if err != nil {
        fmt.Printf("Listen Error: %s\n",err)
        return
    }
    // 监听循环
    for {
        // 接受客户端链接
        conn,err := ln.Accept()
        if err != nil {
            fmt.Printf("Accept Error: %s\n",err)
            continue
        }
        // 处理客户端链接
        go handleConnection(conn)
    }
}
func handleConnection(conn net.Conn) {
    // 关闭链接
    defer conn.Close()
    // 客户端
    fmt.Printf("Client: %s\n",conn.RemoteAddr())
    // 消息缓冲
    msgbuf := bytes.NewBuffer(make([]byte,10240))
    // 数据缓冲
    databuf := make([]byte,4096)
    // 消息长度
    length := 0
    // 消息长度uint32
    ulength := uint32(0)
    // 数据循环
    for {
        // 读取数据
        n,err := conn.Read(databuf)
        if err == io.EOF {
            fmt.Printf("Client exit: %s\n",conn.RemoteAddr())
        }
        if err != nil {
            fmt.Printf("Read error: %s\n",err)
            return
        }
        fmt.Println(databuf[:n])
        // 数据添加到消息缓冲
        n,err = msgbuf.Write(databuf[:n])
        if err != nil {
            fmt.Printf("Buffer write error: %s\n",err)
            return
        }
        // 消息分割循环
        for {
            // 消息头
            if length == 0 && msgbuf.Len() >= 4 {
                binary.Read(msgbuf,binary.LittleEndian,&ulength)
                length = int(ulength)
                // 检查超长消息
                if length > 10240 {
                    fmt.Printf("Message too length: %d\n",length)
                    return
                }
            }
            // 消息体
            if length > 0 && msgbuf.Len() >= length {
                fmt.Printf("Client messge: %s\n",string(msgbuf.Next(length)))
                length = 0
            } else {
                break
            }
        }
    }
}

客户端:

package main
import (
    "bytes"
    "encoding/binary"
    "fmt"
    "net"
    "time"
)
func main() {
    // 链接服务器
    conn,err := net.Dial("tcp","127.0.0.1:6000")
    if err != nil {
        fmt.Printf("Dial error: %s\n",err)
        return
    }
    // 客户端信息
    fmt.Printf("Client: %s\n",conn.LocalAddr())
    // 消息缓冲
    msgbuf := bytes.NewBuffer(make([]byte,1024))
    // 消息内容
    message := []byte("我是utf-8的消息")
    // 消息长度
    messageLen := uint32(len(message))
    // 消息总长度
    mlen := 4 + len(message)
    // 写入5条消息
    for i := 0; i < 10; i++ {
        binary.Write(msgbuf,messageLen)
        msgbuf.Write(message)
    }
    // 单包发送一条消息
    conn.Write(msgbuf.Next(mlen))
    time.Sleep(time.Second)
    // 单包发送三条消息
    conn.Write(msgbuf.Next(mlen * 3))
    time.Sleep(time.Second)
    // 发送不完整的消息头
    conn.Write(msgbuf.Next(2))
    time.Sleep(time.Second)
    // 发送消息剩下部分
    conn.Write(msgbuf.Next(mlen - 2))
    time.Sleep(time.Second)
    // 发送不完整的消息体
    conn.Write(msgbuf.Next(mlen - 6))
    time.Sleep(time.Second)
    // 发送消息剩下部分
    conn.Write(msgbuf.Next(6))
    time.Sleep(time.Second)
    // 多段发送
    conn.Write(msgbuf.Next(mlen + 2))
    time.Sleep(time.Second)
    conn.Write(msgbuf.Next(-2 + mlen - 8))
    time.Sleep(time.Second)
    conn.Write(msgbuf.Next(8 + 1))
    time.Sleep(time.Second)
    conn.Write(msgbuf.Next(-1 + mlen + mlen))
    time.Sleep(time.Second)
    // 关闭链接
    conn.Close()
}

希望本文所述对大家Go语言程序设计有所帮助。

相关文章

什么是Go的接口? 接口可以说是一种类型,可以粗略的理解为他...
1、Golang指针 在介绍Golang指针隐式间接引用前,先简单说下...
1、概述 1.1&#160;Protocol buffers定义 Protocol buffe...
判断文件是否存在,需要用到"os"包中的两个函数: os.Stat(...
1、编译环境 OS :Loongnix-Server Linux release 8.3 CPU指...
1、概述 Golang是一种强类型语言,虽然在代码中经常看到i:=1...