在用golang开发人工客服系统的时候碰到了粘包问题,那么什么是粘包呢?例如我们和客户端约定数据交互格式是一个json格式的字符串:
{"Id":1,"Name":"golang","Message":"message"}
当客户端发送数据给服务端的时候,如果服务端没有及时接收,客户端又发送了一条数据上来,这时候服务端才进行接收的话就会收到两个连续的字符串,形如:
如果接收缓冲区满了的话,那么也有可能接收到半截的json字符串,酱紫的话还怎么用json解码呢?真是头疼。以下用golang模拟了下这个粘包的产生。
备注:下面贴的代码均可以运行于golang 1.3.1,如果发现有问题可以联系我。
粘包示例
//server.go
//粘包问题演示服务端
package mainimport ("fmt"
"net"
"os"
"time"
)func main() {netListen, err := net.Listen("tcp", ":9988")CheckError(err)defer netListen.Close()Log("Waiting for clients")for {conn, err := netListen.Accept()if err != nil {continue
}Log(conn.RemoteAddr().String(), " tcp connect success")go handleConnection(conn)}}func handleConnection(conn net.Conn) {time.Sleep(time.Second * 30) //TCP连接建立好后,使服务器休眠30s,然后再进行Read操作(这里只是为了演示粘包问题)buffer := make([]byte, 1024)for {n, err := conn.Read(buffer)if err != nil {Log(conn.RemoteAddr().String(), " connection error: ", err)return
}Log(conn.RemoteAddr().String(), "receive data length:", n)Log(conn.RemoteAddr().String(), "receive data:", buffer[:n])Log(conn.RemoteAddr().String(), "receive data string:", string(buffer[:n]))}}func Log(v ...interface{}) {fmt.Println(v...)}func CheckError(err error) {if err != nil {fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())os.Exit(1)
}}//client.go
//粘包问题演示客户端
package mainimport ("fmt"
"net"
"os"
"time"
)func sender(conn net.Conn) {for i := 0; i < 20; i++ {words := "{\"Id\":1,\"Name\":\"golang\",\"Message\":\"message\"}"conn.Write([]byte(words))
}}func main() {server := "127.0.0.1:9988"tcpAddr, err := net.ResolveTCPAddr("tcp4", server)if err != nil {fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}conn, err := net.DialTCP("tcp", nil, tcpAddr)if err != nil {fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}defer conn.Close()fmt.Println("connect success")go sender(conn)for {time.Sleep(1 * 1e9)}}运行后查看服务端输出:
C:/go/bin/go.exe run server.go [E:/project/go/proj/src/test]
Waiting for clients
127.0.0.1:50718 tcp connect success
127.0.0.1:50718 receive data length: 880
127.0.0.1:50718 receive data: [123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125 123 34 73 100 34 58 49 44 34 78 97 109 101 34 58 34 103 111 108 97 110 103 34 44 34 77 101 115 115 97 103 101 34 58 34 109 101 115 115 97 103 101 34 125]
127.0.0.1:50718 receive data string: {"Id":1,"Message":"message"}
可以看到,客户端每次发送的json格式的字符串都粘到一起了,有种淡淡的忧伤了——头疼的事情又来了。
粘包产生原因:
关于粘包的产生原因网上有很多相关的说明,主要原因就是tcp数据传递模式是流模式,在保持长连接的时候可以进行多次的收和发。如果要深入了解可以看看tcp协议方面的内容。
1、客户端发送一次就断开连接,需要发送数据的时候再次连接,典型如http。下面用golang演示一下这个过程,确实不会出现粘包问题。
...