Go 언어 네트워크 프로그래밍의 기본을 이해하는 데 도움이 되는 기사

풀어 주다: 2023-07-21 16:19:19
앞으로
1025명이 탐색했습니다.
socker编程

我们所학적인TCPTCPUDP,统称为SockerUDP,统称为Socker编程,也叫做

套接字编程多台机器要实现互상통讯,其实是一个不常复杂的过程,底层从 铺设网线网线接口 ㅋㅋㅋㅋㅋㅋㅋㅋㅋ

응용 프로그램 레이어로 이동QQQQ微信等软件。

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

Go 언어 네트워크 프로그래밍의 기본을 이해하는 데 도움이 되는 기사

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

및 기타 소프트웨어. 🎜🎜

🎜if 정해진 표준은 없습니다. 사용할 때마다 직접 구현해야 합니다. 어쩌면 모든 프로그래머는 머리가 빠지는 것만큼 간단하지 않을 수도 있습니다. 🎜🎜

Go 언어 네트워크 프로그래밍의 기본을 이해하는 데 도움이 되는 기사🎜🎜 알겠습니다🎜🎜 소커 🎜🎜 이후 🎜🎜Socker🎜🎜는 애플리케이션 계층 이전에 다양한 지루한 기본 작업을 숨길 것입니다. 우리는 🎜🎜Socker.TCP🎜🎜가 달성되었습니다🎜🎜TCP🎜🎜 프로토콜 통신. 🎜🎜🎜

Go 언어 TCP

TCP는 안정적이고 신뢰할 수 있는 긴 연결입니다.

통신이 필요하므로 두 개의 터미널이 있어야 하며 적어도 하나는 server입니다. 하나는 클라이언트 입니다. Taobao를 열 때마다 연결해야 합니다. 물론 Taobao가 직접 TCP은 아닙니다.

Server

Go에서 서버를 구현하면 서버에서의 동시성은 매우 간단합니다. 코루틴이 각 연결을 처리하도록 하면 됩니다!

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

클라이언트는 상대적으로 매우 간단하며 동시성이 필요하지 않고 연결만 필요합니다.

代码

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 언어 네트워크 프로그래밍의 기본을 이해하는 데 도움이 되는 기사

이 한 줄에 바로 추가되는데 뭐죠? 무슨 일이 일어나고 있는 걸까요? 이전과 같아야 하지 않나요? ? ?

값이 전송될 때마다 그곳에서 수신됩니다. 이것을 어떻게 하나로 통합할 수 있나요? ! !

이유

가장 큰 이유는 우리가 운영 체제에서 실행되는 소프트웨어인 애플리케이션 레이어소프트웨어이기 때문입니다. 운영 체제 관련 인터페이스 를 호출하여 전송됩니다. 그런 다음 운영 체제는 여러 가지 복잡한 작업을 거쳐 다른 시스템

으로 보냅니다. , 운영 체제에는 기본적으로 버퍼가 특정 크기인 경우 버퍼가 가득 차지 않으면 데이터가 전송되지 않습니다. 따라서 위의 클라이언트가 데이터를 보낼 때 시스템 버퍼가 가득 차지 않습니다. 그리고 항상 운영체제 버퍼에 눌려져 있는데, 결국 데이터가 없다는 걸 발견해서 한꺼번에 서버로 보냈습니다

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

Go 언어 네트워크 프로그래밍의 기본을 이해하는 데 도움이 되는 기사

解决粘包

工具函数

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

socker_sitck/stick.go다시 작동하나요? 이는 버퍼가 두 개 이상의 프로그램에서 사용되기 때문입니다. 1초는 다른 프로그램이 버퍼를 채우고 자체 데이터를 보내는 데 충분한 시간입니다. 첫 번째 작업에는 문제가 없었지만 두 번째에는 문제가 발생했습니다. 두 번째에는 클라이언트에 모든 것이 완전히 로드되었기 때문입니다

Go 언어 네트워크 프로그래밍의 기본을 이해하는 데 도움이 되는 기사Go 언어 네트워크 프로그래밍의 기본을 이해하는 데 도움이 되는 기사

🎜 끈끈한 가방 해결🎜

🎜도구 기능🎜

🎜패킷 풀기 기능을 캡슐화하겠습니다🎜🎜< p cid="n50" mdtype="paragraph" style="box-sizing: border-box;line-height: 상속;고아: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font- family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size: 16px;text-align: start;">🎜<코드 스타일= "상자 크기: 테두리 -상자; 글꼴 계열: var(--monospace); 수직 정렬: 초기; 테두리 너비: 1px; 테두리 스타일: 솔리드; 테두리 색상: rgb(231, 234, 237) ;배경색: rgb (243, 244, 244);경계 반경: 3px;패딩 오른쪽: 2px;패딩 왼쪽: 2px;글꼴 크기: 0.9em;">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)
}
}
로그인 후 복사

执行结果

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으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿