go语言从零学起(四) -- 基于martini和gorilla实现的websocket聊天实例

如果只是想了解chat的实现方式,在gorilla和revel框架里面都有完整的chat实例可以提供参考。本篇讲解的是,如何基于martini实现websocket的聊天。

配置步骤:

1 已经安装了go命令,配置了GOPATH

2 安装gorilla/websocket

go get github.com/gorilla/websocket

3 安装martini

go get github.com/go-martini/martini

项目文件列表

$GOPATH/demo/home.html
$GOPATH/demo/martini_ws.go

home.html 代码

<!DOCTYPE html>
<html lang="en">
<head>
<title>Chat Example</title>
<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;
        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://{{$}}/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('
');
            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>
<style type="text/css">
html {
    overflow: hidden;
}

body {
    overflow: hidden;
    padding: 0;
    margin: 0;
     100%;
    height: 100%;
    background: gray;
}

#log {
    background: white;
    margin: 0;
    padding: 0.5em 0.5em 0.5em 0.5em;
    position: absolute;
    top: 0.5em;
    left: 0.5em;
    right: 0.5em;
    bottom: 3em;
    overflow: auto;
}

#form {
    padding: 0 0.5em 0 0.5em;
    margin: 0;
    position: absolute;
    bottom: 1em;
    left: 0px;
     100%;
    overflow: hidden;
}

</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>

martini_ws.go代码

package main

import (
    "fmt"
    "github.com/go-martini/martini"
    "github.com/gorilla/websocket"
    "log"
    "net/http"
    "text/template"
)

const (
    readBufferSize  = 1024
    writeBufferSize = 1024
)

type Client struct {
    conn     *websocket.Conn
    messages chan []byte
}

var clients map[*Client]bool // 存储所有的链接
var broadcast chan []byte    // 广播聊天的chan
var addClients chan *Client  // 新链接进来的chan

func (c *Client) readPump() {
    for {
        _, message, err := c.conn.ReadMessage()
        if err != nil {
            if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
                log.Printf("error: %v", err)
            }
            break
        }
        fmt.Printf("receive message is :%s
", message)
        broadcast <- message
    }
}

func (c *Client) writePump() {
    for {
        select {
        case message := <-c.messages:
            fmt.Printf("send message is :%s
", message)
            c.conn.WriteMessage(1, message)
        }
    }
}

func manager() {

    clients = make(map[*Client]bool)
    broadcast = make(chan []byte, 10)
    addClients = make(chan *Client)

    for {
        select {
        case message := <-broadcast:
            for client := range clients {
                select {
                case client.messages <- message:
                default:
                    close(client.messages)
                    delete(clients, client)
                }
            }
        case itemClient := <-addClients:
            clients[itemClient] = true
        }
    }
}

func main() {

    var homeTemplate = template.Must(template.ParseFiles("home.html"))

    m := martini.Classic()

    m.Get("/", func(res http.ResponseWriter, req *http.Request) {

        res.Header().Set("Content-Type", "text/html; charset=utf-8")
        homeTemplate.Execute(res, req.Host)
    })

    m.Get("/ws", func(res http.ResponseWriter, req *http.Request) { // res and req are injected by Martini
        conn, err := websocket.Upgrade(res, req, nil, readBufferSize, writeBufferSize)
        if err != nil {
            log.Println(err)
            return
        }
        client := &Client{conn: conn, messages: make(chan []byte, 5)}
        addClients <- client
        go client.writePump()
        client.readPump()
    })

    go manager()

    m.RunOnAddr(":3010")
    //m.Run()

}

按照上面的步骤建好文件,就可以运行了。m.RunOnAddr 可以改成你需要的端口

执行命令:

go run martini_ws.go

在浏览器输入 http://localhost:3010 就能访问你本机的聊天服务了

 参考地址

https://github.com/patcito/martini-gorilla-websocket-chat-example