flask基础 websocket ()
什么是WebSocket?看过html5的同学都知道,WebSocket protocol 是HTML5一种新的协议。它是实现了浏览器与服务器全双工通信(full-duplex)。HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽并达到实时通讯。现在我们来探讨一下html5的WebSocket
在WebSocket出现之前,一般通过两种方式来实现Web实时用:轮询机制和流技术;其中轮询有不同的轮询,还有一种叫Comet的长轮询。
轮询:这是最早的一种实现实时 Web
应用的方案。客户端以一定的时间间隔向服务端发出请求,以频繁请求的方式来保持客户端和服务器端的同步。这种同步方案的缺点是,当客户端以固定频率向服务
器发起请求的时候,服务器端的数据可能并没有更新,这样会带来很多无谓的网络传输,所以这是一种非常低效的实时方案。
长轮询:是对定时轮询的改进和提高,目地是为了降低无效的网络传输。当服务器端没有数据更新的时候,连接会保持一段时间周期直到数据或状态改变或者 时间过期,通过这种机制来减少无效的客户端和服务器间的交互。当然,如果服务端的数据变更非常频繁的话,这种机制和定时轮询比较起来没有本质上的性能的提 高。
WebSocket 协议本质上是一个基于 TCP 的协议。为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息”Upgrade: WebSocket”表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。
websocket 与轮询
轮询:
不断向服务器发起询问,服务器还不断的回复
浪费带宽,浪费前后端资源
保证数据的实时性
长轮询:
1.客户端向服务器发起消息,服务端轮询,放在另外一个地方,客户端去另外一个地方去拿
2.服务端轮询,放在另外一个地方,直接推给客户端
释放客户端资源,服务压力不可避免,节省带宽资源
数据不能实时性
websocket:是一个新的协议 Socket-io
1.前后端hold住
2.建立长链接
彻底解决实时性
解决占用带宽的问题
解决资源
from flask import Flask,request from geventwebsocket.websocket import WebSocket # 导入这个其实没用,只是用来在敲代码的时候能有提示。 from gevent.pywsgi import WSGIServer from geventwebsocket.handler import WebSocketHandler app = Flask(__name__) @app.route('/ws') def ws():
print(request.environ.get('wsgi.websocket'))
print(request.environ) user_socket = request.environ.get('wsgi.websocket') # type: WebSocket while 1: msg = user_socket.receive() # 接受消息 print(msg) user_socket.send(msg) # 发送消息 if __name__ == '__main__': http_serv = WSGIServer(('0.0.0.0',9527),app,handler_class=WebSocketHandler) http_serv.serve_forever()
我们之前发送的请求都是在http协议下发送的请求,
websocket可以看做是另一种协议,如果我们还是在浏览器中输入127.0.0.1:9527/ws,肯定会报错,因为浏览器使用的是http协议。我们应该使用的是ws协议访问,WS://127.0.0.1:9527/ws。我们写一个页面,在页面上写一个script标签,利用页面的script标签去访问。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script type="text/javascript"> var ws = new WebSocket('ws://127.0.0.1:9527/ws'); ws.onmessage = function (data) { alert(data) } </script> </body> </html>
然后在页面的控制台输入ws,就不会报错。
输入ws.send('hello word')就可以向后端发消息。
我们模拟聊天写一个群聊:
from flask import Flask, request,render_template from geventwebsocket.websocket import WebSocket from gevent.pywsgi import WSGIServer from geventwebsocket.handler import WebSocketHandler user_socket_list = [] app = Flask(__name__) @app.route('/') def index(): return render_template('index.html') @app.route('/ws') def ws(): user_socket = request.environ.get('wsgi.websocket') # type:WebSocket # 建立连接 if user_socket: user_socket_list.append(user_socket) # 将建立的连接添加进一个列表 print(len(user_socket_list)) else: return render_template('index.html',message='请用websocket链接') while 1: msg = user_socket.receive() print(msg) for socket in user_socket_list: socket.send(msg) if __name__ == '__main__': http_serv = WSGIServer(('0.0.0.0',9527),app,handler_class=WebSocketHandler) http_serv.serve_forever()
html代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>群聊</h1> <script type="text/javascript"> var ws = new WebSocket('ws://127.0.0.1:9527/ws'); ws.onmessage = function (data) { console.log(data.data) } </script> </body> </html>
这样我们我们访问页面,在控制台输入ws.send(‘hello word’),就可以接受到消息,也可以建立多个连接。
但是这样有一个问题,就是我们自己发送的消息自己也可以收到,我们在群聊的时候,自己发送的消息,自己是不用收到的。所以:加上这么一句话。
然后我们完善一下前端:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>群聊</h1> 消息:<input type="text" id="send_text"> <button id="send" onclick="send_msg()">发送</button> <div style="border: 2px solid; width: 500px;height: 800px;" id="text_div"> </div> <script type="text/javascript"> var ws = new WebSocket("ws://127.0.0.1:9527/ws"); ws.onmessage = function (data) { // console.log(data.data); var text_div = document.getElementById('text_div'); var add_msg = "<p>" + data.data + "</p>"; text_div.innerHTML += add_msg; }; function send_msg(){ var msg = document.getElementById('send_text').value; var text_div = document.getElementById('text_div'); var add_msg = "<p style='text-align: right'>" + msg + "</p>"; text_div.innerHTML += add_msg; ws.send(msg); } </script> </body> </html>
然后我们给这个群聊带上昵称:
from flask import Flask, request,render_template from geventwebsocket.websocket import WebSocket from gevent.pywsgi import WSGIServer from geventwebsocket.handler import WebSocketHandler import json user_socket_dict = {} app = Flask(__name__) @app.route('/') def index(): return render_template('index.html') @app.route('/ws/<nickname>') def ws(nickname): user_socket = request.environ.get('wsgi.websocket') # type:WebSocket # 建立连接 if user_socket: user_socket_dict[nickname] = user_socket # 将建立的连接添加进一个列表 print(len(user_socket_dict)) else: return render_template('index.html',message='请用websocket链接') while 1: msg = user_socket.receive() print(msg) for user_nick_name,socket in user_socket_dict.items(): if user_socket != socket: try: socket.send(json.dumps({'sender':nickname,'msg':msg})) except: continue if __name__ == '__main__': http_serv = WSGIServer(('0.0.0.0',9527),app,handler_class=WebSocketHandler) http_serv.serve_forever()
html代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>群聊</h1> <p>昵称:<input type="text" id="nickname"> <button onclick="createsocket()">连接群聊</button> </p> 消息:<input type="text" id="send_text"> <button id="send" onclick="send_msg()">发送</button> <div style="border: 2px solid; width: 500px;height: 800px;" id="text_div"> </div> <script type="text/javascript"> var ws = null; function createsocket() { var nickname = document.getElementById('nickname').value; var ws = new WebSocket("ws://127.0.0.1:9527/ws/" + nickname); ws.onmessage = function (data) { // console.log(data.data); var text_div = document.getElementById('text_div'); var obj_data = JSON.parse(data.data); var add_msg = "<p>" + obj_data.sender + ':' + obj_data.msg + "</p>"; text_div.innerHTML += add_msg; }; } function send_msg(){ var nickname = document.getElementById('nickname').value; var msg = document.getElementById('send_text').value; var text_div = document.getElementById('text_div'); var add_msg = "<p style='text-align: right'>" + msg + ':' + nickname + "</p>"; text_div.innerHTML += add_msg; ws.send(msg); } </script> </body> </html>