golang網(wǎng)絡(luò)socket粘包問題的解決方法
本文實例講述了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基于struct?tag實現(xiàn)結(jié)構(gòu)體字段級別的訪問控制
本文將會基于這個主題展開,討論Go中的結(jié)構(gòu)體tag究竟是什么,我們該如何利用它,另外,文末還提供了一個實際案例,實現(xiàn)結(jié)構(gòu)體字段級別的訪問,幫助我們進(jìn)一步提升對struct tag的理解2024-02-02
Golang泛型實現(xiàn)類型轉(zhuǎn)換的方法實例
將一個值從一種類型轉(zhuǎn)換到另一種類型,便發(fā)生了類型轉(zhuǎn)換,下面這篇文章主要給大家介紹了關(guān)于Golang泛型實現(xiàn)類型轉(zhuǎn)換的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12
解決Go語言中高頻次和高并發(fā)下隨機(jī)數(shù)重復(fù)的問題
在Golang中,獲取隨機(jī)數(shù)的方法一般會介紹有兩種,一種是基于math/rand的偽隨機(jī),一種是基于crypto/rand的真隨機(jī),math/rand由于其偽隨機(jī)的原理,經(jīng)常會出現(xiàn)重復(fù)的隨機(jī)數(shù),導(dǎo)致在需要進(jìn)行隨機(jī)的業(yè)務(wù)出現(xiàn)較多的重復(fù)問題,所以本文給大家介紹了較好的解放方案2023-12-12
go語言使用Chromedp實現(xiàn)二維碼登陸教程示例源碼
這篇文章主要為大家介紹了go語言使用Chromedp實現(xiàn)二維碼登陸示例源碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
Go語言學(xué)習(xí)之golang-jwt/jwt的教程分享
jwt是?json?web?token的簡稱。go使用jwt目前,主流使用的jwt庫是golang-jwt/jwt。本文就來和大家講講golang-jwt/jwt的具體使用,需要的可以參考一下2023-01-01

