Go realize the function of chat room

Time:2022-5-10

Learning golang for the first time, I found that the language level is really different from PHP, but there is no great difficulty in learning. This small case is also a learning experience. The following code is not implemented by myself. I just sorted it out to understand the overall implementation process logic

The implementation effect is shown in the figure below:

Go realize the function of chat room

The overall logic is as follows. The hub structure stores all the connection user connection information of the chat room. When the user logs in or logs out, the user connection information in the hub structure is added or deleted by transmitting the information through the channel. Whenever there is a user connection, the server will add a protocol to maintain the corresponding connection, and create a data reader and a data writer respectively to read and write to the websocket.

Go realize the function of chat room

In terms of communication protocol, websocket protocol is used. Its biggest feature is that the server can actively push information to the client, and the client can also actively send information to the server. It is a real two-way equal dialogue and belongs to a kind of server push technology. The implementation of websocket protocol is based on HTTP protocol, and the protocol is upgraded through upgrade.

Server code implementation (comments have been added and can be read with confidence):

server.go

package main

import (
    "encoding/json"
    "fmt"
    "net/http"

    "github.com/gorilla/mux"
)

func main() {
    router := mux.NewRouter()
    //Start the cooperation process and start the connection service management center
    go h.run()
    //Create WS service
    router.HandleFunc("/ws", myws)
    //Start HTTP service
    if err := http.ListenAndServe("127.0.0.1:8080", router); err != nil {
        fmt.Println("err:", err)
    }
}

//The user center maintains the connections of multiple users
var h = hub{
    c: make(map[*connection]bool),
    u: make(chan *connection),
    b: make(chan []byte),
    r: make(chan *connection),
}

type hub struct {
    //Current online connection information
    c map[*connection]bool
    //Delete connection
    u chan *connection
    //Transfer data
    b chan []byte
    //Join connection
    r chan *connection
}

func (h *hub) run() {
    for {
        select {
        //User connection, add connection information
        case c := <-h.r:
            h.c[c] = true
            c.data.Ip = c.ws.RemoteAddr().String()
            c.data.Type = "handshake"
            c.data.UserList = user_list
            data_b, _ := json.Marshal(c.data)
            //Send to writer
            c.sc <- data_b
        //Delete specified user connection
        case c := <-h.u:
            if _, ok := h.c[c]; ok {
                delete(h.c, c)
                close(c.sc)
            }
        //Send messages to chat room online people
        case data := <-h.b:
            for c := range h.c {
                select {
                //Send data
                case c.sc <- data:
                //If the sending is unsuccessful, the connection information will be deleted
                default:
                    delete(h.c, c)
                    close(c.sc)
                }
            }
        }
    }
}

connection.go

package main

import (
    "encoding/json"
    "fmt"
    "net/http"

    "github.com/gorilla/websocket"
)

//User connection structure
type connection struct {
    ws   *websocket.Conn
    sc   chan []byte
    data *Data
}

//User online list
var user_list = []string{}

type Data struct {
    Ip       string   `json:"ip"`
    User     string   `json:"user"`
    From     string   `json:"from"`
    Type     string   `json:"type"`
    Content  string   `json:"content"`
    UserList []string `json:"user_list"`
}

var wu = &websocket.Upgrader{ReadBufferSize: 512,
    WriteBufferSize: 512, CheckOrigin: func(r *http.Request) bool { return true }}

//Websocket service
func myws(w http.ResponseWriter, r *http.Request) {
    //Protocol upgrade
    ws, err := wu.Upgrade(w, r, nil)
    if err != nil {
        return
    }
    //Create connection
    c := &connection{sc: make(chan []byte, 256), ws: ws, data: &Data{}}
    //Connection join hub management
    h.r <- c
    go c.writer()
    c.reader()
    //Log out
    defer logout(c)
}
//Data writer
func (c *connection) writer() {
    //Take out the sending information and write it
    for message := range c.sc {
        fmt.Println(message,"\n")
        c.ws.WriteMessage(websocket.TextMessage, message)
    }
    c.ws.Close()
}

//Data reader
func (c *connection) reader() {
    for {
        //Receive WS information
        _, message, err := c.ws.ReadMessage()
        if err != nil {
            h.r <- c
            break
        }
        json.Unmarshal(message, &c.data)
        //Resolve information type
        switch c.data.Type {
        //User login
        case "login":
            c.data.User = c.data.Content
            c.data.From = c.data.User
            //Increase in online population
            user_list = append(user_list, c.data.User)
            c.data.UserList = user_list
            data_b, _ := json.Marshal(c.data)
            //Send message
            h.b <- data_b
        case "user":
            c.data.Type = "user"
            data_b, _ := json.Marshal(c.data)
            h.b <- data_b
        //User logout
        case "logout":
            c.data.Type = "logout"
            //Fewer people online
            user_list = del(user_list, c.data.User)
            data_b, _ := json.Marshal(c.data)
            //Delete connection
            h.b <- data_b
            //Send user offline information
            h.r <- c
        default:
            fmt.Print("========default================")
        }
    }
}

//Delete logged out users and maintain the list of online users
func del(slice []string, user string) []string {
    count := len(slice)
    if count == 0 {
        return slice
    }
    if count == 1 && slice[0] == user {
        return []string{}
    }
    var n_slice = []string{}
    for i := range slice {
        if slice[i] == user && i == count {
            return slice[:count]
        } else if slice[i] == user {
            n_slice = append(slice[:i], slice[i+1:]...)
            break
        }
    }
    return n_slice
}

//Quit
func logout(c *connection){
    c.data.Type = "logout"
    user_list = del(user_list, c.data.User)
    c.data.UserList = user_list
    c.data.Content = c.data.User
    data_b, _ := json.Marshal(c.data)
    h.b <- data_b
    h.r <- c
}

Client code implementation:

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta http-equiv="content-type" content="text/html;charset=utf-8">
    <style>
        p {
            text-align: left;
            padding-left: 20px;
        }
    </style>
</head>
<body>
<div style="width: 800px;height: 600px;margin: 30px auto;text-align: center">
    <div style="width: 800px;border: 1px solid gray;height: 300px;">
        <div style="width: 200px;height: 300px;float: left;text-align: left;">
            <p>< span > currently online: < / span > < span id = "user_num" > 0 < / span ></p>
            <div id="user_list" style="overflow: auto;">
            </div>
        </div>
        <div id="msg_list" style="width: 598px;border:  1px solid gray; height: 300px;overflow: scroll;float: left;">
        </div>
    </div>
    <br>
    <textarea id="msg_box" rows="6" cols="50" onkeydown="confirm(event)"></textarea><br>
    < input type = "button" value = "send" onclick = "send()" >
</div>
</body>
</html>
<script type="text/javascript">
    Var uname = prompt ('Please enter user name ',' user '+ UUID (8, 16));
    var ws = new WebSocket("ws://127.0.0.1:8080/ws");
    ws.onopen = function () {
        Var data = "system message: connection established successfully";
        listMsg(data);
    };
    ws.onmessage = function (e) {
        var msg = JSON.parse(e.data);
        var sender, user_name, name_list, change_type;
        switch (msg.type) {
            case 'system':
                Sender = 'system message:';
                break;
            case 'user':
                sender = msg.from + ': ';
                break;
            case 'handshake':
                var user_info = {'type': 'login', 'content': uname};
                sendMsg(user_info);
                return;
            case 'login':
            case 'logout':
                user_name = msg.content;
                name_list = msg.user_list;
                change_type = msg.type;
                dealUser(user_name, change_type, name_list);
                return;
        }
        var data = sender + msg.content;
        listMsg(data);
    };
    ws.onerror = function () {
        Var data = "system message: error, please exit and try again";
        listMsg(data);
    };
    function confirm(event) {
        var key_num = event.keyCode;
        if (13 == key_num) {
            send();
        } else {
            return false;
        }
    }
    function send() {
        var msg_box = document.getElementById("msg_box");
        var content = msg_box.value;
        var reg = new RegExp("\r\n", "g");
        content = content.replace(reg, "");
        var msg = {'content': content.trim(), 'type': 'user'};
        sendMsg(msg);
        msg_box.value = '';
    }
    function listMsg(data) {
        var msg_list = document.getElementById("msg_list");
        var msg = document.createElement("p");
        msg.innerHTML = data;
        msg_list.appendChild(msg);
        msg_list.scrollTop = msg_list.scrollHeight;
    }
    function dealUser(user_name, type, name_list) {
        var user_list = document.getElementById("user_list");
        var user_num = document.getElementById("user_num");
        while(user_list.hasChildNodes()) {
            user_list.removeChild(user_list.firstChild);
        }
        for (var index in name_list) {
            var user = document.createElement("p");
            user.innerHTML = name_list[index];
            user_list.appendChild(user);
        }
        user_num.innerHTML = name_list.length;
        user_list.scrollTop = user_list.scrollHeight;
        var change = type == 'login' ? ' Online ':' offline ';
        Var data = 'system message:' + user_ Name + 'changed' + change;
        listMsg(data);
    }
    function sendMsg(msg) {
        var data = JSON.stringify(msg);
        ws.send(data);
    }
    function uuid(len, radix) {
        var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
        var uuid = [], i;
        radix = radix || chars.length;
        if (len) {
            for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
        } else {
            var r;
            uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
            uuid[14] = '4';
            for (i = 0; i < 36; i++) {
                if (!uuid[i]) {
                    r = 0 | Math.random() * 16;
                    uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
                }
            }
        }
        return uuid.join('');
    }
</script>

go

This work adoptsCC agreement, reprint must indicate the author and the link to this article