• Go语言基础之WebSocket编程


    Go语言基础之WebSocket编程

    webSocket是什么

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

    举个聊天室的小例子

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

    运行

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

    运行之后执行local.html文件

    server.go文件代码

    package main // 声明 main 包,表明当前是一个可执行程序
    import (
    	"fmt"
    	"github.com/gorilla/mux"
    	"net/http"
    )
    
    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文件代码

    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文件代码

    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"
    	"github.com/gorilla/websocket"
    	"net/http"
    )
    
    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文件代码

    <!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=" 800px;height: 600px;margin: 30px auto;text-align: center">
        <h1>Randy--演示聊天室</h1>
        <div style=" 800px;border: 1px solid gray;height: 300px;">
            <div style=" 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=" 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>
    
    module gowebsocket
    
    go 1.17
    
    require (
    	github.com/gorilla/mux v1.8.0
    	github.com/gorilla/websocket v1.4.2
    )
    
    require (
    	github.com/astaxie/beego v1.12.3 // indirect
    	github.com/beorn7/perks v1.0.1 // indirect
    	github.com/cespare/xxhash/v2 v2.1.1 // indirect
    	github.com/golang/protobuf v1.4.2 // indirect
    	github.com/hashicorp/golang-lru v0.5.4 // indirect
    	github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
    	github.com/prometheus/client_golang v1.7.0 // indirect
    	github.com/prometheus/client_model v0.2.0 // indirect
    	github.com/prometheus/common v0.10.0 // indirect
    	github.com/prometheus/procfs v0.1.3 // indirect
    	github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect
    	golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 // indirect
    	golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect
    	golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 // indirect
    	golang.org/x/text v0.3.0 // indirect
    	google.golang.org/protobuf v1.23.0 // indirect
    	gopkg.in/yaml.v2 v2.2.8 // indirect
    )
    
    

    代码

    启动项目

    image-20211107181558286

    访问:

    image-20211107181523547

  • 相关阅读:
    Java(14):面向对象、封装、继承、方法重写、多态、抽象类与接口、内部类
    Java(13):数组、Arrays类、冒泡排序
    Java(12):方法、重载、命令行传参、可变参数、方法调用
    Java(11):switch、dowhile、九九乘法表、打印质数、打印三角形
    Java(10):用户交互Scanner
    Java(9):包
    Java(8):运算符
    Java(7):变量和常量及其规范、作用域
    Mybatis 打印日志
    mysql 更新数据
  • 原文地址:https://www.cnblogs.com/randysun/p/15522119.html
Copyright © 2020-2023  润新知