美文网首页go 学习笔记
Go语言实现TCP通信

Go语言实现TCP通信

作者: markfork | 来源:发表于2018-10-19 15:13 被阅读235次

    章节

    • go 优势
    • go 实现 TCP 通信

    1 go 语言优势

    1.1 go 语言优势

    go 优势
    注意:跟本章似乎没有什么联系

    2 go 实现TCP通信

    2.1 server.go

    unix 网络编程步骤:

    Server->Bind->Listen->Accept
    go 语言实现 socket 编程步骤:
    Listen->Accept,简化为Listen、Accept

    package socket
    
    import (
        "fmt"
        "log"
        "net"
        "strings"
    )
    
    /**
       需求:
           socket编程实现 客户端 与 服务端进行通讯
           通讯测试场景:
               1.client 发送 ping, server 返回 pong
               2.client 发送 hello, server 返回 world
               3.其余client发送内容, server 回显即可
    
       抽象&解决方案:
           1. socket 编程是对 tcp通讯 过程的封装,unix server端网络编程过程为 Server->Bind->Listen->Accept
              go 中直接使用 Listen + Accept
           2. client 与客户端建立好的请求 可以被新建的 goroutine(go func) 处理 named connHandler
           3. goroutine 的处理过程其实是 输入流/输出流 的应用场景
    
       积累:
           1.基础语法
           2.基本数据结构 slice 使用
           3.goroutine 使用
           4.switch 使用
           4.socket 编程核心流程
           5.net 网络包使用
     */
    
    func connHandler(c net.Conn) {
        //1.conn是否有效
        if c == nil {
            log.Panic("无效的 socket 连接")
        }
    
        //2.新建网络数据流存储结构
        buf := make([]byte, 4096)
        //3.循环读取网络数据流
        for {
            //3.1 网络数据流读入 buffer
            cnt, err := c.Read(buf)
            //3.2 数据读尽、读取错误 关闭 socket 连接
            if cnt == 0 || err != nil {
                c.Close()
                break
            }
    
            //3.3 根据输入流进行逻辑处理
            //buf数据 -> 去两端空格的string
            inStr := strings.TrimSpace(string(buf[0:cnt]))
            //去除 string 内部空格
            cInputs := strings.Split(inStr, " ")
            //获取 客户端输入第一条命令
            fCommand := cInputs[0]
    
            fmt.Println("客户端传输->" + fCommand)
    
            switch fCommand {
            case "ping":
                c.Write([]byte("服务器端回复-> pong\n"))
            case "hello":
                c.Write([]byte("服务器端回复-> world\n"))
            default:
                c.Write([]byte("服务器端回复" + fCommand + "\n"))
            }
    
            //c.Close() //关闭client端的连接,telnet 被强制关闭
    
            fmt.Printf("来自 %v 的连接关闭\n", c.RemoteAddr())
        }
    }
    
    //开启serverSocket
    func ServerSocket() {
        //1.监听端口
        server, err := net.Listen("tcp", ":8087")
    
        if err != nil {
            fmt.Println("开启socket服务失败")
        }
    
        fmt.Println("正在开启 Server ...")
    
        for {
            //2.接收来自 client 的连接,会阻塞
            conn, err := server.Accept()
    
            if err != nil {
                fmt.Println("连接出错")
            }
    
            //并发模式 接收来自客户端的连接请求,一个连接 建立一个 conn,服务器资源有可能耗尽 BIO模式
            go connHandler(conn)
        }
    
    }
    

    注意:上述代码中有对应 socket Server 端编码的关键步骤注释。

    2.2 client.go -> 即编程实现简易telenet

    package socket
    
    import (
        "bufio"
        "fmt"
        "net"
        "os"
        "strings"
    )
    
    /**
       client 发送端 程序
       问题:如何区分  c net.Conn 的 Write 与 Read 的数据流向?
           1. c.Write([]byte("hello"))
              c <- "hello"
           2. c.Read(buf []byte)
              c -> buf (空buf)
       客户端 和 服务器端都有 Close conn 的功能
     */
    
    func cConnHandler(c net.Conn) {
        //返回一个拥有 默认size 的reader,接收客户端输入
        reader := bufio.NewReader(os.Stdin)
        //缓存 conn 中的数据
        buf := make([]byte, 1024)
    
        fmt.Println("请输入客户端请求数据...")
    
        for {
            //客户端输入
            input, _ := reader.ReadString('\n')
            //去除输入两端空格
            input = strings.TrimSpace(input)
            //客户端请求数据写入 conn,并传输
            c.Write([]byte(input))
            //服务器端返回的数据写入空buf
            cnt, err := c.Read(buf)
    
            if err != nil {
                fmt.Printf("客户端读取数据失败 %s\n", err)
                continue
            }
    
            //回显服务器端回传的信息
            fmt.Print("服务器端回复" + string(buf[0:cnt]))
        }
    }
    
    func ClientSocket() {
        conn, err := net.Dial("tcp", "127.0.0.1:8087")
        if err != nil {
            fmt.Println("客户端建立连接失败")
            return
        }
    
        cConnHandler(conn)
    }
    

    问题: 如何区分 c net.Conn 的 Write 与 Read 的数据流向?

    1. c.Write([]byte("hello"))
         c <- "hello"
    2. c.Read(buf []byte) >         
         c -> buf (空buf)
    

    注意:上述 箭头为数据流向

    2.3 程序运行&结果

    在 main.go 中调用 socket.ServerSocket()

    socket.ServerSocket()
    

    server端 运行结果

    server 端运行结果

    在 main.go 中调用 socket.ClientSocket

     socket.ClientSocket
    

    client 端运行结果

    client 端运行结果

    client端 & server 端测试
    client 输入 & server 端输入

    client 端输入

    server端输出

    server 端输出

    完。

    相关文章

      网友评论

        本文标题:Go语言实现TCP通信

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