粘包问题是指在TCP通信中,发送方发送的多个独立消息在接收方被合并成一个消息接收的现象。换句话说,发送方发送的多条消息在接收方被“粘”在一起,导致接收方无法直接区分消息的边界。
二、粘包问题的示例
package main
import (
"fmt"
"net"
)
func main() {
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
fmt.Println("Error connecting:", err)
return
}
defer conn.Close()
_, err = conn.Write([]byte("Hello"))
if err != nil {
fmt.Println("Error writing:", err)
return
}
_, err = conn.Write([]byte("World"))
if err != nil {
fmt.Println("Error writing:", err)
return
}
conn.Close()
}
package main
import (
"fmt"
"net"
)
func main() {
listener, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("Error listening:", err)
return
}
defer listener.Close()
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting:", err)
return
}
defer conn.Close()
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
fmt.Println("Error reading:", err)
return
}
fmt.Println("Received:", string(buffer[:n])) // 输出可能是 "HelloWorld"
}
在上述示例中,发送方发送了两条消息"Hello"和"World",但接收方可能接收到合并后的"HelloWorld",这就是粘包问题。
// 发送方
package main
import (
"fmt"
"net"
)
func main() {
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
fmt.Println("Error connecting:", err)
return
}
defer conn.Close()
// 填充到固定长度(例如10字节)
msg1 := "Hello "
msg2 := "World "
_, err = conn.Write([]byte(msg1))
if err != nil {
fmt.Println("Error writing:", err)
return
}
_, err = conn.Write([]byte(msg2))
if err != nil {
fmt.Println("Error writing:", err)
return
}
conn.Close()
}
// 接收方
package main
import (
"fmt"
"io"
"net"
)
func main() {
listener, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("Error listening:", err)
return
}
defer listener.Close()
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting:", err)
return
}
defer conn.Close()
// 每条消息固定长度为10字节
buffer := make([]byte, 10)
for {
n, err := io.ReadFull(conn, buffer)
if err != nil {
if err != io.EOF {
fmt.Println("Error reading:", err)
}
break
}
fmt.Println("Received:", string(buffer[:n]))
}
}
\n
或\r\n
),接收方通过分隔符来解析消息。// 发送方
package main
import (
"fmt"
"net"
)
func main() {
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
fmt.Println("Error connecting:", err)
return
}
defer conn.Close()
// 添加换行符作为分隔符
_, err = conn.Write([]byte("Hello\n"))
if err != nil {
fmt.Println("Error writing:", err)
return
}
_, err = conn.Write([]byte("World\n"))
if err != nil {
fmt.Println("Error writing:", err)
return
}
conn.Close()
}
// 接收方
package main
import (
"bufio"
"fmt"
"net"
)
func main() {
listener, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("Error listening:", err)
return
}
defer listener.Close()
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting:", err)
return
}
defer conn.Close()
reader := bufio.NewReader(conn)
for {
line, err := reader.ReadString('\n')
if err != nil {
if err.Error() != "EOF" {
fmt.Println("Error reading:", err)
}
break
}
fmt.Println("Received:", line)
}
}
// 发送方
package main
import (
"bytes"
"encoding/binary"
"fmt"
"net"
)
func main() {
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
fmt.Println("Error connecting:", err)
return
}
defer conn.Close()
msg1 := "Hello"
msg2 := "World"
// 发送消息长度 + 消息内容
sendMessage(conn, msg1)
sendMessage(conn, msg2)
conn.Close()
}
func sendMessage(conn net.Conn, msg string) {
// 消息长度(4字节)
length := uint32(len(msg))
buf := make([]byte, 4)
binary.BigEndian.PutUint32(buf, length)
// 发送长度
_, err := conn.Write(buf)
if err != nil {
fmt.Println("Error writing length:", err)
return
}
// 发送消息
_, err = conn.Write([]byte(msg))
if err != nil {
fmt.Println("Error writing message:", err)
return
}
}
// 接收方
package main
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"net"
)
func main() {
listener, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("Error listening:", err)
return
}
defer listener.Close()
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting:", err)
return
}
defer conn.Close()
for {
// 读取消息长度(4字节)
lengthBuf := make([]byte, 4)
_, err := io.ReadFull(conn, lengthBuf)
if err != nil {
if err != io.EOF {
fmt.Println("Error reading length:", err)
}
break
}
length := binary.BigEndian.Uint32(lengthBuf)
msgBuf := make([]byte, length)
// 读取消息内容
_, err = io.ReadFull(conn, msgBuf)
if err != nil {
if err != io.EOF {
fmt.Println("Error reading message:", err)
}
break
}
fmt.Println("Received:", string(msgBuf))
}
}
粘包问题是TCP通信中的常见问题,其本质是TCP协议的面向流特性导致的消息边界丢失。解决粘包问题的方法主要有固定长度法、特殊分隔符法和消息头长度法。选择哪种方法取决于具体的应用场景和需求。如有错误之处烦请指正。