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 服务端发送的报文是真实的报文。