在用golang开发人工客服系统的时候碰到了粘包问题,那么什么是粘包呢?例如咱们和客户端约定数据交互格式是一个json格式的字符串:golang
{"Id":1,"Name":"golang","Message":"message"}
当客户端发送数据给服务端的时候,若是服务端没有及时接收,客户端又发送了一条数据上来,这时候服务端才进行接收的话就会收到两个连续的字符串,形如:json
{"Id":1,"Name":"golang","Message":"message"}{"Id":1,"Name":"golang","Message":"message"}
若是接收缓冲区满了的话,那么也有可能接收到半截的json字符串,酱紫的话还怎么用json解码呢?真是头疼。如下用golang模拟了下这个粘包的产生。数组
备注:下面贴的代码都可以运行于golang 1.3.1,若是发现有问题能够联系我。app
server.gotcp
1spa 2code 3cdn 4server 5blog 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
|
client.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
运行后查看服务端输出:
golang粘包问题演示
能够看到json格式的字符串都粘到一块儿了,有种淡淡的忧伤了——头疼的事情又来了。
关于粘包的产生缘由网上有不少相关的说明,主要缘由就是tcp数据传递模式是流模式,在保持长链接的时候能够进行屡次的收和发。若是要深刻了解能够看看tcp协议方面的内容。这里推荐下鸟哥的私房菜,讲的很是通俗易懂。
主要有两种方法:
一、客户端发送一次就断开链接,须要发送数据的时候再次链接,典型如http。下面用golang演示一下这个过程,确实不会出现粘包问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
服务端代码参考上面演示粘包产生过程的服务端代码
二、包头+数据的格式,根据包头信息读取到须要分析的数据。形式以下图:
golang粘包问题包头定义
从数据流中读取数据的时候,只要根据包头和数据长度就能取到须要的数据。这个其实就是平时说的协议(protocol),只是这个数据传输协议很是简单,不像tcp、ip等协议有较多的定义。在实际的过程当中一般会定义协议类或者协议文件来封装封包和解包的过程。下面代码演示了封包和解包的过程:
protocol.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
|
tips:解包的过程当中要注意数组越界的问题;另外包头要注意惟一性。
server.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
|
client.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
运行这个程序能够看到服务端很好的获取到指望的json格式数据。完整代码演示下载:golang粘包问题解决示例
上面演示的两种方法适用于不一样的场景。第一种方法比较适合被动型的场景,例如打开网页,用户有请求才处理交互。第二种方法适合于主动推送的类型,例如即时聊天系统,由于要即时给用户推送消息,保持长链接是不可避免的,这时候就要用这种方法。