golang網(wǎng)絡(luò)socket粘包問(wèn)題的解決方法
本文實(shí)例講述了golang網(wǎng)絡(luò)socket粘包問(wèn)題的解決方法。分享給大家供大家參考,具體如下:
看到很多人問(wèn)這個(gè)問(wèn)題, 今天就寫(xiě)了個(gè)例子, 希望能幫助大家
首先說(shuō)一下什么是粘包:百度上比較通俗的說(shuō)法是指TCP協(xié)議中,發(fā)送方發(fā)送的若干包數(shù)據(jù)到接收方接收時(shí)粘成一包,從接收緩沖區(qū)看,后一包數(shù)據(jù)的頭緊接著前一包數(shù)據(jù)的尾。
解決方案如下:
服務(wù)端:
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"net"
)
func main() {
// 監(jiān)聽(tīng)端口
ln, err := net.Listen("tcp", ":6000")
if err != nil {
fmt.Printf("Listen Error: %s\n", err)
return
}
// 監(jiān)聽(tīng)循環(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)
// 消息長(zhǎng)度
length := 0
// 消息長(zhǎng)度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)
// 檢查超長(zhǎng)消息
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的消息")
// 消息長(zhǎng)度
messageLen := uint32(len(message))
// 消息總長(zhǎng)度
mlen := 4 + len(message)
// 寫(xiě)入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()
}
希望本文所述對(duì)大家Go語(yǔ)言程序設(shè)計(jì)有所幫助。
相關(guān)文章
Go調(diào)度器學(xué)習(xí)之協(xié)作與搶占詳解
如果某個(gè)G執(zhí)行時(shí)間過(guò)長(zhǎng),其他的G如何才能被正常調(diào)度,這就引出了接下來(lái)的話題:協(xié)作與搶占。本文將通過(guò)一些示例為大家詳細(xì)講講調(diào)度器中協(xié)作與搶占的相關(guān)知識(shí),需要的可以參考一下2023-04-04利用Go語(yǔ)言實(shí)現(xiàn)Raft日志同步
這篇文章主要為大家詳細(xì)介紹了如何利用Go語(yǔ)言實(shí)現(xiàn)Raft日志同步,文中的示例代碼講解詳細(xì),對(duì)我們深入了解Go語(yǔ)言有一定的幫助,需要的可以參考一下2023-05-05go語(yǔ)言通過(guò)odbc操作Access數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了go語(yǔ)言通過(guò)odbc操作Access數(shù)據(jù)庫(kù)的方法,實(shí)例分析了Go語(yǔ)言通過(guò)odbc連接、查詢與關(guān)閉access數(shù)據(jù)庫(kù)的技巧,需要的朋友可以參考下2015-03-03淺談?dòng)肎o構(gòu)建不可變的數(shù)據(jù)結(jié)構(gòu)的方法
這篇文章主要介紹了用Go構(gòu)建不可變的數(shù)據(jù)結(jié)構(gòu)的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09解決goland新建項(xiàng)目文件名為紅色的問(wèn)題
這篇文章主要介紹了解決goland新建項(xiàng)目文件名為紅色的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12go語(yǔ)言實(shí)現(xiàn)簡(jiǎn)易比特幣系統(tǒng)錢(qián)包的原理解析
這篇文章主要介紹了go語(yǔ)言實(shí)現(xiàn)簡(jiǎn)易比特幣系統(tǒng)錢(qián)包的原理解析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04golang sql語(yǔ)句超時(shí)控制方案及原理
一般應(yīng)用程序在執(zhí)行一條sql語(yǔ)句時(shí),都會(huì)給這條sql設(shè)置一個(gè)超時(shí)時(shí)間,本文主要介紹了golang sql語(yǔ)句超時(shí)控制方案及原理,具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12