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
原文地址:https://www.cnblogs.com/staff/p/13164374.html