IT评测·应用市场-qidao123.com
标题:
GolangTCP通信办理粘包问题
[打印本页]
作者:
河曲智叟
时间:
2025-3-15 02:35
标题:
GolangTCP通信办理粘包问题
重要原因就是tcp数据传递模式是流模式,在保持长毗连的时间可以举行多次的收和发。
“粘包"可发生在发送端也可发生在接收端:
由Nagle算法造成的发送端的粘包:Nagle算法是一种改善网络传输效率的算法。简单来说就是当我们提交一段数据给TCP发送时,TCP并不立刻发送此段数据,而是等待一小段时间看看在等待期间是否还有要发送的数据,如有则会一次把这两段数据发送出去。
接收端接收不实时造成的接收端粘包:TCP会把接收到的数据存在自己的缓冲区中,然后关照应用层取数据。当应用层由于某些原因不能实时的把TCP的数据取出来,就会造成TCP缓冲区中存放了几段数据。
办理办法
出现"粘包"的关键在于接收方不确定将要传输的数据包的大小,因此我们可以对数据包举行封包和拆包的操纵。
封包:封包就是给一段数据加上包头,如许一来数据包就分为包头和包体两部门内容了(过滤非法包时封包会参加"包尾"内容)。包头部门的长度是固定的,并且它存储了包体的长度,根据包头长度固定以及包头中含有包体长度的变量就能正确的拆分出一个完备的数据包。
我们可以自己定义一个协议,好比数据包的前4个字节为包头,里面存储的是发送的数据的长度。
protocol包:
package protocol
import (
"bufio"
"bytes"
"encoding/binary"
)
// Encode 将消息编码
func Encode(message string) ([]byte, error) {
// 读取消息的长度,转换成int32类型(占4个字节)
var length = int32(len(message))
var pkg = new(bytes.Buffer)
// 写入消息头
err := binary.Write(pkg, binary.LittleEndian, length)
if err != nil {
return nil, err
}
// 写入消息实体
err = binary.Write(pkg, binary.LittleEndian, []byte(message))
if err != nil {
return nil, err
}
return pkg.Bytes(), nil
}
// Decode 解码消息
func Decode(reader *bufio.Reader) (string, error) {
// 读取消息的长度
lengthByte, _ := reader.Peek(4) // 读取前4个字节的数据
lengthBuff := bytes.NewBuffer(lengthByte)
var length int32
err := binary.Read(lengthBuff, binary.LittleEndian, &length)
if err != nil {
return "", err
}
// Buffered返回缓冲中现有的可读取的字节数。
if int32(reader.Buffered()) < length+4 {
return "", err
}
// 读取真正的消息数据
pack := make([]byte, int(4+length))
_, err = reader.Read(pack)
if err != nil {
return "", err
}
return string(pack[4:]), nil
}
复制代码
客户端:
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:30000")
if err != nil {
fmt.Println("dial failed, err", err)
return
}
defer conn.Close()
for i := 0; i < 20; i++ {
msg := `Hello, Hello. How are you?`
data, err := proto.Encode(msg)
if err != nil {
fmt.Println("encode msg failed, err:", err)
return
}
conn.Write(data)
}
}
复制代码
服务器:
package main
import (
"bufio"
"fmt"
"io"
"net"
proto "studygo/studygo/tcp/protocol"
"sync"
)
var wg sync.WaitGroup
func process(conn net.Conn) {
//与客户端通信
defer conn.Close()
reader := bufio.NewReader(conn)
for {
msg, err := proto.Decode(reader)
if err == io.EOF {
break
}
if err!=nil{
fmt.Println("decode msg failed,err:",err)
return
}
fmt.Println(msg)
}
}
func main() {
//本地端口启动服务
listener, err := net.Listen("tcp", "127.0.0.1:8000")
if err != nil {
fmt.Println("listen failed,err:%v\n", err)
return
}
defer listener.Close()
for {
//等待别人连接
conn, err := listener.Accept()
if err != nil {
fmt.Println("accept failed,err:%v\n", err)
return
}
go process(conn)
}
}
复制代码
参考出处:Go语言基础之网络编程 | 李文周的博客
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/)
Powered by Discuz! X3.4