• Go语言网络编程(socket编程)WebSocket编程


    1、WebSocket编程

    1.1.1. webSocket是什么

    WebSocket是一种在单个TCP连接上进行全双工通信的协议
    WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据
    在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输
    需要安装第三方包:
    cmd中:go get -u -v github.com/gorilla/websocket

    1.1.2.举个聊天室的小例子

    在同一级目录下新建四个go文件connection.go|data.go|hub.go|server.go

    运行

        go run server.go hub.go data.go connection.go
    
    • 1

    运行之后执行local.html文件

    server.go文件代码

    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)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    hub.go文件代码

    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)
                    }
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    data.go文件代码

    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"`
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    local.html文件代码

    <!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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
  • 相关阅读:
    CORDIC based Signal Processor desgn
    Java注解@Transa1ctional失效特殊情况
    ElasticSearch基本操作
    租用好服务器后怎么操作?
    Docker学习-Docker的入门与安装
    机器学习基础篇(4)滤波器
    C# 通过ARP技术来观察目标主机数据包
    算法面试高频题解指南【一】
    鸿蒙开发游戏(一)---大鱼吃小鱼(界面部署)
    countdownlatch 和 completableFuture 和 CyclicBarrier
  • 原文地址:https://blog.csdn.net/qq_44139121/article/details/132763294