Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Artikel untuk membantu anda memahami asas pengaturcaraan rangkaian bahasa Go

Artikel untuk membantu anda memahami asas pengaturcaraan rangkaian bahasa Go

Go语言进阶学习
Go语言进阶学习ke hadapan
2023-07-21 16:19:19972semak imbas
socker编程

我们所学的TCPTCPUDP,统称为SockerUDP,统称为 socker 编程, 也 叫做

套 接字 编程 多台机器要实现互相通讯,其实是一一不层从铺设网线网线接口 交换机路由器,在到规定

🎜🎜各种协🎜🎜🎜🎜🎜🎜

Pergi ke lapisan aplikasiQQQQ微信等软件。

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

Artikel untuk membantu anda memahami asas pengaturcaraan rangkaian bahasa Go

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

dan perisian lain. 🎜🎜

🎜if Tiada satu set piawaian Anda perlu melaksanakannya sendiri setiap kali anda menggunakannya Mungkin setiap pengaturcara tidak semudah kehilangan rambutnya! 🎜🎜

Artikel untuk membantu anda memahami asas pengaturcaraan rangkaian bahasa Go🎜 p cid="n12" mdtype="paragraph" style="max-width:90%"Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size: 16px;text-align: start;">🎜 Faham🎜🎜 Socker Selepas 🎜🎜, 🎜🎜Socker🎜🎜 akan menyembunyikan pelbagai operasi asas yang membosankan sebelum lapisan aplikasi Kita mungkin hanya memerlukan 🎜🎜Socker.TCP🎜🎜 dicapai🎜🎜TCP🎜🎜 komunikasi protokol. 🎜🎜🎜

Go language TCP

TCP ialah sambungan panjang yang stabil dan boleh dipercayai

Memandangkan komunikasi terlibat, mesti ada dua terminal, sekurang-kurangnya satu adalah . satu adalah pelanggan , sama seperti Taobao kami Setiap kali kami membuka Taobao, kami mesti memautkannya secara langsung . TCPPelayan

Laksanakan pelayan dalam Go, dan concurrency pada pelayan adalah sangat mudah, anda hanya perlu membiarkan coroutine mengendalikan setiap sambungan!

Kod

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

Pelanggan


Secara relatifnya, ia tidak memerlukan konkurensi, ia hanya memerlukan sambungan.

代码

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

执行结果

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

Artikel untuk membantu anda memahami asas pengaturcaraan rangkaian bahasa 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

执行结果

Artikel untuk membantu anda memahami asas pengaturcaraan rangkaian bahasa Go

Jika saya mengulas baris ke-18 kod

Artikel untuk membantu anda memahami asas pengaturcaraan rangkaian bahasa Go

hasil pelaksanaan

Artikel untuk membantu anda memahami asas pengaturcaraan rangkaian bahasa Go

, apakah yang ditambah secara langsung pada satu baris? Apa yang berlaku? ? ?

Setiap kali nilai dihantar, ia diterima di sana Bagaimana ini boleh disepadukan menjadi satu bahagian? ! !

Sebabnya

Sebab utamanya ialah kerana kita lapisan aplikasiperisian, iaitu perisian yang berjalan pada sistem pengendalian kami pelayan, Ia dihantar dengan memanggil antara muka yang berkaitan sistem pengendalian Sistem pengendalian kemudiannya melalui pelbagai operasi yang kompleks dan menghantarnya ke mesin yang lain

. sistem pengendalian mempunyai penimbal data penghantaran Secara lalai, jika penimbal mempunyai saiz tertentu, data tidak akan dihantar jika penimbal tidak penuh Oleh itu, apabila pelanggan di atas menghantar data, penimbal sistem tidak penuh, dan ia sentiasa ditekan dalam penimbal sistem operasi , akhirnya mendapati tiada data, jadi ia dihantar ke pelayan sekaligus

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

Artikel untuk membantu anda memahami asas pengaturcaraan rangkaian bahasa Go

解决粘包

工具函数

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

socker_sitck/stick.goIa berfungsi semula? Ini kerana buffer digunakan oleh lebih daripada satu program 1s adalah masa yang cukup untuk program lain untuk mengisi buffer dan kemudian menghantar data mereka sendiri pertama Tiada masalah dengan operasi pertama, tetapi terdapat masalah kali kedua, kerana kali kedua semuanya dimuatkan sepenuhnya pada klien kami

Artikel untuk membantu anda memahami asas pengaturcaraan rangkaian bahasa GoArtikel untuk membantu anda memahami asas pengaturcaraan rangkaian bahasa Go

🎜 Selesaikan beg melekit🎜

🎜Fungsi alat🎜

🎜Kami akan merangkum fungsi membongkar paket🎜🎜🎜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)
}
}

执行结果

Artikel untuk membantu anda memahami asas pengaturcaraan rangkaian bahasa 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))
}
}

执行结果

Artikel untuk membantu anda memahami asas pengaturcaraan rangkaian bahasa Go

总结

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

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

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

Atas ialah kandungan terperinci Artikel untuk membantu anda memahami asas pengaturcaraan rangkaian bahasa Go. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:Go语言进阶学习. Jika ada pelanggaran, sila hubungi admin@php.cn Padam