golang網(wǎng)絡(luò)socket粘包問題的解決方法
本文實(shí)例講述了golang網(wǎng)絡(luò)socket粘包問題的解決方法。分享給大家供大家參考,具體如下:
看到很多人問這個問題, 今天就寫了個例子, 希望能幫助大家
首先說一下什么是粘包:百度上比較通俗的說法是指TCP協(xié)議中,發(fā)送方發(fā)送的若干包數(shù)據(jù)到接收方接收時粘成一包,從接收緩沖區(qū)看,后一包數(shù)據(jù)的頭緊接著前一包數(shù)據(jù)的尾。
解決方案如下:
服務(wù)端:
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"net"
)
func main() {
// 監(jiān)聽端口
ln, err := net.Listen("tcp", ":6000")
if err != nil {
fmt.Printf("Listen Error: %s\n", err)
return
}
// 監(jiān)聽循環(huán)
for {
// 接受客戶端鏈接
conn, err := ln.Accept()
if err != nil {
fmt.Printf("Accept Error: %s\n", err)
continue
}
// 處理客戶端鏈接
go handleConnection(conn)
}
}
func handleConnection(conn net.Conn) {
// 關(guān)閉鏈接
defer conn.Close()
// 客戶端
fmt.Printf("Client: %s\n", conn.RemoteAddr())
// 消息緩沖
msgbuf := bytes.NewBuffer(make([]byte, 0, 10240))
// 數(shù)據(jù)緩沖
databuf := make([]byte, 4096)
// 消息長度
length := 0
// 消息長度uint32
ulength := uint32(0)
// 數(shù)據(jù)循環(huán)
for {
// 讀取數(shù)據(jù)
n, err := conn.Read(databuf)
if err == io.EOF {
fmt.Printf("Client exit: %s\n", conn.RemoteAddr())
}
if err != nil {
fmt.Printf("Read error: %s\n", err)
return
}
fmt.Println(databuf[:n])
// 數(shù)據(jù)添加到消息緩沖
n, err = msgbuf.Write(databuf[:n])
if err != nil {
fmt.Printf("Buffer write error: %s\n", err)
return
}
// 消息分割循環(huán)
for {
// 消息頭
if length == 0 && msgbuf.Len() >= 4 {
binary.Read(msgbuf, binary.LittleEndian, &ulength)
length = int(ulength)
// 檢查超長消息
if length > 10240 {
fmt.Printf("Message too length: %d\n", length)
return
}
}
// 消息體
if length > 0 && msgbuf.Len() >= length {
fmt.Printf("Client messge: %s\n", string(msgbuf.Next(length)))
length = 0
} else {
break
}
}
}
}
客戶端:
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"time"
)
func main() {
// 鏈接服務(wù)器
conn, err := net.Dial("tcp", "127.0.0.1:6000")
if err != nil {
fmt.Printf("Dial error: %s\n", err)
return
}
// 客戶端信息
fmt.Printf("Client: %s\n", conn.LocalAddr())
// 消息緩沖
msgbuf := bytes.NewBuffer(make([]byte, 0, 1024))
// 消息內(nèi)容
message := []byte("我是utf-8的消息")
// 消息長度
messageLen := uint32(len(message))
// 消息總長度
mlen := 4 + len(message)
// 寫入5條消息
for i := 0; i < 10; i++ {
binary.Write(msgbuf, binary.LittleEndian, messageLen)
msgbuf.Write(message)
}
// 單包發(fā)送一條消息
conn.Write(msgbuf.Next(mlen))
time.Sleep(time.Second)
// 單包發(fā)送三條消息
conn.Write(msgbuf.Next(mlen * 3))
time.Sleep(time.Second)
// 發(fā)送不完整的消息頭
conn.Write(msgbuf.Next(2))
time.Sleep(time.Second)
// 發(fā)送消息剩下部分
conn.Write(msgbuf.Next(mlen - 2))
time.Sleep(time.Second)
// 發(fā)送不完整的消息體
conn.Write(msgbuf.Next(mlen - 6))
time.Sleep(time.Second)
// 發(fā)送消息剩下部分
conn.Write(msgbuf.Next(6))
time.Sleep(time.Second)
// 多段發(fā)送
conn.Write(msgbuf.Next(mlen + 2))
time.Sleep(time.Second)
conn.Write(msgbuf.Next(-2 + mlen - 8))
time.Sleep(time.Second)
conn.Write(msgbuf.Next(8 + 1))
time.Sleep(time.Second)
conn.Write(msgbuf.Next(-1 + mlen + mlen))
time.Sleep(time.Second)
// 關(guān)閉鏈接
conn.Close()
}
希望本文所述對大家Go語言程序設(shè)計有所幫助。
相關(guān)文章
Go調(diào)度器學(xué)習(xí)之協(xié)作與搶占詳解
如果某個G執(zhí)行時間過長,其他的G如何才能被正常調(diào)度,這就引出了接下來的話題:協(xié)作與搶占。本文將通過一些示例為大家詳細(xì)講講調(diào)度器中協(xié)作與搶占的相關(guān)知識,需要的可以參考一下2023-04-04go語言通過odbc操作Access數(shù)據(jù)庫的方法
這篇文章主要介紹了go語言通過odbc操作Access數(shù)據(jù)庫的方法,實(shí)例分析了Go語言通過odbc連接、查詢與關(guān)閉access數(shù)據(jù)庫的技巧,需要的朋友可以參考下2015-03-03淺談用Go構(gòu)建不可變的數(shù)據(jù)結(jié)構(gòu)的方法
這篇文章主要介紹了用Go構(gòu)建不可變的數(shù)據(jù)結(jié)構(gòu)的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09go語言實(shí)現(xiàn)簡易比特幣系統(tǒng)錢包的原理解析
這篇文章主要介紹了go語言實(shí)現(xiàn)簡易比特幣系統(tǒng)錢包的原理解析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04