TCP粘包问题
什么是TCP粘包
TCP粘包就是指发送方发送的若干数据包到达接受方时粘成了一包。从接收缓冲区来看,后一包数据的头紧接着前一包数据的尾。
为什么会出现粘包
主要原因就是TCP数据传递模式是流模式,在保持长连接的时候可以进行多次的收和发。
粘包可发生在发送端也可以发生在接收端:
- 发送端:由Nagle算法导致的粘包。Nagle算法是一种改善网络传输效率的算法。这个算法简单来说,就是当我们提交一段数据给TCP发送时,TCP并不会立刻发送这段数据,而是等待一小段时间卡看看在等待期间是否还有要发送的数据,若有则会一次把两段数据合并成一个大的数据块,进行封包,发送出去。
- 接收端:由于接收端接收不及时造成的粘包。TCP会把接收到的数据存在自己的缓冲区内,然后通知应用层取数据。当应用层由于某些原因不能及时的把TCP的数据取出来,就会造成TCP缓冲区存放了几段数据。
粘包示例程序
Server端:
package main
import (
"bufio"
"fmt"
"io"
"net"
"os"
)
func ServerHandlerErr(err error, where string){
if err != nil{
fmt.Println(err, where)
os.Exit(1)
}
}
func Processmsg(conn net.Conn){
defer conn.Close()
buff := make([]byte, 1024)
reader := bufio.NewReader(conn)
for{
n, err := reader.Read(buff)
if err != nil{
fmt.Println("Read from client failed,error is ", err)
break
}
if err == io.EOF{
break
}
recvStr := string(buff[0:n])
fmt.Println("Read from client message: ", recvStr)
}
}
func main(){
listen, err := net.Listen("tcp", "127.0.0.1:20000")
ServerHandlerErr(err, "net.Listen")
defer listen.Close()
for{
conn, listenerr := listen.Accept()
if listenerr != nil{
fmt.Println("accept failed,error is: ", listenerr)
continue
}
go Processmsg(conn)
}
}
Cilent端:
package main
import (
"fmt"
"net"
)
func main(){
conn, err := net.Dial("tcp", "127.0.0.1:20000")
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?`
conn.Write([]byte(msg))
}
}

稍微改动一下:每发送一句就延时一秒
for i := 0; i < 20; i++{
msg := `Hello, Hello. How are you?`
conn.Write([]byte(msg))
time.Sleep(time.Second)
}

解决粘包问题
出现粘包的关键在于接收方不确定将要传输的数据包的大小,因此我们可以对数据包进行封包和拆包的操作。
封包:封包就是给一段数据加上包头,这样一来数据包就分为了包头和包体两部分内容(过滤非法包时,封包会加入包尾内容)。包头部分的长度时固定的,并且它存储了包体的长度,根据包头长度固定以及包头中含有包体长度的变量就能正确的拆分一个完整的数据包。
我们可以自己定义一个协议,比如数据包的前4个字节为包头,里面存储的是发送的数据的长度。
版权声明:本文为qq_37466661原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。