zoukankan      html  css  js  c++  java
  • websocket

    理论:

    它基于TCP传输协议,并复用HTTP的握手通道,属于应用层协议

    Socket 是传输控制层协议,WebSocket 是应用层协议。

    1. 建立连接
    2. 交换数据
    3. 数据帧格式
    4. 维持连接

    1.建立连接

    首先,客户端发起协议升级请求。可以看到,采用的是标准的HTTP报文格式,且只支持GET方法。[将数据放到socket里发送]

    GET / HTTP/1.1
    Host: localhost:8080
    Origin: http://127.0.0.1:3000
    Connection: Upgrade       # 表示要升级协议
    Upgrade: websocket        # 表示要升级到websocket协议。
    Sec-WebSocket-Version: 13    # 表示websocket的版本。如果服务端不支持该版本,需要返回一个Sec-WebSocket-Versionheader,里面包含服务端支持的版本号。
    Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==   # 与后面服务端响应首部的Sec-WebSocket-Accept是配套的,提供基本的防护,比如恶意的连接,或者无意的连接。

    服务端返回内容如下,状态代码101表示协议切换。到此完成协议升级,后续的数据交互都按照新的协议来。

    HTTP/1.1 101 Switching Protocols
    Connection:Upgrade
    Upgrade: websocket
    Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=    #Sec-WebSocket-Accept由Sec-WebSocket-Key计算得出: toBase64( sha1( Sec-WebSocket-Key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 )  )

    magic_string是什么?【固定值】: 258EAFA5-E914-47DA-95CA-C5AB0DC85B11

    2.交换数据

    一旦WebSocket客户端、服务端建立连接后,后续的操作都是基于数据帧的传递。

    WebSocket根据opcode来区分操作的类型。比如0x8表示断开连接,0x0-0x2表示数据交互。

    3.数据帧格式

    4.维持连接

    发送心跳,ping  pong

    具体使用:

    浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。

    当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。

    以下 API 用于创建 WebSocket 对象。

     

    示例:web聊天室应用

    原理,一个websocket是一个用户,服务端可以将用户A的消息推送给所有用户,服务端维护一个用户列表和一个历史消息列表。

    访问http://127.0.0.1:8888/ 即打开群聊窗口,显示所有历史消息。用户发送的消息能被所有客户端实时看到。用户关闭浏览器,服务端用户列表-1

    目录结构:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>web聊天室</title>
    </head>
    <body>
        <div>
            <input type="text" id="txt"/>
            <input type="button" id="btn" value="提交" onclick="sendMsg();"/>
            <input type="button" id="close" value="关闭连接" onclick="closeConn();"/>
        </div>
        <div id="container" style="border: 1px solid #dddddd;margin: 20px;min-height: 500px;">
    
        </div>
    
        <script src="/static/jquery-2.1.4.min.js"></script>
        <script type="text/javascript">
            $(function () {
                wsUpdater.start();  //页面一加载就开始执行,目的是初始化websocket连接,让websocket处于就绪状态
            });
    
            var wsUpdater = {
                socket: null,
                uid: null,
                start: function() {
                    var url = "ws://127.0.0.1:8888/chat";   // 定义websocket请求地址
                    wsUpdater.socket = new WebSocket(url);  // 初始化websocket时传入url,建立websocket连接
                    // websocket对象的onmessage事件,收到消息触发,收到的消息存放在event.data中。刚建立连接就会收到服务端的UUID
                    wsUpdater.socket.onmessage = function(event) {
                        if(wsUpdater.uid){
                            wsUpdater.showMessage(event.data);
                        }else{
                            wsUpdater.uid = event.data;   //刚建立连接时,将服务器发来的UUID赋值给uid
                        }
                    }
                },
                showMessage: function(content) {
                    $('#container').append(content);
                },
                stop:function () {
                    console.log(wsUpdater.socket);
                    wsUpdater.socket.stop();
                }
            };
    
            function sendMsg() {   //点击发送按钮
                var msg = {
                    uid: wsUpdater.uid,
                    message: $("#txt").val()
                };
                wsUpdater.socket.send(JSON.stringify(msg));
            }
    
            function closeConn() {
                wsUpdater.stop();
                alert("关闭成功")
            }
    
    </script>
    
    </body>
    </html>
    index.html

    message.html >>>

    <div style="border: 1px solid #dddddd;margin: 10px;">
        <div>游客{{uid}}</div>
        <div style="margin-left: 20px;">{{message}}</div>
    </div>
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import uuid
    import json
    import tornado.ioloop
    import tornado.web
    import tornado.websocket
    
    
    class IndexHandler(tornado.web.RequestHandler):
        def get(self):
            self.render('index.html')
    
    
    class ChatHandler(tornado.websocket.WebSocketHandler):   # 继承WebSocketHandler
        # 用户存储当前聊天室用户
        waiters = set()
        # 用于存储历时消息
        messages = []
    
        def open(self):
            """
            客户端websocket连接成功时,自动执行
            :return: 
            """
            ChatHandler.waiters.add(self)    # websocket连一次就是一个新的self
            uid = str(uuid.uuid4())
            self.write_message(uid)     # 若成功建立连接,将uid发给来连接的客户端
    
            print("一个新的websocket加入了。。uid已发给客户端", self)
            print("当前聊天室人数", len(ChatHandler.waiters))
    
            for msg in ChatHandler.messages:    # 刚连上,ChatHandler.messages为历史消息,直接推送给刚登陆进来的用户
                content = self.render_string('message.html', **msg)
                self.write_message(content)
    
        def on_message(self, message):
            """
            客户端连发送消息时,自动执行
            :param message: 
            :return: 
            """
            msg = json.loads(message)
            ChatHandler.messages.append(msg)
    
            for client in ChatHandler.waiters:     # 接收到消息,将消息推送给所有websocket用户
                content = client.render_string('message.html', **msg)
                client.write_message(content)
    
        def on_close(self):
            """
            客户端关闭连接时,,自动执行
            :return: 
            """
            ChatHandler.waiters.remove(self)
            print("当前聊天室人数", len(ChatHandler.waiters))
    
    
    def run():
        settings = {
            'template_path': 'templates',
            'static_path': 'static',
        }
        application = tornado.web.Application([
            (r"/", IndexHandler),
            (r"/chat", ChatHandler),
        ], **settings)
        application.listen(8888)
        tornado.ioloop.IOLoop.instance().start()
    
    
    if __name__ == "__main__":
        run()
    app.py

    websocket实现打印后端执行进度

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import time
    import json
    import tornado.ioloop
    import tornado.web
    import tornado.websocket
    
    
    class IndexHandler(tornado.web.RequestHandler):
        def get(self):
            self.render('index.html')
    
    
    class ChatHandler(tornado.websocket.WebSocketHandler):  # 继承WebSocketHandler
        def on_message(self, message):
            """
            客户端连发送消息时,自动执行
            :param message: 
            :return: 
            """
            msg = json.loads(message)
            self.write_message("收到你的请求了,开始将excel载入内存...")
            time.sleep(5)
            self.write_message("载入内存完成,开始在内存中遍历解析excel...")
            time.sleep(5)
            self.write_message("解析数据完成,开始入库...")
            time.sleep(5)
            self.write_message("入库完成,报送成功")
            self.close()
    
    
    def run():
        settings = {
            'template_path': 'templates',
            'static_path': 'static',
        }
        application = tornado.web.Application([
            (r"/", IndexHandler),
            (r"/chat", ChatHandler),
        ], **settings)
        application.listen(8888)
        tornado.ioloop.IOLoop.instance().start()
    
    
    if __name__ == "__main__":
        run()
    app.py
  • 相关阅读:
    分布式搜索elasticsearch 基本概念
    hdoj 4828 卡特兰数取模
    Tyvj3308毒药解药题解
    安全类工具制作第004篇:进程管理器(上)
    UVA
    G 全然背包
    指针结构体函数-事实上能够这样具体理解
    关于大型站点技术演进的思考(七)--存储的瓶颈(7)
    Testing Is the Engineering Rigor of Software Development
    poj 1273 Drainage Ditches(最大流)
  • 原文地址:https://www.cnblogs.com/staff/p/13164374.html
Copyright © 2011-2022 走看看