目录
  • 写在前面
  • 并发聊天服务器
  • 具体代码
    • 服务端
    • 客户端
  •  总结

    写在前面

    Go语言在很多方面天然的具备很多便捷性,譬如网络编程,并发编程。而通道则又是Go语言实现并发编程的重要工具,因为其承担着通道之间互相通信的重任。并且因为其本身就是并发安全的,所以在某些场景下是非常好用的。

    并发聊天服务器

    这里主要是实现一个简单的并发聊天服务器。首先,客户端可以在服务器中注册自己的信息(登录以及退出),客户端发出的所有的信息由服务器向各个客户端进行转发,或者换句话说是广播。

    具体代码

    服务端

    说的再多,没有代码简单明了,直接上代码~

    package main
    
    import (
        "bufio"
        "fmt"
        "log"
        "net"
    )
    type client chan <- string //定义一个单向的向外发送数据的通道
    
    var (
        entering = make(chan client)
        leaving = make(chan client)
        messages = make(chan string)
    )
    
    func main() {
        listener, err := net.Listen("tcp","localhost:8000")
        if err != nil {
            log.Fatal("network is broken", err)
        }
        go broadcaster()
        for {
            conn, err := listener.Accept()
            if err != nil {
                log.Print(err)
                continue
            }
            go handleConn1(conn)
        }
    }
    func broadcaster()  {
        clients := make(map[client]bool) //存储每个client的登录状态
        for{
            select {
            case msg := <-messages:
                for cli := range clients {
                    cli <- msg
                }
            case cli := <-entering:
                clients[cli] = true
            case cli := <-leaving:
                delete(clients,cli)
                close(cli)
            }
        }
    }
    
    func handleConn1(conn net.Conn)  {
        ch := make(chan string)
        go clientWriter(conn, ch)
        who := conn.RemoteAddr().String()
        ch <- "You are " + who
        entering <- ch
        messages <- who + "has arrived"
    
        input := bufio.NewScanner(conn)
        for input.Scan() {
            messages <- who + ":" + input.Text()
        }
    
        leaving <- ch
        messages <- who + "has left"
        conn.Close()
    }
    
    func clientWriter(conn net.Conn, ch <- chan string)  {
        for msg := range ch {
            fmt.Fprintln(conn, msg)
        }
    }

    客户端

    客户端相对简单,只是涉及到信息的发送和接受工作。

    package main
    
    import (
        "io"
        "log"
        "net"
        "os"
    )
    
    func main() {
        conn, err := net.Dial("tcp","localhost:8000")
        if err != nil {
            log.Fatal("Connected has been refused!",err)
        }
        defer conn.Close()
        go mesCopy(os.Stdout,conn)
        mesCopy(conn,os.Stdin)
    }
    
    func mesCopy(des io.Writer, res io.Reader)  {
        if _, err := io.Copy(des, res); err != nil {
            log.Print("wrong!")
        }
    }

     总结

    实现原理较为简单,所以代码并没有多少注释,如果有任何疑问,欢迎留言讨论。最后说一句,在MIT的课程中,其实并不是很推荐在并发编程中使用通道chan,除非你对其应用的场景和可能出现的情况有很好的把握,不然可能会出现很多不可预测的事情,譬如死锁(见另外一篇博客)。在这种时候,共享变量将会是一种很好的选择,具体查看go多线程实践。

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。