脚本专栏 
首页 > 脚本专栏 > 浏览文章

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

(编辑:jimmy 日期: 2024/12/22 浏览:3 次 )

本文实例讲述了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, 0, 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, 0, 1024))
    // 消息内容
    message := []byte("我是utf-8的消息")
    // 消息长度
    messageLen := uint32(len(message))
    // 消息总长度
    mlen := 4 + len(message)
    // 写入5条消息
    for i := 0; i < 10; i++ {
        binary.Write(msgbuf, binary.LittleEndian, 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编写的Socket服务器模块解耦及基础模块的设计
下一篇:服务器端Go程序对长短链接的处理及运行参数的保存
一句话新闻
Windows上运行安卓你用过了吗
在去年的5月23日,借助Intel Bridge Technology以及Intel Celadon两项技术的驱动,Intel为PC用户带来了Android On Windows(AOW)平台,并携手国内软件公司腾讯共同推出了腾讯应用宝电脑版,将Windows与安卓两大生态进行了融合,PC的使用体验随即被带入到了一个全新的阶段。