美文网首页PHP开发PHP经验分享
go语言聊天室实现(二)gorilla/websocket中的聊

go语言聊天室实现(二)gorilla/websocket中的聊

作者: 公式般欢笑 | 来源:发表于2020-03-15 08:18 被阅读0次

    我们可以看到 gorilla/websocket中的examples中有一个聊天室的demo。

    └── src
        ├── github.com
        │   └── gorilla
        │       └── websocket
        │           ├── examples
        │           │   ├── chat
    

    我们进入该项目可以看到里面有这样的一些内容

    $ ll
    总用量 24K
    -rw-r--r-- 1 wangkan wangkan 3.4K 3月   9 12:31 client.go
    -rw-r--r-- 1 wangkan wangkan 2.2K 3月   9 12:31 home.html
    -rw-r--r-- 1 wangkan wangkan 1.2K 3月   9 12:31 hub.go
    -rw-r--r-- 1 wangkan wangkan  884 3月   9 12:31 main.go
    -rw-r--r-- 1 wangkan wangkan 4.7K 3月   9 12:31 README.md
    

    按照官方的运行方式来运行这个项目

    $ go run *.go
    

    在浏览器中打开8080端口,可以看到该项目可以被成功运行了。


    gorilla.png

    就是这样一个简单的demo。

    然后我们去看一下它的具体实现。
    在这个项目中首先定义了一个hub的结构体:

    type Hub struct {
        // Registered clients.
        clients map[*Client]bool
    
        // Inbound messages from the clients.
        broadcast chan []byte
    
        // Register requests from the clients.
        register chan *Client
    
        // Unregister requests from clients.
        unregister chan *Client
    }
    

    这个结构体中,clients代表所有已经注册的用户,broadcast管道会存储客户端发送来的信息。 register是一个*Client类型的管道,用于存储新注册的用户,unregister管道反之。

    我们打开main.go,main函数的源码为:

    func serveHome(w http.ResponseWriter, r *http.Request) {
        log.Println(r.URL)
        if r.URL.Path != "/" {
            http.Error(w, "Not found", http.StatusNotFound)
            return
        }
        if r.Method != "GET" {
            http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
            return
        }
        http.ServeFile(w, r, "home.html")
    }
    func main() {
        flag.Parse()
        hub := newHub()
        go hub.run()
        http.HandleFunc("/", serveHome)
        http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
            serveWs(hub, w, r)
        })
        err := http.ListenAndServe(*addr, nil)
        if err != nil {
            log.Fatal("ListenAndServe: ", err)
        }
    }
    

    在这里首先会新开一个goroutine,去跑hub的run方法,run方法中一个死循环,不停地去轮询hub中的内容

    func (h *Hub) run() {
        for {
            select {
            case client := <-h.register:
                h.clients[client] = true
            case client := <-h.unregister:
                if _, ok := h.clients[client]; ok {
                    delete(h.clients, client)
                    close(client.send)
                }
            case message := <-h.broadcast:
                //循环所有的client,并给他们的send中写入数据。
                for client := range h.clients {
                    select {
                    case client.send <- message:
                    default:
                        close(client.send)
                        delete(h.clients, client)
                    }
                }
            }
        }
    }
    

    如果取到了新用户,就加入到clients中,如果取到了信息,就循环所有的client,将信息写到client.send中。
    我们看到在请求路径为根的时候,它会请求一个函数,而这个函数就是将home.html发送到客户端。
    而在请求路径为“/ws”的时候,他会执行一个serveWS的函数。

    func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) {
        conn, err := upgrader.Upgrade(w, r, nil)
        fmt.Println(*hub)
        if err != nil {
            log.Println(err)
            return
        }
        client := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)}
        //将client写入到当前client.hub.register中
        client.hub.register <- client
    
        // Allow collection of memory referenced by the caller by doing all work in
        // new goroutines.
        go client.writePump()
        go client.readPump()
    }
    

    每当一个新的用户进来之后,首先将连接升级为长连接,然后将当前的client写到register中,由hub.run函数去做处理。然后开启两个goroutine,一个去读client中发送来的数据,一个将数据写入到所有的client中,去发送给用户。
    这就是整个聊天室的实现原理。

    相关文章

      网友评论

        本文标题:go语言聊天室实现(二)gorilla/websocket中的聊

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