本文實(shí)例講述了golang網(wǎng)絡(luò)socket粘包問(wèn)題的解決方法。分享給大家供大家參考,具體如下:
看到很多人問(wèn)這個(gè)問(wèn)題, 今天就寫了個(gè)例子, 希望能幫助大家
首先說(shuō)一下什么是粘包:百度上比較通俗的說(shuō)法是指TCP協(xié)議中,發(fā)送方發(fā)送的若干包數(shù)據(jù)到接收方接收時(shí)粘成一包,從接收緩沖區(qū)看,后一包數(shù)據(jù)的頭緊接著前一包數(shù)據(jù)的尾。
解決方案如下:
服務(wù)端:
復(fù)制代碼 代碼如下:
package main
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)
// 消息長(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
}
}
}
}
客戶端:
復(fù)制代碼 代碼如下:
package main
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)
// 寫入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ì)有所幫助。
您可能感興趣的文章:- golang 實(shí)現(xiàn)tcp server端和client端,并計(jì)算RTT時(shí)間操作
- 使用Go基于WebSocket構(gòu)建千萬(wàn)級(jí)視頻直播彈幕系統(tǒng)的代碼詳解
- golang websocket 服務(wù)端的實(shí)現(xiàn)
- golang socket斷點(diǎn)續(xù)傳大文件的實(shí)現(xiàn)方法
- golang基于websocket實(shí)現(xiàn)的簡(jiǎn)易聊天室程序
- Golang 實(shí)現(xiàn)Socket服務(wù)端和客戶端使用TCP協(xié)議通訊