Heim  >  Artikel  >  Backend-Entwicklung  >  Ein Artikel, der Ihnen hilft, die Grundlagen der Go-Sprachnetzwerkprogrammierung zu verstehen

Ein Artikel, der Ihnen hilft, die Grundlagen der Go-Sprachnetzwerkprogrammierung zu verstehen

Go语言进阶学习
Go语言进阶学习nach vorne
2023-07-21 16:19:19972Durchsuche
socker编编程

我们所学的TCPTCPUDP,统称为SockerUDP,统称为 Sockker 编程 , 也 叫做 套 接字 编程 编程

编程 , 也 叫做 叫做 套 编程

🎜🎜 🎜🎜🎜。🎜🎜🎜🎜多台🎜🎜🎜机器🎜🎜🎜要实现🎜🎜🎜互相通讯🎜🎜🎜底层从🎜🎜🎜铺设网线🎜🎜🎜,🎜🎜🎜网线接口🎜🎜🎜,🎜🎜 🎜交换机🎜🎜🎜,🎜🎜🎜路由器🎜🎜🎜,在到规定🎜🎜🎜各种协议🎜🎜🎜。🎜🎜

Gehen Sie zur AnwendungsebeneQQQQ微信等软件。

如果没有一套标准,每次使用都要自己去实现,可能每个程序员都不是掉头发那么简单了!

Ein Artikel, der Ihnen hilft, die Grundlagen der Go-Sprachnetzwerkprogrammierung zu verstehen

有了Socker之后,Socker会在应用层之前,将各种繁琐的的底层操作隐藏,我们可能只需要Socker.TCP就实现了TCP, WeChat

und andere Software. 🎜🎜

🎜if Es gibt keine Reihe von Standards. Sie müssen es jedes Mal selbst implementieren, wenn Sie es verwenden. Vielleicht ist es nicht so einfach, dass jeder Programmierer seine Haare verliert. 🎜🎜

Ein Artikel, der Ihnen hilft, die Grundlagen der Go-Sprachnetzwerkprogrammierung zu verstehen🎜🎜 Verstanden🎜🎜 Socker Nach 🎜🎜, 🎜🎜Socker🎜🎜 verbirgt verschiedene mühsame zugrunde liegende Vorgänge vor der Anwendungsschicht. Wir benötigen möglicherweise nur 🎜🎜Socker.TCP🎜🎜 wird erreicht🎜🎜TCP🎜🎜 Protokollkommunikation. 🎜🎜🎜

Go-Sprache TCP

TCP ist eine stabile und zuverlässige lange Verbindung

Da es sich um Kommunikation handelt, müssen zwei Terminals vorhanden sein, mindestens eines ist der Server. Einer ist der Kunde , genau wie unser Taobao. Jedes Mal, wenn wir Taobao öffnen, müssen wir natürlich nicht direkt darauf verlinken TCP.

Server

Implementieren Sie den Server in Go, und die Parallelität auf dem Server ist sehr einfach, Sie müssen nur jede Verbindung einer Coroutine überlassen!

Code

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)
}
}

Client

Der Client ist sehr einfach. Relativ gesehen erfordert er keine Parallelität, sondern nur eine Verbindung.

代码

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("发送成功")
        }
}
}

执行结果

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

Ein Artikel, der Ihnen hilft, die Grundlagen der Go-Sprachnetzwerkprogrammierung zu verstehen

粘包

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

服务端

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

执行结果

Ein Artikel, der Ihnen hilft, die Grundlagen der Go-Sprachnetzwerkprogrammierung zu verstehen

Wenn ich die 18. Codezeile auskommentiere

Ein Artikel, der Ihnen hilft, die Grundlagen der Go-Sprachnetzwerkprogrammierung zu verstehen

wird das Ausführungsergebnis

Ein Artikel, der Ihnen hilft, die Grundlagen der Go-Sprachnetzwerkprogrammierung zu verstehen

direkt zu einer Zeile hinzugefügt, was? Was passiert? Sollte es nicht dasselbe sein wie zuvor? ? ?

Jedes Mal, wenn ein Wert gesendet wird, wird er dort empfangen. Wie kann dies in einem Stück integriert werden? ! !

Der Grund

Der Hauptgrund ist, dass wir eine AnwendungsschichtSoftware sind, also Software, die auf dem Betriebssystem läuft Der Server wird gesendet, indem die zugehörige Schnittstelle des Betriebssystems aufruft. Das Betriebssystem durchläuft dann verschiedene komplexe Vorgänge und sendet es an die andere Maschine

Wenn der Puffer eine bestimmte Größe hat, werden die Daten standardmäßig nicht gesendet, wenn der obige Client Daten sendet wird immer im Betriebssystempuffer gedrückt, schließlich wurde festgestellt, dass keine Daten vorhanden waren, sodass sie auf einmal an den Server gesendet wurden

Aber warumsleep(1)sleep(1)又管用了呢?这是因为缓冲区不止一个程序在用,1s的时间足够其他程序将缓冲区打满,然后各自发各自的数据,这也是为什么第一次操作没问题,第二次有问题,因为第二次全部都是我们客户端打满的

Ein Artikel, der Ihnen hilft, die Grundlagen der Go-Sprachnetzwerkprogrammierung zu verstehen

解决粘包

工具函数

我们将解包封包的函数封装一下

socker_sitck/stick.goDas liegt daran, dass der Puffer von mehr als einem Programm verwendet wird. 1s ist genug Zeit für andere Programme, um den Puffer zu füllen und dann ihre eigenen Daten zu senden Erstens gab es beim ersten Vorgang kein Problem, aber beim zweiten Mal gab es ein Problem, weil beim zweiten Mal alles vollständig auf unserem Client geladen war

Ein Artikel, der Ihnen hilft, die Grundlagen der Go-Sprachnetzwerkprogrammierung zu verstehenEin Artikel, der Ihnen hilft, die Grundlagen der Go-Sprachnetzwerkprogrammierung zu verstehen

🎜 Klebetaschen lösen🎜

🎜Tool function🎜

🎜Wir werden die Funktion des Entpackens des Pakets kapseln🎜🎜🎜socker_sitck/stick.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)
}
}

执行结果

Ein Artikel, der Ihnen hilft, die Grundlagen der Go-Sprachnetzwerkprogrammierung zu verstehen

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

对了,只有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))
}
}

执行结果

Ein Artikel, der Ihnen hilft, die Grundlagen der Go-Sprachnetzwerkprogrammierung zu verstehen

总结

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

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

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

Das obige ist der detaillierte Inhalt vonEin Artikel, der Ihnen hilft, die Grundlagen der Go-Sprachnetzwerkprogrammierung zu verstehen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:Go语言进阶学习. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen