Tornado WebSocket

WebSocket协议

WebSocket是基于HTML5规范的网络协议,它实现了浏览器与服务器之间全双工full-duplex通信,即允许服务器主动发送消息给客户端。

WebSocket通信协议于2011年被IETF定为标准RFC 6455,并被RFC 7936所补充规范。在WebSocket的API中,浏览器和服务器只需要做一个握手动作。然后,浏览器和服务器之间就形成了一条快速通道,两者之间就直接可以用传输数据了。

WebSocket是HTML5规范中新提出的客户端与服务器之间的通信协议,协议本身使用新的ws://URL的格式。

WebSocket是独立的创建在TCP之上的协议,和HTTP唯一的关系是使用HTTP的101状态码进行协议转换,默认使用TCP端口是80,因此可以绕过大多数防火墙的限制。

WebSocket使客户端和服务器之间的数据交换变得更加简单,它允许服务器直接向客户端推送数据而无需客户端进行请求,两者之间可以创建持久化的连接,并允许数据进行双向传递。

WebSocket是一种网络通信协议,与HTTP协议不同的是,WebSocket连接允许客户端和服务器之间进行全双工通信,以便任意一方都可以通过建立的连接将数据推送到另一端。WebSocket仅需要建立一次连接就可以一直保持连接状态。

WebSocket客户端编程

浏览器通过JavaScript向服务器发出建立WebSocket连接的请求,连接建立以后,客户端和服务器就可以通过TCP连接直接交换数据。当你获取WebSocket连接后,可以通过send()方法向服务器发送数据,并通过onmessage事件来接收服务器返回的数据。

$ mkdir static
$ mkdir template
$ vim server.py
#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from tornado.options import define, options
from tornado.web import Application, RequestHandler
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from tornado.websocket import WebSocketHandler

import os, datetime

define("port", type=int, default=8000)

class IndexHandler(RequestHandler):
    def get(self):
        self.render("index.html")

class MessageHandler(WebSocketHandler):
    connections = set()
    def open(self):
        print("message open")
        self.connections.add(self)
    def on_message(self, message):
        print("message send: %s" % message)
        ip = self.request.remote_ip
        dt = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%s")
        print(ip)
        for conn in self.connections:
            conn.write_message("[%s] %s : %s " % (dt, ip, message))
    def on_close(self):
        print("message close")
        if conn in self.connections:
            self.connections.remove(conn)

class App(Application):
    def __init__(self):
        handlers = [
            (r"/index", IndexHandler),
            (r"/message", MessageHandler)
        ]
        settings = dict(
            debug = True,
            cookie_secret = "m2ho0d9",
            static_path = os.path.join(os.path.dirname(__file__), "static"),
            template_path = os.path.join(os.path.dirname(__file__), "template")
        )
        Application.__init__(self, handlers, **settings)

def main():
    options.parse_command_line()
    app = App()
    server = HTTPServer(app)
    server.listen(options.port)
    IOLoop.current().start()

if __name__ == "__main__":
    main()
$ vim template/index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>websocket</title>
  <link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<div class="container">
  <div class="form-group">
    <label>消息</label>
    <textarea class="form-control" rows="3" id="message"></textarea>
  </div>
  <button type="button" class="btn btn-primary" id="send">发送</button>
  <div id="result"></div>
</div>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>""
<script src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
<script>
if("WebSocket" in window){
    var ws = new WebSocket("ws://192.168.56.103:8000/message");
    ws.onopen = function(){
        console.log("websocket open");
    };
    ws.onmessage = function(evt){
        var data = evt.data;
        console.log(data);
    };
    ws.onclose = function(){
        console.log("websocket close");
    };
    $("#send").on("click", function(){
        var message = $("#message").val();
        console.log(message);
        if(message !== ""){
            ws.send(message);
        }
    });
}else{
    console.log("browser not support WebSocket");
}
</script>
</body>
</html>

CentOS防火墙开启指定端口供外部访问

# 查看状态
$ firewall-cmd --state
$ firewall-cmd --get-services
$ firewall-cmd --reload
$ firewall-cmd --version
$ firewall-cmd --help

# 防火墙添加端口
$ firewall-cmd --zone=public --add-port=8000/tcp --permanent

# 重新载入
$ firewall-cmd --reload

# 查看
$ firewall-cmd --zone=public --query-port=8000/tcp
$ firewall-cmd --list-services
$ firewall-cmd --zone=public --list-ports

# 删除
$ firewall-cmd --zone=public --remove-port=8000/tcp --permanent

Tornado的WebSocket模块

Tornado提供支持WebSocket的模块是tornado.websocket,并提供了WebSocketHandler类用于处理通信。

WebSocketHandler类提供的方法包括

# 当一个WebSocket连接建立后被调用
WebSocketHandler.open()

# 当客户端发送消息过来时被调用,此方法必须被重写。
WebSocketHandler.on_message(message)

# 当WebSocket连接关闭后调用
WebSocketHandler.on_close()

# 向客户端发送消息,消息可以是字符串或字典(字典会被转换为JSON字符串),若binary为False则消息会以utf8编码发送,否则以二进制格式发送。
WebSocketHandler.write_message(message, binary=False)

# 关闭WebSocket连接
WebSocketHandler.close()

# 判断请求源,对于符合条件的请求源允许其连接,否则返回403。可重写此方法来解决WebSocket的跨域请求。
WebSocketHandler.check_origin(origin)

例如:使用浏览器中WebSocket协议访问服务器

$ mkdir static
$ mkdir template
$ vim server.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import os

from tornado.options import define, options
from tornado.web import Application, RequestHandler
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from tornado.websocket import WebSocketHandler

define("port", type=int, default=8000, help="defualt port 8000")

class IndexHandler(RequestHandler):
    def get(self):
        self.render("index.html")

class WSHandler(WebSocketHandler):
    connections = set()
    def open(self):
        self.connections.add(self)
        for conn in self.connections:
            conn.write_message(self.request.remote_ip)
    def on_message(self, message):
        for conn in self.connections:
            conn.write_message(message)
        print(message)
    def on_close(self):
        self.connections.remove(self)
    def check_origin(self, origin):
        return True
    @classmethod
    def send_demand_updates(cls, message):
        for conn in cls.connections:
            conn.write_message(message)

class App(Application):
    def __init__(self):
        handlers = [
            (r"/index", IndexHandler),
            (r"/websocket", WSHandler)
        ]
        staticPath = os.path.join(os.path.dirname(__file__), "static")
        templatePath = os.path.join(os.path.dirname(__file__), "template")
        settings = dict(
            debug=True,
            static_path = staticPath,
            template_path = templatePath
        )
        Application.__init__(self, handlers, **settings)

def main():
    options.parse_command_line()
    app = App()
    server = HTTPServer(app)
    server.listen(options.port)
    IOLoop.current().start()

if __name__ == "__main__":
    main()
$ vim template/index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>websocket</title>
</head>
<body>
<script>
var ws = new WebSocket("ws://127.0.0.1:8000/websocket");
ws.onopen = function(){
  ws.send("hello world");
}
ws.onmessage = function(evt){
  console.log(evt.data);
}
</script>
</body>
</html>
$ python server.py

运行测试,在浏览器中输入http://127.0.0.1/index查看控制台输出内容,查看服务器命令行输出内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值