http协议是一种单向的网络协议,在创建链接后,它只容许Browser/UA(UserAgent)向WebServer发出请求资源后,WebServer才能返回相应的数据。而WebServer不能主动的推送数据给Browser/UAjavascript
websocket是实现网络的全双工通讯,一次连接,实时通信,节省带宽html
引言:java
http1.1版本之后支持持久链接:Connection:keep-alive;虽说websocket也支持持久化链接,那么咱们为何还使用websocket呢?web
-------》websocket:服务器可主动向客户端推送数据算法
-----》应用场景:须要服务器主动向客户端推送数据的地方 json
var ws = new WebSockdet(ws://127.0.0.1:9527/);
ws.onopen = function(){ #创建ws链接后作。。。事情 } ws.onmessage = function(){ } ws.onclose = function(){ } ws.onerror = function(){ #ws链接错误作。。。事情
window.location.reload();//刷新页面
}
import socket, base64, hashlib sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('127.0.0.1', 9527)) sock.listen(5) # 获取客户端socket对象 conn, address = sock.accept() # 获取客户端的【握手】信息 data = conn.recv(1024) print(data)#ws链接请求 """ GET /ws HTTP/1.1\r\n Host: 127.0.0.1:9527\r\n Connection: Upgrade\r\n Pragma: no-cache\r\n Cache-Control: no-cache\r\n User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36\r\n Upgrade: websocket\r\n Origin: http://localhost:63342\r\n Sec-WebSocket-Version: 13\r\n Accept-Encoding: gzip, deflate, br\r\n Accept-Language: zh-CN,zh;q=0.9\r\n Cookie: session=a6f96c20-c59e-4f33-84d9-c664a2f29dfc\r\n Sec-WebSocket-Key: MAZZU5DPIxWmhk/UWL2+BA==\r\n Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n\r\n """ # 如下动做是有websockethandler完成的 # magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11 #获取Sec-WebSocket-Key def get_headers(data): header_dict = {} header_str = data.decode("utf8") for i in header_str.split("\r\n"): if str(i).startswith("Sec-WebSocket-Key"): header_dict["Sec-WebSocket-Key"] = i.split(":")[1].strip() return header_dict headers = get_headers(data) # 提取请求头信息 # magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' #Sec-WebSocket-Key: MAZZU5DPIxWmhk/UWL2+BA== value = headers['Sec-WebSocket-Key'] + magic_string #钥匙+锁 print(value) ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest()) #保险柜被打开,在确认保险柜那么ws链接就创建了 # 对请求头中的sec-websocket-key进行加密 response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \ "Upgrade:websocket\r\n" \ "Connection: Upgrade\r\n" \ "Sec-WebSocket-Accept: %s\r\n" \ "WebSocket-Location: ws://127.0.0.1:9527\r\n\r\n" print(ac.decode('utf-8')) response_str = response_tpl % (ac.decode('utf-8')) # 响应【握手】信息 conn.send(response_str.encode("utf8")) # while True: msg = conn.recv(8096) print(msg)
# b'\x81\x83\xceH\xb6\x85\xffz\x85' hashstr = b'\x81\x83\xceH\xb6\x85\xffz\x85' # b'\x81 \x83 \xceH\xb6\x85\xffz\x85' # 将第二个字节也就是 \x83 第9-16位 进行与127进行位运算 payload = hashstr[1] & 127 print(payload) if payload == 127: extend_payload_len = hashstr[2:10] mask = hashstr[10:14] decoded = hashstr[14:] # 当位运算结果等于127时,则第3-10个字节为数据长度 # 第11-14字节为mask 解密所需字符串 # 则数据为第15字节至结尾 if payload == 126: extend_payload_len = hashstr[2:4] mask = hashstr[4:8] decoded = hashstr[8:] # 当位运算结果等于126时,则第3-4个字节为数据长度 # 第5-8字节为mask 解密所需字符串 # 则数据为第9字节至结尾 if payload <= 125: extend_payload_len = None mask = hashstr[2:6] decoded = hashstr[6:] # 当位运算结果小于等于125时,则这个数字就是数据的长度 # 第3-6字节为mask 解密所需字符串 # 则数据为第7字节至结尾 str_byte = bytearray() for i in range(len(decoded)): byte = decoded[i] ^ mask[i % 4] str_byte.append(byte) print(str_byte.decode("utf8")) 脑瓜疼脑瓜疼
import struct msg_bytes = "hello".encode("utf8") token = b"\x81" length = len(msg_bytes) if length < 126: token += struct.pack("B", length) elif length == 126: token += struct.pack("!BH", 126, length) else: token += struct.pack("!BQ", 127, length) msg = token + msg_bytes print(msg) 加密算法脑瓜疼
服务端flask
from flask import Flask,render_template,request from geventwebsocket.handler import WebSocketHandler from geventwebsocket.websocket import WebSocket from gevent.pywsgi import WSGIServer app = Flask(__name__) @app.route('/index') def index(): # request.headers #注意:Sec-Websocket-Key: Sec-Websocket-Extensions user_socket = request.environ.get('wsgi.websocket')#获取websocket链接,就是经过这个给客户端发消息 while 1: msg = user_socket.receive() user_socket.send('你'+msg) if __name__ == '__main__': # app.run('0.0.0.0',9527,debug=True) http_serv = WSGIServer(('0.0.0.0',9527),app,handler_class=WebSocketHandler)#遇到请求时交给app处理,遇到websocket请求时交给WebSocketHandler处理 http_serv.serve_forever() #启动
客户端bash
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script type="application/javascript"> var ws = new WebSocket('ws://192.168.13.142:9527/index') //接收到服务端的消息 ws.onmessage = function (data) { console.log(data.data); } </script> </body> </html>
测试服务器
服务器websocket
from flask import Flask,render_template,request from geventwebsocket.handler import WebSocketHandler from geventwebsocket.websocket import WebSocket from gevent.pywsgi import WSGIServer app = Flask(__name__) user_socket_list = [] #存放获取链接的客户端的websocket @app.route('/index') def index(): # request.headers #注意:Sec-Websocket-Key: Sec-Websocket-Extensions user_socket = request.environ.get('wsgi.websocket')#获取websocket链接,就是经过这个给客户端发消息 if user_socket: user_socket_list.append(user_socket) print(user_socket_list) while 1: msg = user_socket.receive() for u_socket in user_socket_list: if u_socket == user_socket: #A客户端发的,服务器就不给A发送A本身的消息了 continue try:#若是链接会话死掉,跳过这个链接就能够了 u_socket.send(msg) except: continue @app.route('/') def ws(): return render_template('index.html') if __name__ == '__main__': # app.run('0.0.0.0',9527,debug=True) http_serv = WSGIServer(('0.0.0.0',9527),app,handler_class=WebSocketHandler)#遇到请求时交给app处理,遇到websocket请求时交给WebSocketHandler处理 http_serv.serve_forever() #启动
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> <input type="text" id="msg"> <button onclick="send_msg()">发送</button> <div id='chat_list' style="color: red;width: 500px;height: 500px"></div> </div> <script type="application/javascript"> var ws = new WebSocket('ws://192.168.13.142:9527/index') //接收到服务端的消息,放入客户端的显示信息框中 ws.onmessage = function (data) { console.log(data.data); var ptage = document.createElement('p'); ptage.innerText = data.data; document.getElementById('chat_list').appendChild(ptage); }; //点击发送触发的事件 function send_msg() { var msg = document.getElementById('msg').value; ws.send(msg); } </script> </body> </html>
测试:
客户端访问这个路径就能够群聊了,群聊显示在下方框中
服务器
import json from flask import Flask,render_template,request from geventwebsocket.handler import WebSocketHandler from geventwebsocket.websocket import WebSocket from gevent.pywsgi import WSGIServer app = Flask(__name__) user_socket_list = [] #存放获取链接的客户端的websocket user_socket_dict = {} #存放单聊的客户端 @app.route('/index/<username>') def index(username): # request.headers #注意:Sec-Websocket-Key: Sec-Websocket-Extensions user_socket = request.environ.get('wsgi.websocket')#获取websocket链接,就是经过这个给客户端发消息 if user_socket: # user_socket_list.append(user_socket) user_socket_dict[username]=user_socket print(len(user_socket_dict),user_socket_dict) while 1: msg = user_socket.receive() #收件人 消息 发件人 msg_dict = json.loads(msg) print(msg_dict) msg_dict['from_user']=username to_user = msg_dict.get('to_user') # chat = msg_dict.get('msg') u_socket = user_socket_dict.get(to_user)#获取须要发送的那个客户端的websocket链接 u_socket.send(json.dumps(msg_dict)) @app.route('/') def ws(): return render_template('index.html') if __name__ == '__main__': # app.run('0.0.0.0',9527,debug=True) http_serv = WSGIServer(('0.0.0.0',9527),app,handler_class=WebSocketHandler)#遇到请求时交给app处理,遇到websocket请求时交给WebSocketHandler处理 http_serv.serve_forever() #启动
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> <input type="text" id="username"><button onclick="login()">登陆聊天室</button> 给<input type="text" id="to_user">发送 <input type="text" id="msg"><button onclick="send_msg()">发送</button> <div id='chat_list' style="color: red;width: 500px;height: 500px"></div> </div> <script type="application/javascript"> var ws = null; //点击发送触发的事件 function send_msg() { var msg = document.getElementById('msg').value; var to_user = document.getElementById('to_user').value; var send_str = { 'to_user':to_user, 'msg':msg }; ws.send(JSON.stringify(send_str)); } //点击登陆聊天室 function login() { var username = document.getElementById('username').value; ws = new WebSocket('ws://192.168.13.142:9527/index/'+username); //接收到服务端的消息,放入客户端的显示信息框中 ws.onmessage = function (data) { console.log(data.data); recv_msg = JSON.parse(data.data); ptag = document.createElement("p"); ptag.innerText= recv_msg.from_user + ":" + recv_msg.msg; document.getElementById("chat_list").appendChild(ptag); }; } </script> </body> </html>
测试
参考资料
https://www.cnblogs.com/jingmoxukong/p/7755643.html (不错)