zoukankan      html  css  js  c++  java
  • [go]websocket

    web实时更新技术

    • http的特点
    1. 半双工: 同一个时刻,只能单向传数据(request/response).
    2. 服务器不能主动给客户端推消息

    • 轮询(polling)
      不断的建立http连接,严重浪费了服务器端和客户端的资源. 人越多,服务器压力越大.
    - server.js
    
    let express = require('express');
    let app = express();
    app.use(express.static(__dirname));
    
    app.get("/clock", function (req, res) {
        res.end(new Date().toLocaleTimeString());
    });
    app.listen(8080);
    
    - client
    
    <body>
    <div id="clock"></div>
    <script>
        setInterval(function () {
            let xhr = new XMLHttpRequest();
            xhr.open('GET', 'http://localhost:8080/clock', true);
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    document.querySelector('#clock').innerHTML = xhr.responseText;
                }
            };
            xhr.send();
        }, 1000);
    </script>
    </body>
    
    - 访问http://localhost:8080/clock
    
    • 长轮询(long polling)(comet)
      当一次请求完成后, 在发起进行下一次
    - client
    
    
    <body>
    <div id="clock"></div>
    <script>
        setInterval(function () {
            let xhr = new XMLHttpRequest();
            xhr.open('GET', 'http://localhost:8080/clock', true);
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    document.querySelector('#clock').innerHTML = xhr.responseText;
                }
            };
            xhr.send();
        }, 1000);
    </script>
    </body>
    
    - server.js
    
    
    let express = require('express');
    let app = express();
    app.use(express.static(__dirname));
    
    app.get("/clock", function (req, res) {
        //优化: 当时间为每隔5的倍数才返回.
        let $timer = setInterval(function () {
            let date = new Date();
            let seconds = date.getSeconds();
            if (seconds % 5 === 0) {
                res.end(new Date().toLocaleTimeString());
                clearInterval($timer)
            }
        }, 1000);
    
    });
    app.listen(8080);
    
    • iframe流
      打开浏览器会主动请求iframe页
      iframe可以调用parent父类的方法
      缺点: server不断开连接.浏览器一直转圈.
    - server.js
    const express = require('express');
    const app = express();
    app.use(express.static(__dirname));
    app.get('/clock', function (req, res) {
        res.header("Content-Type", "text/html");
        setInterval(function () {
            res.write(`
                <script type="text/javascript">
                    parent.setTime("${new Date().toLocaleTimeString()}");
                </script>
            `);
        }, 1000);
    });
    app.listen(8080);
    
    - client
    
    <body>
    <div id="clock"></div>
    <iframe src="/clock" style="display:none"></iframe>
    <script>
        function setTime(ts) {
            document.querySelector('#clock').innerHTML = ts;
        }
    </script>
    </body>
    
    • 长连接(SSE)
    SSE的简单模型是:
    	一个客户端去从服务器端订阅一条流,
    	之后服务端可以发送消息给客户端直到服务端或者客户端关闭该“流”,
    	所以eventsource也叫作"server-sent-event`
    
    MIME格式为text/event-stream
    必须编码成utf-8的格式
    消息的每个字段使用"
    "来做分割,最后用
    
    表示结束
    常用的消息key
    	Event: 事件类型,消息名称要和前端对应,如定义的event: xxx, 则前端可以用onxxx对应
    	Data: 发送的数据
    	ID: 每一条事件流的ID
    
    不支持跨域
    
    - server
    
    let express = require('express');
    let app = express();
    app.use(express.static(__dirname));
    let sendCount = 1;
    app.get('/eventSource', function (req, res) {
        res.header('Content-Type', 'text/event-stream',);
        setInterval(() => {
            res.write(`id:${sendCount++}
    event:message
    data:${new Date().toLocaleTimeString()}
    
    `);
        }, 1000)
    });
    app.listen(8080);
    
    - client
    
    <body>
    <div id="clock"></div>
    <script>
        var eventSource = new EventSource('/eventSource');
        eventSource.onmessage = function (e) {
            document.querySelector('#clock').innerHTML =e.data
        };
        eventSource.onerror = function (err) {
            console.log(err);
        }
    </script>
    </body>
    
    • WebSocket
    - server.js
    
    const path = require('path');
    let app = express();
    let server = require('http').createServer(app);
    app.get('/', function (req, res) {
        res.sendFile(path.resolve(__dirname, 'index.html'));
    });
    app.listen(3000);
    
    
    //-----------------------------------------------
    let WebSocketServer = require('ws').Server;
    let wsServer = new WebSocketServer({ port: 8888 });
    wsServer.on('connection', function (socket) {
        console.log('连接成功');
        socket.on('message', function (message) {
            console.log('接收到客户端消息:' + message);
            socket.send('服务器回应:' + message);
        });
    });
    
    - client
    
        <script>
            let ws = new WebSocket('ws://localhost:8888');
            ws.onopen = function () {
                console.log('客户端连接成功');
                ws.send('hello');
            }
            ws.onmessage = function (event) {
                console.log('收到服务器的响应 ' + event.data);
            }
        </script>
    

    go websocket

    - server.go: gorilla/websocket

    package main
    
    import (
    	"github.com/gorilla/websocket"
    	"log"
    	"net/http"
    )
    
    var upgrader = websocket.Upgrader{
    	// 解决跨域问题
    	CheckOrigin: func(r *http.Request) bool {
    		return true
    	},
    }
    
    func main() {
    	http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
    		//升级w, 此后用conn读写消息
    		conn, err := upgrader.Upgrade(w, r, nil)
    
    		if err != nil {
    			log.Println(err)
    			return
    		}
    		for {
    			messageType, p, err := conn.ReadMessage() //会读阻塞
    			if err != nil {
    				log.Println(err)
    				return
    			}
    			if err := conn.WriteMessage(messageType, p); err != nil {
    				log.Println(err)
    				return
    			}
    		}
    	})
    	log.Fatal(http.ListenAndServe(":3000", nil))
    }
    

    WebSocket 教程: 浏览器api

    • 最简单的ws的客户端
    <script>
    
        let ws = new WebSocket("wss://echo.websocket.org");
        //打开ws连接
        ws.onopen = function (event) {
            console.log("Connection open ...");
            ws.send("Hello WebSockets!");
        };
        //收到消息时回调
        ws.onmessage = function (event) {
            console.log("Received Message: " + event.data);
            ws.close();
        };
        //关闭连接时回调(直接x掉浏览器,服务端也会感知)
        ws.onclose = function (event) {
            console.log("Connection closed.");
        };
    </script>
    
    • 支持手动连接,反复收发的客户端
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
    
    </head>
    <body>
    <table>
        <tr>
            <td valign="top" width="50%">
                <form>
                    <button id="open">Open</button>
                    <button id="close">Close</button>
                    <br>
    
                    <input id="input" type="text" value="Hello world!">
                    <button id="send">Send</button>
                </form>
            </td>
    
            <td valign="top" width="50%">
                <div id="output"></div>
            </td>
        </tr>
    </table>
    
    <script>
        window.addEventListener("load", function (event) {
            let output = document.getElementById("output");
            let input = document.getElementById("input");
            let ws;
    
            let print = function (message) {
                let d = document.createElement("div");
                d.innerHTML = message;
                output.appendChild(d);
            };
    
            document.getElementById("open").onclick = function (event) {
                if (ws) return false;
    
                ws = new WebSocket("ws://localhost:3000/ws");
                ws.onopen = function (event) {
                    print("OPEN");
                };
                ws.onclose = function (event) {
                    print("CLOSE");
                    ws = null;
                };
                ws.onmessage = function (event) {
                    print("RESPONSE: " + event.data);
                };
                ws.onerror = function (event) {
                    print("ERROR: " + event.data);
                };
                return false;
            };
            document.getElementById("send").onclick = function (event) {
                if (!ws) {
                    return false;
                }
                print("SEND: " + input.value);
                ws.send(input.value);
                return false;
            };
            document.getElementById("close").onclick = function (event) {
                if (!ws) {
                    return false;
                }
                ws.close();
                return false;
            };
        });
    </script>
    </body>
    </html>
    
  • 相关阅读:
    mysql读写分离
    mysql主从同步
    mysql修改密码
    mysql单机安装以及语法,alter表
    zabbix主动监控
    数据集
    K-近邻算法
    常规套路
    区分子类方法中重名的三种变量
    Java的数学工具类Math
  • 原文地址:https://www.cnblogs.com/iiiiiher/p/12209115.html
Copyright © 2011-2022 走看看