websocket 基于geventwebsocket模块的信息收发系统
websocket详解戳进来 本文转载自wupeiqi/博客地址
在这里要把概念理清楚,把自己理解的东西写出来
我们的websocket的本质是一套协议,
http协议和websocket协议是同级的,
要说到websocket还要从轮询和长轮询来说,我们的浏览器里面设置轮询和长轮询的应用场景是,我们
的浏览器需要实时监听用户的请求,一旦有请求进来就需要及时响应,就好比我们登录一个网站,如果半天登录不上就会影响用户体验,所以我们的浏览器需要在后端隔一段时间发送一次检验用户是否登录的请求,这个过程可以用定时器来实现,但是这样我们的浏览器压力就太大了,而且对于用户的请求处理得不够及时,会出现等待的过程中,用户的请求发过来了,然后要等到定时器的时间设定结束才能响应用户的请求,进行处理,这都是用户等待的成本,这个时候就引出了长轮询,所谓的长轮询就是我们的浏览器向后端发起请求会夯住,一直夯到我们的用户有请求到来,然后对请求的数据进行处理,如果用户一直没有请求来,则最多夯住30sec,然后就紧接着再次发送请求,如果有数据返回就对返回的数据进行处理,然后浏览器再继续向后端发起请求,这种方式跟上面提到的轮询相比较而言浏览器的压力会小一些,只是对于用户的请求处理的会更及时,
以上两种方式对于浏览器的压力都很大,我们这里就引出了websocket,它跟我们上面的两种http协议的轮询和长轮询不一样,http协议的请求是无状态短链接,是由\r\n来分割请求头,\r\n\r\n来分割请求头和请求体,所以服务端无法向客户端实时推送消息.就要用到上面我们提到的轮询和长轮询
这里来引出我们的websocket协议:\r\n分割,创建连接后不断开,在创建连接的时候,会先进行验证,验证通过之后才能把连接创建完成,
它实际上就是一个不会断开的socket,连接创建之后,客户端也就是浏览器会自动向服务端发送消息,消息包含Sec-WebSocket-Key:iyRe1KMHi4S4QXzcoboMmw==,
服务端接收到之后,会对于该数据进行加密通过base64以及magic_string等方式加密,
构造响应头,
HTTP/1.1 101 Switching Protocols\r\n
Upgrade:websocket\r\n
Connection: Upgrade\r\n
Sec-WebSocket-Accept: 加密后的值\r\n
WebSocket-Location: ws://127.0.0.1:8002\r\n\r\n
发送给客户端也就是浏览器
建立双工通道,然后就是进行数据收发,
在数据收发的时候我们要对数据进行加密,然后解密,根据我们的payload_len的值来进行处理,
payload_len有三总情况,就是如下三种:
payload_len<=125
payload_len==126
payload_len==127
获取内容
mask_key
以及数据
根据mask_key和位运算,就能把值解析出来.
基于websocket建立简单的tornado项目
示例如下:
投票系统基于websocket进行实现:
一个py文件写核心主代码

1 from geventwebsocket.handler import WebSocketHandler 2 from gevent.pywsgi import WSGIServer 3 from flask import Flask,render_template,request 4 import pickle 5 6 wsk_li=[] 7 aps=Flask(__name__) 8 aps.secret_key='asfd' 9 10 @aps.route('/index') 11 def index(): 12 return render_template('index.html') 13 14 15 @aps.route('/hello') 16 def test(): 17 wsk=request.environ.get('wsgi.websocket') 18 if not wsk: 19 return 'please use websocket-rules' 20 # 程序走到这里就是已经连接成功 21 wsk_li.append(wsk) 22 while True: 23 # 接收用户发送的消息 24 ret=wsk.receive() 25 # print(ret) 26 27 # 关闭连接 28 if not ret: 29 wsk_li.remove(wsk) 30 wsk.close() 31 break 32 33 for obj in wsk_li: 34 obj.send(ret) 35 36 return 'hello world' 37 38 39 if __name__ == '__main__': 40 http_socket=WSGIServer(('192.168.12.9',5000,),aps,handler_class=WebSocketHandler) 41 http_socket.serve_forever()
前端代码

1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>Title</title> 8 <style> 9 .msg-item { 10 padding: 10px; 11 border: 1px; 12 margin: 0 5px; 13 } 14 </style> 15 </head> 16 <body> 17 <h1>home-page</h1> 18 <div> 19 <h3>send-msg</h3> 20 <input type="text" id="msg" type="text"> 21 <input type="button" value="send" onclick="sendObj()"> 22 <h4>recive-message</h4> 23 <div id="content"> 24 25 </div> 26 </div> 27 28 <script src="/static/jquery-3.3.1.min.js"></script> 29 <script> 30 31 // 创建websocket连接, 32 ws = new WebSocket('ws://192.168.12.9:5000/hello'); 33 ws.onmessage=function(event){ 34 console.log(event); 35 36 var tag=document.createElement('div');// 这里是创建一个标签,使用jquery语法 37 tag.className='msg-item'; 38 tag.innerText=event.data; 39 $('#content').append(tag); //找到存放我们新创建的标签的位置,把它加进去 40 }; 41 function sendObj(){ 42 ws.send($('#msg').val()); 43 } 44 45 </script> 46 </body> 47 </html>
什么是websocket?
本质上是一个不会断开连接的socket,由\r\n进行分割,在建立连接的时候会先进行校验,校验通过才建立连接,由此完成服务端向客户端主动推送消息然后就是对于数据的收发过程中的加密和解密,
加密和解密用到的是magic_string还有base64,hashlib,以及payload_len的值所对应的三种状态,
<=125,==126,==127
我们使用geventwebsocket模块实现的简单的消息收发功能,利用我们的websocket建立连接后就永不断开的特质,我们把这个搭建起来之后就可以一直发送消息,一直发下去

1 from geventwebsocket.handler import WebSocketHandler 2 from gevent.pywsgi import WSGIServer 3 from flask import Flask, render_template, request 4 5 6 aps = Flask(__name__) 7 aps.secret_key = 'asdfa' 8 9 10 @aps.route('/host') 11 def host(): 12 return render_template('host.html') 13 14 15 li = [] 16 17 18 @aps.route('/index') 19 def demo(): 20 ws = request.environ.get('wsgi.websocket') 21 if not ws: 22 return 'please use the websocket-rule' 23 # 连接已经成功 24 li.append(ws) 25 while True: 26 # 等待用户发送消息并接收 27 message = ws.receive() 28 29 # 关闭message=None 30 if not message: 31 li.remove(ws) 32 ws.close() 33 break 34 35 for obj in li: 36 obj.send(message) 37 print(message) 38 return 'hello from outside' 39 40 41 if __name__ == '__main__': 42 http_server = WSGIServer(('0.0.0.0', 9090,), aps, handler_class=WebSocketHandler) 43 http_server.serve_forever()

1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>Title</title> 8 <style> 9 .msg-item { 10 padding: 5px; 11 border: 2px; 12 margin: 0px; 13 } 14 </style> 15 </head> 16 <body> 17 18 <h2>home page</h2> 19 20 <div> 21 <h3>send message</h3> 22 <input type="text" id="msg"> 23 <input type="button" value="send" onclick="sendObj()"> 24 <hr> 25 <div id="blunk"> 26 27 </div> 28 </div> 29 30 31 <script src="/static/jquery-3.3.1.min.js"> 32 </script> 33 34 <script> 35 // ws = new WebSocket('ws://192.168.12.9:9090/index'); 36 ws = new WebSocket('ws://127.0.0.1:9090/index'); 37 ws.onmessage = function (event) { 38 console.log(event.data); 39 var tag = document.createElement('div'); 40 tag.className = 'msg-item'; 41 tag.innerText = event.data; 42 $('#blunk').append(tag); 43 }; 44 45 function sendObj() { 46 ws.send($('#msg').val()); 47 } 48 </script> 49 </body> 50 </html>
这里需要补充的是我们的前端的websocket对象创建的时候可以写ip地址也可以写127.0.0.1这个地址,但是不论写那个,我们的浏览器访问的地址都是127.0.0.1这个地址,使用192.168.12.9这个地址是无法访问的,包括后端的WSGIServer也是要写成0.0.0.0这个地址,不能写192.168.12.9否则会报错.