念念不忘,必有迴響

golang 通过TCP连接进行简单的server与client通信
以一个简单的多client端对话为例
效果大致如下

clipboard.png
clipboard1.png
clipboard2.png

Server 端

package main

import (
    "fmt"
    "log"
    "net"
    "os"
)

const (
    host = "127.0.0.1"
    port = "9220"
)

var (
    // 存放所有连接的客户端
    clients []net.Conn
    // 读取客户端的消息体
    data    = make([]byte, 1024)
)

func main() {
    // 创建一个 tcp 服务端连接
    listener, err := net.Listen("tcp", host+":"+port)
    if err != nil {
        log.Println(err)
        os.Exit(0)
    }
    defer listener.Close()
    log.Println("tcp server start")

    // 处理客户端的连接
    for {

        // 客户端初次连接
        conn, err := listener.Accept()
        if err != nil {
            log.Print(err)
            os.Exit(1)
        }
        // 保存客户端的连接
        clients = append(clients, conn)

        // todo for 嵌套 for 需要 协程???
        go func(conn net.Conn) {
            // 当前连接的客户端地址
            ClientRemoteAddr := conn.RemoteAddr().String()

            log.Println("客户端连接来自:", ClientRemoteAddr)

            // 读取客户端的数据包
            _, errRead := conn.Read(data)
            if errRead != nil {
                fmt.Printf("Client %v quit.\n", conn.RemoteAddr())
                conn.Close()
                disconnect(conn, conn.RemoteAddr().String())
                return
            }

            name := string(data) + "(" + ClientRemoteAddr + ")"

            conn.Write([]byte("欢迎你," + name))

            notify(conn, name+" 上线了")

            log.Println("客户端:" + name + "上线了")

            // 处理消息交互
            for {
                _, err := conn.Read(data)
                if err != nil {
                    fmt.Printf("Client %s quit.\n", name)
                    conn.Close()
                    disconnect(conn, name)
                    return
                }
                res := string(data)

                sprdMsg := name + ":" + res
                fmt.Println(sprdMsg)
                res = "我:" + res
                conn.Write([]byte(res))
                notify(conn, sprdMsg)
            }
        }(conn)

    }
}

// 通知除自己的其他所有客户端
func notify(conn net.Conn, msg string) {
    for _, con := range clients {
        if con.RemoteAddr() != conn.RemoteAddr() {
            con.Write([]byte(msg))
        }
    }
}

// 离开通知所有其他在线的客户端
func disconnect(conn net.Conn, name string) {
    for index, con := range clients {
        if con.RemoteAddr() == conn.RemoteAddr() {
            disMsg := name + " 离开了."
            fmt.Println(disMsg)
            clients = append(clients[:index], clients[index+1:]...)
            notify(conn, disMsg)
        }
    }
}

Client

package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
    "strings"
)

var (
    // 定义写入跟读取服务端的消息体
    writeStr, readStr = make([]byte, 1024), make([]byte, 1024)
)

func main() {

    var (
        host = "127.0.0.1"
        port = "9220"
        // 获取用户在终端的输入
        reader = bufio.NewReader(os.Stdin)
    )

    conn, err := net.Dial("tcp", host+":"+port)

    if err != nil {
        fmt.Println("Error connecting:", err)
        os.Exit(1)
    }

    defer conn.Close()

    fmt.Println("连接成功 Connecting to " + "127.0.0.1:9220")

    fmt.Printf("输入用户名进行聊天: \n")

    fmt.Scanf("%s", &writeStr)

    in, errWrite := conn.Write(writeStr)
    if errWrite != nil {
        fmt.Printf("Error when send to server: %d\n", in)
        os.Exit(0)
    }

    // 一定得用协程!!! 获取服务端的消息
    go read(conn)

    // todo main 下不能有两个或以上 for{} !!!
    // 将客户端的输入通知服务端
    readLineWrite(reader, conn)

}

func readLineWrite(reader *bufio.Reader, conn net.Conn) {
    for {
        //fmt.Printf(":")
        writeStr, _, _ = reader.ReadLine()
        if string(writeStr) == "quit" {
            fmt.Println("Communication terminated.")
            os.Exit(1)
        }

        in, err := conn.Write(writeStr)
        if err != nil {
            fmt.Printf("Error when send to server: %d\n", in)
            os.Exit(0)
        }
    }
}

func read(conn net.Conn)  {
    for  {
        // 这里需要捕捉错误,不然服务端连接关闭会进入死循环
        _, err := conn.Read(readStr)
        if err != nil {
            fmt.Printf("Client quit.\n")
            conn.Close()
            return
        }

        msg := string(readStr)
        msg = strings.Replace(msg, "\r\n", "", -1)
        fmt.Println(msg)
    }
}

-- END

写的不错,赞助一下主机费

扫一扫,用支付宝赞赏
扫一扫,用微信赞赏

暂无评论~~