在用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粘包問題解決示例
上面演示的兩種方法適用於不一樣的場景。第一種方法比較適合被動型的場景,例如打開網頁,用戶有請求才處理交互。第二種方法適合於主動推送的類型,例如即時聊天系統,由於要即時給用戶推送消息,保持長鏈接是不可避免的,這時候就要用這種方法。