基于go websocket写一个聊天室

来源:互联网 发布:mac的邮箱怎么设置 编辑:程序博客网 时间:2024/05/21 17:56

上一篇介绍了一下go websocket,这篇blog主要是通过websocket写一个聊天室。
先写前端

</style></head><body><div id="log"></div><form id="form">    <input type="submit" value="Send" />    <input type="text" id="msg" size="64"/></form></body></html>

这里面有三个组件,一个log记录,一个是msg消息发送,一个是发送按钮。下面给这个两个组件添加事件,

<script type="text/javascript">window.onload = function () {    var conn;    var msg = document.getElementById("msg");    var log = document.getElementById("log");    function appendLog(item) {        var doScroll = log.scrollTop > log.scrollHeight - log.clientHeight - 1;        log.appendChild(item);        if (doScroll) {            log.scrollTop = log.scrollHeight - log.clientHeight;        }    }    document.getElementById("form").onsubmit = function () {        if (!conn) {            return false;        }        if (!msg.value) {            return false;        }        conn.send(msg.value);        msg.value = "";        return false;    };    if (window["WebSocket"]) {        conn = new WebSocket("ws://" + document.location.host + "/ws");        conn.onclose = function (evt) {            var item = document.createElement("div");            item.innerHTML = "<b>Connection closed.</b>";            appendLog(item);        };        conn.onmessage = function (evt) {            var messages = evt.data.split('\n');            for (var i = 0; i < messages.length; i++) {                var item = document.createElement("div");                item.innerText = messages[i];                appendLog(item);            }        };    } else {        var item = document.createElement("div");        item.innerHTML = "<b>Your browser does not support WebSockets.</b>";        appendLog(item);    }};</script>

这个里面先对发送按钮添加事件,每当按下时候执行send,将数据发送到后端;当后端发送数据到前端,就执行appendLog追加内容。好了,介绍完这个就介绍后端。后端里面先介绍主程序

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)    }}

这个里面hub是一个消息的分发器,就是把消息发送到每个客户端。这个里面把消息广播到每个聊天的client。

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:            for client := range h.clients {                select {                case client.send <- message:                default:                    close(client.send)                    delete(h.clients, client)                }            }        }    }}

还有一个是serverWs的实现

    go client.writePump()    go client.readPump()

就是负责client的读和写。

func (c *Client) readPump() {    defer func() {        c.hub.unregister <- c        c.conn.Close()    }()    c.conn.SetReadLimit(maxMessageSize)    c.conn.SetReadDeadline(time.Now().Add(pongWait))    c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })    for {        _, message, err := c.conn.ReadMessage()        if err != nil {            if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {                log.Printf("error: %v", err)            }            break        }        message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))        c.hub.broadcast <- message    }}

读很简单,通过ReadMessage读取
写也是类似,

func (c *Client) writePump() {    ticker := time.NewTicker(pingPeriod)    defer func() {        ticker.Stop()        c.conn.Close()    }()    for {        select {        case message, ok := <-c.send:            c.conn.SetWriteDeadline(time.Now().Add(writeWait))            if !ok {                // The hub closed the channel.                c.conn.WriteMessage(websocket.CloseMessage, []byte{})                return            }            w, err := c.conn.NextWriter(websocket.TextMessage)            if err != nil {                return            }            w.Write(message)            // Add queued chat messages to the current websocket message.            n := len(c.send)            for i := 0; i < n; i++ {                w.Write(newline)                w.Write(<-c.send)            }            if err := w.Close(); err != nil {                return            }        case <-ticker.C:            c.conn.SetWriteDeadline(time.Now().Add(writeWait))            if err := c.conn.WriteMessage(websocket.PingMessage, []byte{}); err != nil {                return            }        }    }}

那么这个聊天室就写好了。