基於python,vue使用websocket進行資料通訊
阿新 • • 發佈:2021-10-09
1.vue實現websocket
1.1初始化websocket
//this為vue全域性變數
if (typeof (WebSocket) === "undefined") { //您的瀏覽器不支援socket } else { // 關閉已有連線 if (this.socket) { try { this.socket.close(); } catch (e) { } } // 例項化socket this.socket = new WebSocket(path); // 監聽socket連線 this.socket.onopen = function () { //處理websocket啟動事件 }; // 監聽socket錯誤資訊 this.socket.onerror = function () { //處理websocket錯誤事件 }; // 監聽socket訊息 this.socket.onmessage = function () { //處理websocket訊息 }; // 監聽socket關閉 this.socket.onclose = function () { //處理websocket關閉事件 }; }1.2websocket傳送資料
1.3websocket狀態
this.socket.readyState 0 CONNECTING 連線尚未建立 1 OPEN WebSocket的連結已經建立 2 CLOSING 連線正在關閉 3 CLOSED 連線已經關閉或不可用2.python實現websocket服務端
2.1啟動websocket監聽
# 啟動socket監聽 def socketStart(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('0.0.0.0', 8215)) sock.listen(999) while True: connection, addr = sock.accept() try: buf = connection.recv(102400).decode() # 初次握手 if 'GET' in buf: headers = get_headers(buf) send_handshake(headers, connection)# 為當前連線開闢一個新的執行緒 mythread = threading.Thread(target=subThreadIn, args=(connection, connection.fileno())) mythread.setDaemon(True) mythread.start() else: connection.send('close') connection.close() except: pass
2.2接收資料執行緒
# 接受資訊 def subThreadIn(myconnection, connNumber): isLogin = False try: while not isLogin: # 讀取登入報文 nickname = myconnection.recv(102400) nickname = parsingRecv(nickname) # "\x03\xe9"為退出報文 if nickname and nickname != "\x03\xe9": nickname = json.loads(nickname) # 判斷是否為登入報文,自定義傳送的報文資料,需要自己實現 if nickname.get('type') == 'sendName': nickname = nickname.get('value') mykey = myconnection.fileno() mydict[mykey] = nickname mylist.append(myconnection) isLogin = True # 監聽訊息 while True: try: recvedMsg = myconnection.recv(102400) recvedMsg = parsingRecv(recvedMsg) if recvedMsg and recvedMsg != "\x03\xe9": recvedMsg = json.loads(recvedMsg) # 接受資料處理 handleRecvedMsg(recvedMsg, connNumber) except: try: del mydict[mykey] mylist.remove(myconnection) except: pass myconnection.close() return except: return
2.3解析報文
# 解析報文 def parsingRecv(all_data): if not all_data: return None # 獲取報文長度 code_len = ord(all_data[1]) & 127 if code_len == 126: masks = all_data[4:8] data = all_data[8:] elif code_len == 127: masks = all_data[10:14] data = all_data[14:] else: masks = all_data[2:6] data = all_data[6:] # 拼裝報文 raw_str = "" for i,d in enumerate(data): raw_str += chr(ord(d) ^ ord(masks[i % 4])) return raw_str
2.4傳送報文加密
# 傳送報文加密 def restructureSend(data): if data: data = str(data) else: return False token = "\x81" length = len(data) # struct為Python中處理二進位制數的模組,二進位制流為C,或網路流的形式。 if length < 126: token += struct.pack("B", length) elif length <= 0xFFFF: token += struct.pack("!BH", 126, length) else: token += struct.pack("!BQ", 127, length) data = '%s%s' % (token, data) return data
2.5將請求頭轉換為字典
# 將請求頭轉換為字典 def get_headers(data): header_dict = {} header, body = data.split("\r\n\r\n", 1) header_list = header.split("\r\n") for i in range(0, len(header_list)): if i == 0: if len(header_list[0].split(" ")) == 3: header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[0].split(" ") else: k, v = header_list[i].split(":", 1) header_dict[k] = v.strip() return header_dict
2.6響應握手
# 響應握手 def send_handshake(headers, conn): # 對請求頭中的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://%s%s\r\n\r\n" magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' value = headers['Sec-WebSocket-Key'] + magic_string ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest()) response_str = response_tpl % (ac.decode('utf-8'), headers['Host'], headers['url']) # 響應【握手】資訊 conn.send(response_str)
2.7訊息傳送
# 資訊廣播 def broadcast(whatToSay, exceptNum=None, allUser=True): for c in mylist: if allUser or c.fileno() != exceptNum: try: c.send(restructureSend(json.dumps(whatToSay))) except: pass # 資訊推送 def appoint(whatToSay, exceptNum): for c in mylist: if c.fileno() == exceptNum: try: c.send(restructureSend(json.dumps(whatToSay))) except: pass def handleRecvedMsg(recvedMsg, connNumber): _type = recvedMsg.get('type') # 聊天室廣播 if _type == 'heartbeat': appoint(recvedMsg, connNumber) elif _type == 'chatRoom': broadcast(recvedMsg, connNumber, False) elif _type == 'chatAllRoom': broadcast(recvedMsg, connNumber, True)
3.說明
客戶端傳送的報文: 第一個位元組: 第 1 位(1位): 0表示報文還未結束,1表示報文結束。 第 2--4 位(3位): 保留欄位,擴充套件自己的協議。 第 5--8 位(4位): 報文型別,1為文字,2為二進位制,8為斷開連結,9為ping,10為pong。第二個位元組: 第 1 位(1位): 1表示需要掩碼操作,0表示不需要。這裡是寫死的1 第 2--8 位(7位): 報文長度。小於126表示報文長度。等於126,後面2個位元組表示報文長度。等於127,後面8個位元組表示報文長度。
接下來四個位元組: 表示4個掩碼。
剩餘位元組: 真正的報文。
說明: 1 tcp接收到的資料大小可能為2+4+報文大小,或者2+2+4+報文大小,或者2+8+4+報文大小。 2 報文的每一個位元組需要與對應的掩碼異或運算。第一個位元組與第一個掩碼,第二個位元組與第二個掩碼.....第五個位元組與第一個掩碼,以此類推。
服務端傳送的報文: 第一個位元組: 第 1 位(1位): 0表示報文還未結束,1表示報文結束。 第 2--4 位(3位): 保留欄位,擴充套件自己的協議。 第 5--8 位(4位): 報文型別,1為文字,2為二進位制。。。。。
第二個位元組: 第 1 位(1位): 1表示需要掩碼操作,0表示不需要。這裡是寫死的0 第 2--8 位(7位): 報文長度。小於126表示報文長度。等於126,後面2個位元組表示報文長度。等於127,後面8個位元組表示報文長度。
剩餘位元組: 真正的報文。
說明: 1 tcp接收到的資料大小可能為2+報文大小,或者2+2+報文大小,或者2+8+報文大小。 2 服務端傳送的報文是真實的報文。