go network programming-websocket

1. WebSocket programming

1.1.1. What is webSocket

WebSocket is a protocol for full-duplex communication on a single TCP connection.
WebSocket makes the data exchange between the client and the server easier, allowing the server to actively push data to the client.
In the WebSocket API, the browser and the server It only needs to complete a handshake, and a persistent connection can be created directly between the two, and two-way data transmission needs to be
installed. A third-party package needs to be installed:
In cmd: go get -u -v github.com/gorilla/websocket

1.1.2. A small example of a chat room

Note:
Here you need to run four go files at the same time in the terminal
insert image description here

Create four new go files connection.go|data.go|hub.go|server.go in the same directory

run

go run server.go hub.go data.go connection.go

Execute the local.html file after running

server.go file code

package main

import (
    "fmt"
    "net/http"

    "github.com/gorilla/mux"
)

func main() {
    
    
    router := mux.NewRouter()
    go h.run()
    router.HandleFunc("/ws", myws)
    if err := http.ListenAndServe("127.0.0.1:8080", router); err != nil {
    
    
        fmt.Println("err:", err)
    }
}

hub.go file code

package main

import "encoding/json"

var h = hub{
    
    
    c: make(map[*connection]bool),
    u: make(chan *connection),
    b: make(chan []byte),
    r: make(chan *connection),
}

type hub struct {
    
    
    c map[*connection]bool
    b chan []byte
    r chan *connection
    u chan *connection
}

func (h *hub) run() {
    
    
    for {
    
    
        select {
    
    
        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)
            c.sc <- data_b
        case c := <-h.u:
            if _, ok := h.c[c]; ok {
    
    
                delete(h.c, c)
                close(c.sc)
            }
        case data := <-h.b:
            for c := range h.c {
    
    
                select {
    
    
                case c.sc <- data:
                default:
                    delete(h.c, c)
                    close(c.sc)
                }
            }
        }
    }
}

data.go file code

package main

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"`
}
connection.go文件代码
package main

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

    "github.com/gorilla/websocket"
)

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

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

func myws(w http.ResponseWriter, r *http.Request) {
    
    
    ws, err := wu.Upgrade(w, r, nil)
    if err != nil {
    
    
        return
    }
    c := &connection{
    
    sc: make(chan []byte, 256), ws: ws, data: &Data{
    
    }}
    h.r <- c
    go c.writer()
    c.reader()
    defer func() {
    
    
        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
    }()
}

func (c *connection) writer() {
    
    
    for message := range c.sc {
    
    
        c.ws.WriteMessage(websocket.TextMessage, message)
    }
    c.ws.Close()
}

var user_list = []string{
    
    }

func (c *connection) reader() {
    
    
    for {
    
    
        _, message, err := c.ws.ReadMessage()
        if err != nil {
    
    
            h.r <- c
            break
        }
        json.Unmarshal(message, &c.data)
        switch c.data.Type {
    
    
        case "login":
            c.data.User = c.data.Content
            c.data.From = c.data.User
            user_list = append(user_list, c.data.User)
            c.data.UserList = user_list
            data_b, _ := json.Marshal(c.data)
            h.b <- data_b
        case "user":
            c.data.Type = "user"
            data_b, _ := json.Marshal(c.data)
            h.b <- data_b
        case "logout":
            c.data.Type = "logout"
            user_list = del(user_list, c.data.User)
            data_b, _ := json.Marshal(c.data)
            h.b <- data_b
            h.r <- c
        default:
            fmt.Print("========default================")
        }
    }
}

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
        }
    }
    fmt.Println(n_slice)
    return n_slice
}

local.html file code

<!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">
    <h1>www.5lmh.comy演示聊天室</h1>
    <div style="width: 800px;border: 1px solid gray;height: 300px;">
        <div style="width: 200px;height: 300px;float: left;text-align: left;">
            <p><span>当前在线:</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="发送" onclick="send()">
</div>
</body>
</html>
<script type="text/javascript">
    var uname = prompt('请输入用户名', 'user' + uuid(8, 16));
    var ws = new WebSocket("ws://127.0.0.1:8080/ws");
    ws.onopen = function () {
      
      
        var data = "系统消息:建立连接成功";
        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 = '系统消息: ';
                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 = "系统消息 : 出错了,请退出重试.";
        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' ? '上线' : '下线';
        var data = '系统消息: ' + user_name + ' 已' + 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>

![在这里插入图片描述](https://img-blog.csdnimg.cn/64552642dadb45a18bcfc34020cfff40.png)
多开几个网页就可以呀聊天

Guess you like

Origin blog.csdn.net/qq_48975137/article/details/129329236