国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Go語言網(wǎng)絡(luò)編程(socket編程)TCP粘包

這篇具有很好參考價(jià)值的文章主要介紹了Go語言網(wǎng)絡(luò)編程(socket編程)TCP粘包。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

1、TCP粘包

服務(wù)端代碼如下:

// socket_stick/server/main.go

func process(conn net.Conn) {
    defer conn.Close()
    reader := bufio.NewReader(conn)
    var buf [1024]byte
    for {
        n, err := reader.Read(buf[:])
        if err == io.EOF {
            break
        }
        if err != nil {
            fmt.Println("read from client failed, err:", err)
            break
        }
        recvStr := string(buf[:n])
        fmt.Println("收到client發(fā)來的數(shù)據(jù):", recvStr)
    }
}

func main() {

    listen, err := net.Listen("tcp", "127.0.0.1:30000")
    if err != nil {
        fmt.Println("listen failed, err:", err)
        return
    }
    defer listen.Close()
    for {
        conn, err := listen.Accept()
        if err != nil {
            fmt.Println("accept failed, err:", err)
            continue
        }
        go process(conn)
    }
}

客戶端代碼如下:

// socket_stick/server/main.go

func process(conn net.Conn) {
    defer conn.Close()
    reader := bufio.NewReader(conn)
    var buf [1024]byte
    for {
        n, err := reader.Read(buf[:])
        if err == io.EOF {
            break
        }
        if err != nil {
            fmt.Println("read from client failed, err:", err)
            break
        }
        recvStr := string(buf[:n])
        fmt.Println("收到client發(fā)來的數(shù)據(jù):", recvStr)
    }
}

func main() {

    listen, err := net.Listen("tcp", "127.0.0.1:30000")
    if err != nil {
        fmt.Println("listen failed, err:", err)
        return
    }
    defer listen.Close()
    for {
        conn, err := listen.Accept()
        if err != nil {
            fmt.Println("accept failed, err:", err)
            continue
        }
        go process(conn)
    }
}

將上面的代碼保存后,分別編譯。先啟動(dòng)服務(wù)端再啟動(dòng)客戶端,可以看到服務(wù)端輸出結(jié)果如下:

收到client發(fā)來的數(shù)據(jù): Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?
收到client發(fā)來的數(shù)據(jù): Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?
收到client發(fā)來的數(shù)據(jù): Hello, Hello. How are you?Hello, Hello. How are you?
收到client發(fā)來的數(shù)據(jù): Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?
收到client發(fā)來的數(shù)據(jù): Hello, Hello. How are you?Hello, Hello. How are you?

客戶端分10次發(fā)送的數(shù)據(jù),在服務(wù)端并沒有成功的輸出10次,而是多條數(shù)據(jù)“粘”到了一起。

1.1.1 為什么出現(xiàn)粘包

主要原因就是tcp數(shù)據(jù)傳遞模式是流模式,在保持長(zhǎng)連接的時(shí)候可以進(jìn)行多次的收和發(fā)。

“粘包”可發(fā)生在發(fā)送端也可發(fā)生在接收端:

1.由Nagle算法造成的發(fā)送端的粘包:Nagle算法是一種改善網(wǎng)絡(luò)傳輸效率的算法。簡(jiǎn)單來說就是當(dāng)我們提交一段數(shù)據(jù)給TCP發(fā)送時(shí),TCP并不立刻發(fā)送此段數(shù)據(jù),而是等待一小段時(shí)間看看在等待期間是否還有要發(fā)送的數(shù)據(jù),若有則會(huì)一次把這兩段數(shù)據(jù)發(fā)送出去。
2.接收端接收不及時(shí)造成的接收端粘包:TCP會(huì)把接收到的數(shù)據(jù)存在自己的緩沖區(qū)中,然后通知應(yīng)用層取數(shù)據(jù)。當(dāng)應(yīng)用層由于某些原因不能及時(shí)的把TCP的數(shù)據(jù)取出來,就會(huì)造成TCP緩沖區(qū)中存放了幾段數(shù)據(jù)。

1.1.2 解決辦法

出現(xiàn)”粘包”的關(guān)鍵在于接收方不確定將要傳輸?shù)臄?shù)據(jù)包的大小,因此我們可以對(duì)數(shù)據(jù)包進(jìn)行封包和拆包的操作。

封包:封包就是給一段數(shù)據(jù)加上包頭,這樣一來數(shù)據(jù)包就分為包頭和包體兩部分內(nèi)容了(過濾非法包時(shí)封包會(huì)加入”包尾”內(nèi)容)。包頭部分的長(zhǎng)度是固定的,并且它存儲(chǔ)了包體的長(zhǎng)度,根據(jù)包頭長(zhǎng)度固定以及包頭中含有包體長(zhǎng)度的變量就能正確的拆分出一個(gè)完整的數(shù)據(jù)包。

我們可以自己定義一個(gè)協(xié)議,比如數(shù)據(jù)包的前4個(gè)字節(jié)為包頭,里面存儲(chǔ)的是發(fā)送的數(shù)據(jù)的長(zhǎng)度。

// socket_stick/proto/proto.go
package proto

import (
    "bufio"
    "bytes"
    "encoding/binary"
)

// Encode 將消息編碼
func Encode(message string) ([]byte, error) {
    // 讀取消息的長(zhǎng)度,轉(zhuǎn)換成int32類型(占4個(gè)字節(jié))
    var length = int32(len(message))
    var pkg = new(bytes.Buffer)
    // 寫入消息頭
    err := binary.Write(pkg, binary.LittleEndian, length)
    if err != nil {
        return nil, err
    }
    // 寫入消息實(shí)體
    err = binary.Write(pkg, binary.LittleEndian, []byte(message))
    if err != nil {
        return nil, err
    }
    return pkg.Bytes(), nil
}

// Decode 解碼消息
func Decode(reader *bufio.Reader) (string, error) {
    // 讀取消息的長(zhǎng)度
    lengthByte, _ := reader.Peek(4) // 讀取前4個(gè)字節(jié)的數(shù)據(jù)
    lengthBuff := bytes.NewBuffer(lengthByte)
    var length int32
    err := binary.Read(lengthBuff, binary.LittleEndian, &length)
    if err != nil {
        return "", err
    }
    // Buffered返回緩沖中現(xiàn)有的可讀取的字節(jié)數(shù)。
    if int32(reader.Buffered()) < length+4 {
        return "", err
    }

    // 讀取真正的消息數(shù)據(jù)
    pack := make([]byte, int(4+length))
    _, err = reader.Read(pack)
    if err != nil {
        return "", err
    }
    return string(pack[4:]), nil
}

接下來在服務(wù)端和客戶端分別使用上面定義的proto包的Decode和Encode函數(shù)處理數(shù)據(jù)。

服務(wù)端代碼如下:

// socket_stick/server2/main.go

func process(conn net.Conn) {
    defer conn.Close()
    reader := bufio.NewReader(conn)
    for {
        msg, err := proto.Decode(reader)
        if err == io.EOF {
            return
        }
        if err != nil {
            fmt.Println("decode msg failed, err:", err)
            return
        }
        fmt.Println("收到client發(fā)來的數(shù)據(jù):", msg)
    }
}

func main() {

    listen, err := net.Listen("tcp", "127.0.0.1:30000")
    if err != nil {
        fmt.Println("listen failed, err:", err)
        return
    }
    defer listen.Close()
    for {
        conn, err := listen.Accept()
        if err != nil {
            fmt.Println("accept failed, err:", err)
            continue
        }
        go process(conn)
    }
}

客戶端代碼如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-705236.html

// socket_stick/client2/main.go

func main() {
    conn, err := net.Dial("tcp", "127.0.0.1:30000")
    if err != nil {
        fmt.Println("dial failed, err", err)
        return
    }
    defer conn.Close()
    for i := 0; i < 20; i++ {
        msg := `Hello, Hello. How are you?`
        data, err := proto.Encode(msg)
        if err != nil {
            fmt.Println("encode msg failed, err:", err)
            return
        }
        conn.Write(data)
    }
}

到了這里,關(guān)于Go語言網(wǎng)絡(luò)編程(socket編程)TCP粘包的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Go語言實(shí)戰(zhàn):網(wǎng)絡(luò)編程與TCP_UDP

    Go語言是一種現(xiàn)代的編程語言,由Google的Robert Griesemer、Rob Pike和Ken Thompson于2009年開發(fā)。Go語言的設(shè)計(jì)目標(biāo)是簡(jiǎn)單、高效、可擴(kuò)展和易于使用。它具有垃圾回收、類型安全、并發(fā)性能等優(yōu)點(diǎn)。Go語言的網(wǎng)絡(luò)編程庫(kù) net 包提供了TCP/UDP的實(shí)現(xiàn),使得開發(fā)者可以輕松地編寫網(wǎng)絡(luò)應(yīng)用程序

    2024年02月21日
    瀏覽(96)
  • Go語言的網(wǎng)絡(luò)編程與TCP_IP

    Go語言是一種現(xiàn)代的編程語言,由Google的Robert Griesemer、Rob Pike和Ken Thompson在2009年開發(fā)。Go語言的設(shè)計(jì)目標(biāo)是簡(jiǎn)單、高效、可擴(kuò)展和易于使用。它具有弱類型、垃圾回收、并發(fā)性和原生支持的網(wǎng)絡(luò)編程。Go語言的網(wǎng)絡(luò)編程是其強(qiáng)大功能之一,它使得開發(fā)者可以輕松地編寫高性能的

    2024年02月22日
    瀏覽(115)
  • Go語言網(wǎng)絡(luò)編程入門:TCP、HTTP、JSON序列化、Gin、WebSocket、RPC、gRPC示例

    Go語言網(wǎng)絡(luò)編程入門:TCP、HTTP、JSON序列化、Gin、WebSocket、RPC、gRPC示例

    在本文中,我們將介紹Go語言中的網(wǎng)絡(luò)編程的不同方式,包括TCP、HTTP、Gin框架、WebSocket、RPC、gRPC的介紹與連接實(shí)例,并對(duì)所有示例代碼都給出了詳細(xì)的注釋,最后對(duì)每種模式進(jìn)行了總結(jié)。 TCP(傳輸控制協(xié)議)是一種面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議,提供

    2024年02月16日
    瀏覽(28)
  • Go語言TCP Socket編程

    Golang的主要 設(shè)計(jì)目標(biāo)之一就是面向大規(guī)模后端服務(wù)程序,網(wǎng)絡(luò)通信這塊是服務(wù)端 程序必不可少也是至關(guān)重要的一部分。在日常應(yīng)用中,我們也可以看到Go中的net以及其subdirectories下的包均是“高頻+剛需”,而TCP socket則是網(wǎng)絡(luò)編程的主流,即便您沒有直接使用到net中有關(guān)TCP

    2024年02月15日
    瀏覽(20)
  • GO語言網(wǎng)絡(luò)編程(并發(fā)編程)Channel

    GO語言網(wǎng)絡(luò)編程(并發(fā)編程)Channel

    1.1.1 Channel 單純地將函數(shù)并發(fā)執(zhí)行是沒有意義的。函數(shù)與函數(shù)間需要交換數(shù)據(jù)才能體現(xiàn)并發(fā)執(zhí)行函數(shù)的意義。 雖然可以使用共享內(nèi)存進(jìn)行數(shù)據(jù)交換,但是共享內(nèi)存在不同的goroutine中容易發(fā)生競(jìng)態(tài)問題。為了保證數(shù)據(jù)交換的正確性,必須使用互斥量對(duì)內(nèi)存進(jìn)行加鎖,這種做法勢(shì)

    2024年02月09日
    瀏覽(104)
  • GO語言網(wǎng)絡(luò)編程(并發(fā)編程)select

    1.1.1 select多路復(fù)用 在某些場(chǎng)景下我們需要同時(shí)從多個(gè)通道接收數(shù)據(jù)。通道在接收數(shù)據(jù)時(shí),如果沒有數(shù)據(jù)可以接收將會(huì)發(fā)生阻塞。你也許會(huì)寫出如下代碼使用遍歷的方式來實(shí)現(xiàn): 這種方式雖然可以實(shí)現(xiàn)從多個(gè)通道接收值的需求,但是運(yùn)行性能會(huì)差很多。為了應(yīng)對(duì)這種場(chǎng)景,G

    2024年02月09日
    瀏覽(239)
  • GO語言網(wǎng)絡(luò)編程(并發(fā)編程)runtime包

    1.1.1. runtime.Gosched() 讓出CPU時(shí)間片,重新等待安排任務(wù)(大概意思就是本來計(jì)劃的好好的周末出去燒烤,但是你媽讓你去相親,兩種情況第一就是你相親速度非常快,見面就黃不耽誤你繼續(xù)燒烤,第二種情況就是你相親速度特別慢,見面就是你儂我儂的,耽誤了燒烤,但是還饞就

    2024年02月09日
    瀏覽(122)
  • GO語言網(wǎng)絡(luò)編程(并發(fā)編程)并發(fā)介紹,Goroutine

    GO語言網(wǎng)絡(luò)編程(并發(fā)編程)并發(fā)介紹,Goroutine

    進(jìn)程和線程 并發(fā)和并行 協(xié)程和線程 協(xié)程:獨(dú)立的??臻g,共享堆空間,調(diào)度由用戶自己控制,本質(zhì)上有點(diǎn)類似于用戶級(jí)線程,這些用戶級(jí)線程的調(diào)度也是自己實(shí)現(xiàn)的。 線程:一個(gè)線程上可以跑多個(gè)協(xié)程,協(xié)程是輕量級(jí)的線程。 goroutine 只是由官方實(shí)現(xiàn)的超級(jí)\\\"線程池\\\"。 每個(gè)

    2024年02月09日
    瀏覽(92)
  • Go語言網(wǎng)絡(luò)編程介紹以及案例運(yùn)用

    1. 基本概念 TCP 和 UDP : Go語言支持TCP(傳輸控制協(xié)議)和UDP(用戶數(shù)據(jù)報(bào)協(xié)議)。TCP提供可靠的、面向連接的通信,而UDP提供無連接的快速數(shù)據(jù)傳輸。 并發(fā) : Go語言的并發(fā)模型是通過goroutines實(shí)現(xiàn)的。每個(gè)網(wǎng)絡(luò)請(qǐng)求都可以在自己的goroutine中處理,實(shí)現(xiàn)高效的并發(fā)。 Channels : 用于

    2024年01月25日
    瀏覽(105)
  • Go語言的網(wǎng)絡(luò)編程與HTTP服務(wù)

    Go語言(Golang)是Google開發(fā)的一種靜態(tài)類型、垃圾回收、并發(fā)簡(jiǎn)單的編程語言。Go語言的設(shè)計(jì)目標(biāo)是讓程序員更容易編寫并發(fā)程序,并在多核處理器上充分發(fā)揮性能。Go語言的網(wǎng)絡(luò)編程和HTTP服務(wù)是其核心功能之一,可以輕松地構(gòu)建高性能、可擴(kuò)展的網(wǎng)絡(luò)應(yīng)用程序。 在本文中,我們

    2024年02月19日
    瀏覽(100)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包