欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-04-29 12:57:50
本文实例讲述了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, 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语言程序设计有所帮助。