Tornado编程-WebSocket原理及代码示例

一. WebSocket协议

WebSocket 协议本质上是一个基于 TCP 的协议。

为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息”Upgrade: WebSocket”表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。

由于这个规范目前还是处于草案阶段,版本的变化比较快,通常采用 draft-hixie-thewebsocketprotocol-76 版本来描述 WebSocket 协议,详情可访问 http://dev.w3.org/html5/websockets/。

二. Firefox的WebSocket实现
1. 请求

GET /send HTTP/1.1
Accept:	text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Cache-Control: no-cache
Connection: keep-alive, Upgrade
Host: 127.0.0.1:8200
Origin: http://127.0.0.1:8200
Pragma: no-cache
Sec-WebSocket-Key: KOySRTQgzqImO5s57OkTPA==
Sec-WebSocket-Version: 13
Upgrade: websocket
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:30.0) Gecko/20100101 Firefox/30.0

2. 应答

HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Sec-WebSocket-Accept: rr+f1d2NTgfAJeQ8t6/kzHli9ao=
Upgrade: websocket

三. 基于Tornado3.1的WebSocket编程实现
1.HTML示例
注:基于jQuery、Bootstrap,同时将websocket.html保存在websocket.py同级的template目录下

<html>
<head>  
<link href="http://cdn.bootcss.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<link href="http://cdn.bootcss.com/bootstrap/3.2.0/css/bootstrap-theme.min.css" rel="stylesheet">
<script src="http://cdn.bootcss.com/jquery/2.1.1/jquery.min.js"></script>
<script src="http://cdn.bootcss.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script>  
function send() {  
    var ws = new WebSocket("ws://127.0.0.1:8200/send");  

    var data = {
        input:$("#input_data").val(),
    };

    $("#message").empty();
    ws.onopen = function() {
        ws.send(JSON.stringify(data));  
    };

    $("#message").append('<div class="panel-body"><p>');
    ws.onmessage = function(event) {  
        $("#message").append(JSON.parse(event.data).input + "<br>");
    };

    ws.onclose = function(event) {
        $("#message").append('</p></div>');
    };
}
</script>
</head>
<body>
  <div id="test">
  <form class="form-horizontal" role="form">
    <div class="panel panel-default">
      <div class="panel-heading">
        <h5 class="panel-title">>>输入</h5>
      </div>
      <div class="panel-body">
        <div class="form-group">
          <div class="col-md-8">
            <input type="text" class="form-control" id="input_data" value="">
          </div>
        </div>
        <div class="form-group">
          <div class="col-md-8">
            <button type="submit" class="btn btn-success" id="input_btn" onclick="send();">发送</button>
          </div>
        </div>
      </div>
    </div>
  </form>
  <div class="panel panel-default">
    <div class="panel-heading">
      <h5 class="panel-title">>> 输出</h5>
    </div>
    <div class="panel-body">
      <div id="message"></div>
    </div>
  </div>
</body>
</html>

2.python示例

import os
import sys
import json
import time
import tornado.httpserver
import tornado.web
import tornado.ioloop
from tornado import websocket

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("websocket.html")

class SendHandler(websocket.WebSocketHandler):
    clients = set()

    def open(self):
        SendHandler.clients.add(self)
        self.write_message(json.dumps({'input': 'connected...'}))
        self.stream.set_nodelay(True)

    def on_message(self, message):
        message = json.loads(message)
        self.write_message(json.dumps({'input': 'response...'}))
        i = 0
        while i <= 10:
            i += 1
            self.write_message(json.dumps(message))
            time.sleep(1)
        # 服务器主动关闭
        self.close()
        SendHandler.clients.remove(self)

    def on_close(self):
        # 客户端主动关闭
        SendHandler.clients.remove(self)

if __name__ == '__main__':
    app = tornado.web.Application(
        handlers=[
            (r"/", IndexHandler),
            (r"/send", SendHandler)
        ],
        debug = False,
        template_path = os.path.join(os.path.dirname(__file__), "template"),
        static_path = os.path.join(os.path.dirname(__file__), "static")
    )
    http_server = tornado.httpserver.HTTPServer(app, xheaders=True)
    http_server.listen(8200)
    tornado.ioloop.IOLoop.instance().start()
原创声明:除非注明,本站文章均为原创!转载请注明来自 嗨!大佟! www.qmailer.net
本文链接:http://www.qmailer.net/archives/232.html

发表评论