美文网首页
Go-网络通信

Go-网络通信

作者: 骑蜗上高速 | 来源:发表于2019-12-28 12:30 被阅读0次

    TCP
    TCP通信:面向连接的,可靠的数据包传输。
    协议:一组规则,要求使用协议双方必须遵守协议内容。
    网络分层架构:
    OSI七层模型结构:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
    TCP/IP四层模型:数据链路层、网络层、传输层、应用层。
    各层功能:
    1、链路层:ARP
    源mac-目标mac
    ARP协议作用:借助IP获取mac地址
    2、网络层:IP
    缘IP-目标IP
    IP协议的作用:在网络环境中 唯一标示一台主机
    IP地址本质:2机制。点分十进制IP地址(string)
    3、传输层:TCP/UDP
    port-在一台计算机上唯一标示一个进程
    4、应用层:ftp、http、自定义
    对数据进行封装、解封装
    数据通知过程:
    封装:应用层-传输层-网络层-链路层 。没有经过封装,不能在网络环境中传递。
    解封装:链路层-网络层-传输层-应用层
    通信过程:
    1、mac地址(不需要用户指定)(ARP协议)->mac
    2、IP地址(需要用户指定)-确定主机
    3、port端口号(需要用户指定)-确定程序
    1、不能使用系统占用的端口。2、5000+端口。我们使用。3、65535为端口上线。
    socket:套接字
    网络通信过程中,socket一定是成对出现的。

    TCP通信过程
    三次握手:
    1、主动发起请求端,发送SYN
    2、被动建立连接 请求端,应答ACK同时发送SYN
    3、主动发起请求端,发送ACK
    标志TCP三次握手建立完成,-----server:accept()返回。-----client:Dial()返回

    四次挥手:
    1、主动关闭连接请求端,发送FIN
    2、被动关闭连接请求端,应答ACK
    标志,半关闭完成。---close()
    3、主动关连接请求端,发送FIN
    4、被动关闭连接请求端,应答ACK
    标志,四次挥手建立完成。---close()
    TCP状态转换图


    image.png image.png

    代码示例(服务端):

    package main
    import (
        "fmt"
        "net"
    )
    func main() {
        //1、指定服务器通信协议,ip地址,port。创建一个用于监听的socket
        listener, err := net.Listen("tcp","127.0.0.1:8000")
        if err != nil {
            fmt.Println("net.Listen Err",err)
            return
        }
        defer listener.Close()
        fmt.Println("服务器正在等待客户端连接...")
    
        //2、阻塞监听客户端连接请求,成功建立连接,返回用于通信的socket
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("listener.Accept() Err",err)
            return
        }
        defer conn.Close()
        fmt.Println("服务器与客户端成功建立连接!!!")
    
        //3、读取客户端发送的数据
        buf := make([]byte,4096)
        n, err := conn.Read(buf)
        if err != nil {
            fmt.Println("conn.Read(buf) Err",err)
            return
        }  
        conn.Write(buf[:n])//读多少,写多少
        //4、处理数据
        fmt.Println("服务器读到的数据,",string(buf[:n]))
    }
    

    执行结果:

    服务器正在等待客户端连接...
    服务器与客户端成功建立连接!!!
    服务器读到的数据, hello world!
    

    客户端:

    package main
    
    import (
        "fmt"
        "net"
    )
    
    func main(){
        //指定服务器IP + port创建通信套接字
        conn, err := net.Dial("tcp","127.0.0.1:8000")
        if err != nil {
            fmt.Println("net.Dial err",err)
            return
        }
        defer conn.Close()
        //主动写数据给服务器
        _, err = conn.Write([]byte("Hello World"))
        if err != nil {
            fmt.Println("conn.Write err",err)
            return
        }
        //接收服务器回发数据
        buf := make([]byte,4096)
        n, err := conn.Read(buf)
        if err != nil {
            fmt.Println("conn.Write err",err)
            return
        }
        fmt.Println("服务器回发",string(buf[:n]))
    }
    

    并发服务器(服务端):

    package main
    
    import (
        "fmt"
        "net"
        "strings"
    )
    func HaldlerConn(conn net.Conn)  {
        defer conn.Close()
        //获取客户端连接地址
        addr := conn.RemoteAddr()
        fmt.Println(addr,"客户端连接成功!!")
    
        buf := make([]byte,4096)
        //循环获取客户端发送的数据
        for {
            n, err := conn.Read(buf)
            if "exit\n" == string(buf[:n]) || "exit\r\n" == string(buf[:n]) {
                fmt.Println("服务器接收到客户端退出请求!!服务器退出")
            }
            if n == 0 {
                fmt.Println("服务器监测到客户端已经关闭,断开连接!!")
                return
            }
            if err != nil {
                fmt.Println("conn.Read Err",err)
                return
            }
            fmt.Printf("服务器获取客户端%s的数据%s",addr.String(),string(buf[:n]))
            //答谢转小写,回发给客户端
            upperStrings := strings.ToUpper(string(buf[:n]))
            _, err = conn.Write([]byte(upperStrings))
            if err != nil {
                fmt.Println("conn.Write Err",err)
                return
            }
        }
    }
    func main () {
        //指定服务器通信协议,ip地址,port。创建一个用于监听的socket
        listener, err := net.Listen("tcp","127.0.0.1:8000")
        if err != nil {
            fmt.Println("net.Listen Err",err)
            return
        }
        defer listener.Close()
        fmt.Println("服务器正在等待客户端连接...")
        for {
            conn, err := listener.Accept()
            if err != nil {fmt.Println("listener.Accept Err",err)
                return
            }
            go HaldlerConn(conn)
        }
    
    }
    

    执行结果:

    ~/Downloads/learngo/src/(master ✘)✖✹✚✭ ᐅ ./tcp            
    服务器正在等待客户端连接...
    127.0.0.1:62699 客户端连接成功!!
    127.0.0.1:62700 客户端连接成功!!
    服务器获取客户端127.0.0.1:62700的数据wef
    服务器获取客户端127.0.0.1:62699的数据erwe
    服务器获取客户端127.0.0.1:62699的数据hello
    服务器获取客户端127.0.0.1:62700的数据world
    
    ~  ᐅ nc 127.0.0.1 8000               
    erwe
    ERWE
    hello
    HELLO
    ᐅ nc 127.0.0.1 8000               
    wef
    WEF
    world
    WORLD
    

    并发服务器(客户端):

    package main
    
    import (
        "fmt"
        "net"
        "os"
    )
    
    func main()  {
    
        //指定服务器IP + port创建通信套接字
        conn, err := net.Dial("tcp","127.0.0.1:8000")
        if err != nil {
            fmt.Println("net.Dial Err",err)
            return
        }
        defer conn.Close()
    
        //指定键盘输入
        go func() {
            str := make([]byte, 4096)
            for {
                n, err := os.Stdin.Read(str)
                if err != nil {
                    fmt.Println("os.Stdin.Read Err",err)
                    continue
                }
                n, err = conn.Write(str[:n])
                if err != nil {
                    fmt.Println("os.Stdin.Read Err",err)
                    return
                }
            }
        }()
        //回显服务器回发来的大写数据
        buf := make([]byte, 4096)
        for {
            n, err := conn.Read(buf)
            if n == 0 {//服务器端回显数据为0
                fmt.Println("检查到服务器关闭,客户端退出")
                return
            }
            if err != nil {
                fmt.Println("conn.Read Err",err)
                return
            }
            fmt.Println("客户端读到服务器回发",string(buf[:n]))
        }
    }
    

    UDP
    UDP通信:无连接的,不可靠的报文传输。

    UDP服务端示例代码:

    package main
    
    import (
        "fmt"
        "net"
        "time"
    )
    
    func main (){
        //组织一个udp地址结构,指定服务器的ip+port
        srvAddr, err := net.ResolveUDPAddr("udp","127.0.0.1:8002")
        if err != nil {
            fmt.Println("net.ResolveUDPAddr Err",err)
            return
        }
        fmt.Println("udp服务器地址结构创建完成!")
        //创建一个用户通信的socket
        udpConn, err := net.ListenUDP("udp",srvAddr)
        if err != nil {
            fmt.Println("net.ListenUDP Err",err)
            return
        }
        defer udpConn.Close()
        fmt.Println("udp服务器socket创建完成!")
        //读取客户端发送的数据
        buf := make([]byte, 4096)
        //返回三个只,分别是读取的字节数,客户端的IP地址,err
        n, clientAddr, err := udpConn.ReadFromUDP(buf)
        if err != nil {
            fmt.Println("ReadFromUDP Err",err)
            return
        }
        //处理数据
        fmt.Printf("服务器读到%v的数据:%s\n",clientAddr,string(buf[:n]))
        //提取系统当前时间
        nowTime := time.Now().String()
        //写回数据给客户端
        _, err = udpConn.WriteToUDP([]byte(nowTime),clientAddr)
        if err != nil {
            fmt.Println("WriteToUDP Err",err)
            return
        }
    }
    

    UDP客户端示例代码:

    package main
    
    import (
        "fmt"
        "net"
    )
    
    func main(){
        //指定服务器IP + port创建通信套接字
        conn, err := net.Dial("udp","127.0.0.1:8002")
        if err != nil {
            fmt.Println("net.Dial err",err)
            return
        }
        defer conn.Close()
        //主动写数据给服务器
        _, err = conn.Write([]byte("Hello World"))
        if err != nil {
            fmt.Println("conn.Write err",err)
            return
        }
        //接收服务器回发数据
        buf := make([]byte,4096)
        n, err := conn.Read(buf)
        if err != nil {
            fmt.Println("conn.Write err",err)
            return
        }
        fmt.Println("服务器回发",string(buf[:n]))
    }
    

    总结UDP和TCP差异:


    image.png

    使用场景:
    TCP:对数据传输安全性、稳定性要求较高的场合,网络文件传输、下载、上传。
    UDP:对数据实时传输要求较高的场景,视频直播、在线电话会议、游戏。

    文件传输-发送端(客户端)
    1、提示用户使用命令行参数输入文件名,接收文件名filepath(含有访问路径)
    2、使用os.stat()获取文件属性。得到文件名filename(取出访问路径)
    3、主动发起连接服务器请求,结束时关闭连接
    4、发送文件名到接收端conn.write()
    5、读取接收端回发的确认数据conn.read()
    6、判断是否为“ok”,如果是,封装函数sendFile发送文件内容,传参filepath和conn
    7、只读方式open文件,结束时close文件
    8、循环读取本地文件,读到EOF,读取完毕
    9、将读取的内容原封不动的conn.write给接收端(服务端)
    文件传输示例:
    发送端

    package main
    
    import (
        "fmt"
        "io"
        "net"
        "os"
        )
    
    func main () {
        //获取命令行参数
        list := os.Args
        //获取文件路径
        filePath := list[1]
        fileInfo, err := os.Stat(filePath)
        if err != nil {
            fmt.Println(" os.Stat err",err)
            return
        }
        //获取文件名
             fileName := fileInfo.Name()
    
        //建立连接
        conn, err := net.Dial("tcp","127.0.0.1:8003")
        if err != nil {
            fmt.Println("net.Dial err",err)
            return
        }
        defer conn.Close()
        //发送文件名给接收端
        _, err = conn.Write([]byte(fileName))
            if err != nil {
            fmt.Println("conn.Read err",err)
            return
        }
        //接收服务器回发消息
        buf := make([]byte,4096)
        n, err := conn.Read(buf)
        if err != nil {
            fmt.Println("conn.Write err",err)
            return
        }
        if "ok" == string(buf[:n]) {
            sendFile(conn,filePath)
        }
    }
    
    func sendFile (conn net.Conn, filePath string) {
        //打开文件
        file, err := os.Open(filePath)
        if err != nil {
            fmt.Println("os.Open err",err)
            return
        }
        defer file.Close()
        //从文件中读取数据,写给接收端.读多少,写多少。
        buf := make([]byte, 4096)
        for {
            n, err := file.Read(buf)
            if err != nil {
                if err == io.EOF {
                    fmt.Println("文件发送完毕!")
                    return
                }else {
                    fmt.Println("file.Read err",err)
                }
                return
            }  
            //写到网络中
            _, err = conn.Write(buf[:n])
            if err != nil {
                fmt.Println("conn.Write err",err)
                return
            }
        }
    }
    

    文件传输-接收端
    1、创建监听listener,程序结束时关闭。
    2、阻塞等待客户端连接conn,程序结束时关闭conn
    3、读取客户端发送文件名,保存filename
    4、回发“ok”
    5、封装函数recvFile接收客户端发送的文件内容,穿参数filename和conn
    6、按照文件名create文件,结束时close
    7、循环read发送端网络温年内容,当读到0时,说明文件读取完毕
    8、将读取的文件内容原封不动的write到创建的文件中
    接收端:

    package main
    
    import (
        "fmt"
        "net"
        "os"
    )
    
    func main()  {
        //创建监听socket
        listener, err := net.Listen("tcp","127.0.0.1:8003")
        if err != nil {
            fmt.Println("net.Dial err",err)
            return
        }
        defer listener.Close()
        //阻塞等待发送端建立连接
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("listener.Accept err",err)
            return
        }
        defer conn.Close()
        //获取文件名,保存
        buf := make([]byte, 4096)
        n, err := conn.Read(buf)
        if err != nil {
            fmt.Println("conn.Read err",err)
            return
        }
        filename := string(buf[:n])
        //回写ok给发送端
        _, err = conn.Write([]byte("ok"))
        if err != nil {
            fmt.Println("conn.Write err",err)
            return
        }
        //接收文件
        recvFile(conn,filename)
    }
    
    func recvFile(conn net.Conn,filename string)  {
            //创建文件
        file, err := os.Create(filename)
        if err != nil {
            fmt.Println("os.Create err",err)
            return
        }
        defer file.Close()
        //从网络中读数据,写入文件
        buf := make([]byte, 4096)
        for {
            n, err := conn.Read(buf)
            if n == 0 {
                fmt.Printf("文件%s接收完毕!",filename)
                return
            }
            if err != nil {
                fmt.Println("conn.Read err",err)
                return
            }
            //写入文件
            _,err = file.Write(buf[:n])
            if err != nil {
                fmt.Println("file.Write err",err)
                return
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:Go-网络通信

          本文链接:https://www.haomeiwen.com/subject/trksoctx.html