基于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 服务端发送的报文是真实的报文。
上一篇:智汀家庭云-开发指南Web:业务功能【设备通讯】


下一篇:路飞-支付宝支付接口