案例:并发的网络聊天室服务

import (
        "bufio"
        "fmt"
        "log"
        "net"
)

type client chan<- string //声明一个数据类型client 是一个只能写字符串的channel

var (
        //entering 表示登陆 channel,
        //如果有新的client登陆,
        //应该向该channel发送数据,告知是哪个client登陆
        entering = make(chan client) //用法比较特殊,entering是一个channel,里面存的元素也是一个client channel

        //leaving 表示离开 channel,
        //如果有某个cilent退出链接,
        //应该向该channel发送数据,告知是哪个client离开
        leaving = make(chan client)

        //message 表示正常聊天消息,广播消息事件,
        //client发送的正常聊天信息都应该向该channel发送数据
        messages = make(chan string)
)



func broadcaster() {
        //用来记录所有已经链接的client客户端
        var clients = make(map[client]bool)

        for {
                select {
                case msg := <-messages:
                        //客户端正常消息事件
                        //取出全部在线的客户端client,将该消息发送给每个客户端
                        for cli := range clients {
                                cli <- msg
                        }
                case cli := <-entering:
                        //client登陆事件
                        //给clients map增添一对 key-value
                        clients[cli] = true
                case cli := <-leaving:
                        //client离开事件
                        //给clients map删除一堆 key-value
                        delete(clients, cli)
                        //将该登出的client channel关闭
                        close(cli)
                }
        }
}

func clientWrite(conn net.Conn, ch <-chan string) {
        for msg := range ch {
                fmt.Fprintln(conn, msg)
        }
}

func handleConn(conn net.Conn) {

        //给当前客户端创建一个channel 这个channel和conn套接字保持同步
        //只要想ch写数据, clientWrite 这个goroutine就会想对应的conn套接字写数据给客户端
        ch := make(chan string) //ch就是当前客户端的client
        go clientWrite(conn, ch)

        who := conn.RemoteAddr().String()
        //给当前客户端回应 client的id
        ch <- "You are" + who

        //向全部已经在线的client广播 当前新的client已经登录
        messages <- who + "已经登录"
        //发送当前ch登录事件 broadcaster会处理这个事件,将ch放在clients中
        entering <- ch

        //阻塞等待客户端输入消息
        input := bufio.NewScanner(conn)
        for input.Scan() {
                //将客户端输入消息进行广播 发给其他client
                messages <- who + ":" + input.Text()
        }

        //如果input.Scan退出,表示客户端断开连接 此处可以添加超时机制

        //发送当前ch退出事件
        leaving <- ch

        //想其他client广播 当前ch已经退出
        messages <- who + "已经退出"

        //关闭当前客户端套接字conn
        conn.Close()
}

func main() {
        //启动服务器监听本地端口 得到listenner
        listener, err := net.Listen("tcp", "localhost:8001")
        if err != nil {
                log.Fatal(err)
        }

        //开一个goroutine去处理3个channel事件
        go broadcaster()

        for {
                //等待新的客户端链接请求过来
                conn, err := listener.Accept()
                if err != nil {
                        log.Print(err)
                        continue
                }
                //开一个goroutine去处理client请求
                go handleConn(conn)
        }
}

results matching ""

    No results matching ""