首頁 > 後端開發 > Golang > 主體

一篇文章帶你了解Go語言基礎之網絡編程

發布: 2023-07-21 16:19:19
轉載
908 人瀏覽過
socker程式設計

我們所學的TCPUDP,統稱為Socker編程,也稱為套接字程式設計

多台機器#要實作互相通訊#,其實是一個很複雜的過程,底層從鋪設網路線網路線介面交換器路由器,在到規定各種協定

到應用層QQ微信等軟體.

如果沒有一套標準,每次使用都要自己去實現,可能每個程式設計師都不是掉頭髮那麼簡單了!

一篇文章帶你了解Go語言基礎之網絡編程

有了#Socker之後, Socker會在應用層之前,將各種繁瑣的的底層操作隱藏,我們可能只需要Socker.TCP就實作了TCP協定的通訊。

#

Go語言TCP

TCP屬於穩定的,可靠的長連接,

既然要涉及通訊,必然有兩個終端,最起碼一個是服務端,一個是#客戶端,就像我們的淘寶,我們每次打開淘寶,都要去連結它,當然,淘寶可不直接是##TCP

服務端

在Go中實作服務端,並且在服務端並發很簡單,只需要將每個連線讓一個協程處理即可!

程式碼

package main


import (
    "bufio"
    "fmt"
    "net"
)


func process(conn net.Conn) {
    defer conn.Close()
    for {
        reader := bufio.NewReader(conn)
        buf := make([]byte, 128)
        n, err := reader.Read(buf)
        if err != nil {
            fmt.Println("数据读取失败", err)
            return
        }
        recvStr := string(buf[:n])
        fmt.Println("客户端发送过来的值:", recvStr)
}


}
func main() {
    lister, err := net.Listen("tcp", "0.0.0.0:8008")
    if err != nil {
        fmt.Println("连接失败", err)
}
    for {
        fmt.Println("等待建立连接,此时会阻塞住")
        conn, err := lister.Accept() //等待建立连接
        fmt.Println("连接建立成功,继续")
        if err != nil {
            fmt.Println("建立连接失败", err)
            //继续监听下次链接
            continue
        }
        go process(conn)
}
}
登入後複製

#客戶端

##客戶端就很簡單了,相對來說是不需要並發的,只要連接就行。

代码

package main


import (
    "bufio"
    "fmt"
    "net"
    "os"
)


//客户端
func main() {
    conn, err := net.Dial("tcp", "192.168.10.148:8008")
    if err != nil {
        fmt.Println("连接服务器失败",err)
}
    defer conn.Close()
    inputReader:=bufio.NewReader(os.Stdin)
    for{
        fmt.Println(":")
        input,_:=inputReader.ReadString('\n')
        _, err = conn.Write([]byte(input))
        if err != nil {
            fmt.Println("发送成功")
        }
}
}
登入後複製

执行结果

就这样,我们实现了服务端并发的处理所有客户端的请求。

一篇文章帶你了解Go語言基礎之網絡編程

粘包

我们先看一下什么是粘包。

服务端

package main


import (
    "bufio"
    "fmt"
    "io"
    "net"
)


func process(conn net.Conn) {
    defer conn.Close()
    reader := bufio.NewReader(conn)
    buf := make([]byte, 1024)
    for {
        n, err := reader.Read(buf)
        //读完了
        if err == io.EOF {
            fmt.Println("读完了")
            break
        }
        //读错了
        if err != nil {
            fmt.Println("数据读取失败", err)
            return
        }
        recvStr := string(buf[:n])
        fmt.Println("客户端发送过来的值:", recvStr)
}


}
func main() {
    lister, err := net.Listen("tcp", "0.0.0.0:8008")
    if err != nil {
        fmt.Println("连接失败", err)
        return
}
    defer lister.Close()
    for {
        fmt.Println("等待建立连接,此时会阻塞住")
        conn, err := lister.Accept() //等待建立连接
        fmt.Println("连接建立成功,继续")
        if err != nil {
            fmt.Println("建立连接失败", err)
            //继续监听下次链接
            continue
        }
        go process(conn)
}
}
登入後複製

客户端

package main


import (
    "fmt"
    "net"
)


//客户端
func main() {
    conn, err := net.Dial("tcp", "192.168.10.148:8008")
    if err != nil {
        fmt.Println("连接服务器失败", err)
}
    defer conn.Close()
    for i := 0; i < 10; i++ {
        sendStr := "hello world ads asdf asd fads fadsf ads ads asd asd ads "
        conn.Write([]byte(sendStr))
        time.Sleep(time.Second)
}
}
登入後複製

注意:18行代码睡眠了1s

执行结果

一篇文章帶你了解Go語言基礎之網絡編程

如果我註解了第18行程式碼

一篇文章帶你了解Go語言基礎之網絡編程

#執行結果

一篇文章帶你了解Go語言基礎之網絡編程

直接都淦到一行了,what?這是啥情況,不應該跟原來一樣嗎? ? ?

每發送一個值,那邊就接收一下,這怎麼整到一塊了! ! !

原因

主要原因是因為我們是##應用層軟體,是跑在作業系統之上的軟體,當我們向伺服器發送一個資料時,是呼叫作業系統相關介面#發送的,作業系統再經過各種複雜的操作,傳送到對方機器

但是作業系統有一個傳送資料緩衝區,預設如果緩衝區是有大小的,如果緩衝區沒滿,是不會傳送資料的,所以上述客戶端在傳送資料時,系統的緩衝區都沒滿,一直壓在了作業系統的緩衝區中,最後發現沒資料了,才一次都傳送到服務端#

但是為什麼sleep(1)又管用了呢?這是因為緩衝區不只一個程式在用,1s的時間足夠其他程式將緩衝區打滿,然後各自發各自的數據,這也是為什麼第一次操作沒問題,第二次有問題,因為第二次全部都是我們客戶端打滿的

一篇文章帶你了解Go語言基礎之網絡編程

解決黏包

工具函數

我們將解套件封包的函數封裝一下

socker_sitck/stick.go

一篇文章帶你了解Go語言基礎之網絡編程

package socker_stick


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


//Encode 将消息编码
func Encode(message string) ([]byte, error) {
    length := int32(len(message))
    var pkg = new(bytes.Buffer)
    //写入消息头
    err := binary.Write(pkg, binary.LittleEndian, length)
    if err != nil {
        fmt.Println("写入消息头失败", err)
        return nil, err
}
    //写入消息实体
    err = binary.Write(pkg, binary.LittleEndian, []byte(message))
    if err != nil {
        fmt.Println("写入消息实体失败", err)
        return nil, err
}
    return pkg.Bytes(), nil
}


//Decode解码消息
func Decode(reader *bufio.Reader) (string, error) {
    //读取信息长度
    lengthByte, _ := reader.Peek(4)
    lengthBuff := bytes.NewBuffer(lengthByte)
    var length int32
    err := binary.Read(lengthBuff, binary.LittleEndian, &length)
    if err != nil {
        return "", err
}
    //BuffRead 返回缓冲区现有的可读的字节数
    if int32(reader.Buffered()) < length+4 {
        return "", err
}
    pack := make([]byte, int(4+length))
    _, err = reader.Read(pack)
    if err != nil {
        return "", err
}
    return string(pack[4:]), nil
}
登入後複製

服务端

package main


import (
    "a3_course/socker_stick"
    "bufio"
    "fmt"
    "io"
    "net"
)


func process(conn net.Conn) {
    defer conn.Close()
    reader := bufio.NewReader(conn)


    for {
        msg, err := socker_stick.Decode(reader)
        //读完了
        if err == io.EOF {
            fmt.Println("读完了")
            break
        }
        //读错了
        if err != nil {
            fmt.Println("数据读取失败", err)
            return
        }


        fmt.Println("客户端发送过来的值:", msg)
}


}
func main() {
    lister, err := net.Listen("tcp", "0.0.0.0:8008")
    if err != nil {
        fmt.Println("连接失败", err)
        return
}
    defer lister.Close()
    for {
        fmt.Println("等待建立连接,此时会阻塞住")
        conn, err := lister.Accept() //等待建立连接
        fmt.Println("连接建立成功,继续")
        if err != nil {
            fmt.Println("建立连接失败", err)
            //继续监听下次链接
            continue
        }
        go process(conn)
}
}
登入後複製

客户端

package main


import (
    "a3_course/socker_stick"
    "fmt"
    "net"
)


//客户端
func main() {
    conn, err := net.Dial("tcp", "192.168.10.148:8008")
    if err != nil {
        fmt.Println("连接服务器失败", err)
}
    defer conn.Close()
    for i := 0; i < 10; i++ {
        sendStr := "hello world ads asdf asd fads fadsf ads ads asd asd ads "
        data, err := socker_stick.Encode(sendStr)
        if err != nil {
            fmt.Println("编码失败",err)
            return
        }
        conn.Write(data)
        //time.Sleep(time.Second)
}
}
登入後複製

执行结果

一篇文章帶你了解Go語言基礎之網絡編程

这次真的不管执行几次,都是这样的结果

对了,只有TCP才有粘包

Go语言UDP

UDP是一个无连接协议,客户端不会在乎服务端有没有问题,客户端只管发,通常用于实时性比较高的领域

例如直播行业

服务端

package main


import (
    "fmt"
    "net"
)


func main() {
    listen, err := net.ListenUDP("udp", &net.UDPAddr{
        IP:   net.IPv4(0, 0, 0, 0),
        Port: 8009,
})
    if err != nil {
        panic(fmt.Sprintf("udp启动失败,err:%v", err))
}
    defer listen.Close()
    for{
        var data = make([]byte,1024)
        n, addr, err := listen.ReadFromUDP(data)
        if err != nil {
            panic(fmt.Sprintf("读取数据失败,err:%v", err))
        }
        fmt.Println(string(data[:n]),addr,n)
}
}
登入後複製

客户端

package main


import (
    "fmt"
    "net"
)


func main() {
    socker, err := net.DialUDP("udp", nil, &net.UDPAddr{
        IP:   net.IPv4(0, 0, 0, 0),
        Port: 8009,
})
    if err != nil {
        panic(fmt.Sprintf("连接服务器失败,err:%v", err))
}
    defer socker.Close()
    sendStr:="你好呀"
    _, err = socker.Write([]byte(sendStr))
    if err != nil {
        panic(fmt.Sprintf("数据发送失败,err:%v", err))
}
}
登入後複製

执行结果

一篇文章帶你了解Go語言基礎之網絡編程

总结

本次章节我们讲述了什么是TCP,什么是UDP。

并且编写了代码如何实现TCP服务端TCP客户端UDP服务端UDP客户端

讲述了为什么会出现粘包,该怎么解决粘包

以上是一篇文章帶你了解Go語言基礎之網絡編程的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:Go语言进阶学习
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!