websocket
Websocket是HTML5开始提供的一种在单个TCP连接上进行全双工通讯的协议。HTML5规范在2014年10月29日制定完成。
Websocket允许一个建立HTTP连接的双方可以互相推送数据,这意味着不仅仅可以从客户端发送请求数据到服务端,也可以从服务端主动推送数据到客户端,这打破了服务端只能被动处理请求的传统,让一些对数据有实时性要求的app开发变得更加轻松。
以往在没有websocket的时候,为了实现这种实时数据传输,往往会在页面上创建一个定时器用于循环请求服务端数据,这对于页面来说压力过大,特别是定时器在没有正常销毁的时候,多次设定定时器很可能会导致页面卡死。在有了websocket之后,这一切都不是问题,这意味着在web上实现多人在线实时聊天、实时游戏等等成为了可能。
简单了解
websocket必须由浏览器提供支持,所幸现在HTML5已经被绝大部分浏览器支持,在使用的时候,必须还必须前后端配合。浏览器通过javascript向服务器发起websocket连接的请求之后,服务器响应连接,连接成功之后客户端和服务器就可以通过这个连接互相推送数据。
客户端通过send()方法向服务器发送数据,并通过onmessage事件来接收服务器返回的数据。
websocket连接对象包含以下几种事件,可以设置这几种事件的回调函数来实现需求:
事件 | 含义 |
---|---|
open | 连接建立 |
message | 客户端接收到服务端数据 |
error | 通信发生错误 |
close | 连接关闭 |
客户端javascript代码演示
var ws = new WebSocket("ws://xxxxxxx")
ws.onopen = function() {
alert("连接建立")
}
ws.onmessage = function(evt) {
alert("接收数据")
var msg = evt.data
}
ws.onerror = function() {
alert("发生错误")
}
ws.onclose = function() {
alert("连接关闭")
}
对于想要兼容某些不支持websocket的浏览器,你可以检测浏览器的websocket属性并进行后续处理,使用以下代码进行检查:
if ("WebSocket" in window) {
alert("浏览器支持websocket")
} else {
alert("浏览器不支持websocket")
}
一个前后端使用websocket的例子(flask && javascript with jQuery)
本例子参考使用Flask-SocketIO完成服务端和客户端的双向通信并在本地通过测试。
python采集cpu实时负载数据,前端使用echart.js库实时显示。
main.py
提供数据接口,并且处理根目录路由访问,返回www/index.html
文件。
# -*- coding:utf-8 -*-
#!/usr/bin/env python2
from flask import Flask
from flask_socketio import SocketIO
from threading import Thread, Lock
import time
import psutil
app = Flask(__name__)
socketio = SocketIO(app)
thread = None
thread_lock = Lock()
def connecting():
count = 0
while True:
cpus = psutil.cpu_percent(interval=None, percpu=True)
count += 1
t = time.strftime('%M:%S', time.localtime())
socketio.emit('test_data', {
'data': [t, cpus], 'count': count
}, namespace='/test')
socketio.sleep(3)
@socketio.on('connect', namespace="/test")
def test_connect():
global thread
with thread_lock:
if thread is None:
thread = socketio.start_background_task(target=connecting)
@app.route('/')
def index():
with open('www/index.html') as f:
content = f.read()
return content
if __name__ == "__main__":
socketio.run(app, debug=True)
index.html
放在当前位置www
目录下。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>websocket测试</title>
<script type="text/javascript" src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript" src="//cdn.bootcss.com/socket.io/1.5.1/socket.io.min.js"></script>
<script src="http://echarts.baidu.com/dist/echarts.min.js"></script>
</head>
<body>
<div id="main"></div>
<style>
#main {
height: 400px;
width: 80%;
margin: 10% auto;
}
</style>
<script>
var myChart = echarts.init(document.getElementById('main'));
myChart.setOption({
title: {
text: '测试数据'
},
tooltip: {},
legend: {
data:['CPU负载']
},
xAxis: {},
yAxis: {},
series: [{
name: 'CPU负载',
type: 'line',
data: []
}]
});
var time = ["", "", "", "", "", "", "", "", "", ""]
var cpu = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
var update_chart = function(res) {
myChart.hideLoading()
time.push(res.data[0])
cpu.push(res.data[1][0])
if (time.length >= 10) {
time.shift()
cpu.shift()
}
myChart.setOption({
xAxis: {
type: "category",
data: time
},
yAxis: {
name: "CPU负载",
type: "value"
},
series: [{
data: cpu,
type: 'line'
}]
})
}
myChart.showLoading();
$(document).ready(function() {
namesapce = "/test"
var socket = io.connect('ws://localhost:5000/test')
socket.on('test_data', function(res) {
update_chart(res)
})
})
</script>
</body>
</html>
效果演示