1. 程式人生 > 其它 >基於python,vue使用websocket進行資料通訊

基於python,vue使用websocket進行資料通訊

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傳送資料

this.socket.send("需要傳送的資料");

 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 服務端傳送的報文是真實的報文。