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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM