ホームページ > バックエンド開発 > Golang > Go 言語ネットワーク プログラミングの基本を理解するのに役立つ記事

Go 言語ネットワーク プログラミングの基本を理解するのに役立つ記事

リリース: 2023-07-21 16:19:19
転載
1029 人が閲覧しました
ソッカー プログラミング

これまでに学んだことTCPUDP は、まとめて Socker プログラミングと呼ばれ、別名ソケット プログラミング

#複数の マシン #相互通信を実現するために ## は、実際には非常に複雑なプロセスです。最下層では、ネットワーク ケーブルからネットワーク ケーブルを敷設します。インターフェース#,#スイッチ,##ルーター 、Provisionsさまざまなプロトコル

アプリケーション層に移動QQWeChat 、などのソフトウェア。

標準のセットがなく、各プログラマがそれを使用するたびに自分で実装しなければならない場合、髪を失うほど簡単ではないかもしれません。

Go 言語ネットワーク プログラミングの基本を理解するのに役立つ記事

サッカーをした後ソッカー, Socker は、アプリケーション層の前にさまざまな面倒な基礎操作を非表示にします。必要なのは、Socker.TCP だけです。 TCP プロトコルの通信。

Go languageTCP

TCP は安定した信頼性の高い長い接続です。

通信が関係するため、2 つの端末が必要です。少なくとも 1 つは サーバー で、もう 1 つは # です。 ##クライアント, タオバオと同じように、タオバオを開くたびにタオバオにリンクする必要があります。もちろん、タオバオは直接 TCP## ではありません#。

サーバー

Go でサーバーを実装すると、サーバーでの同時実行が非常に簡単になり、接続ごとに 1 つのプロトコルを許可するだけで済みます。 . 加工するだけ!

コード

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 言語ネットワーク プログラミングの基本を理解するのに役立つ記事

実行結果


##すべて 1 行にまとめられていますが、何ですか?何が起こっているのでしょうか? 以前と同じではないでしょうか? ? ? Go 言語ネットワーク プログラミングの基本を理解するのに役立つ記事

値が送信されるたびに、それは向こうで受信されます。なぜこれがすべて 1 つのまとまりになっているのでしょうか。 ! !

#理由

主な理由は、#アプリケーション層であるためです

ソフトウェアは、 オペレーティング システム 上で実行されるソフトウェアです。サーバーにデータを送信するときのデータは、## です。 # オペレーティング システム 関連インターフェイス を呼び出して送信すると、オペレーティング システムはさまざまな複雑な操作を経て、 ただし、オペレーティング システムにはデータ送信用のバッファがあります。デフォルトでは、バッファにサイズがあり、バッファがいっぱいでない場合、データは送信されません送信する必要があるため、上記のクライアントは、データを送信する際、システムのバッファがいっぱいではなく、オペレーティング システムのバッファに保持されていましたが、最終的にデータが存在しないことが判明したため、サーバーに一括送信されました

しかし、なぜ sleep(1) は再び機能するのでしょうか? これは、バッファーが複数のプログラムによって使用されており、1 秒が十分です 他のプログラムがバッファをいっぱいにして、独自のデータを送信します。これが、最初の操作は問題ありませんが、2 回目の操作は問題がある理由です。2 回目はクライアントによってすべて埋められるためです。

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 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:Go语言进阶学习
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート