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 (不錯)