python---websocket的使用
目录
一:简介
推文:WebSocket 是什么原理?为什么可以实现持久连接?
推文:WebSocket:5 分钟从入门到精通(很好)
WebSocket 协议是基于 TCP 的一种新的协议。WebSocket 最初在 HTML5 规范中被引用为 TCP 连接,作为基于 TCP 的套接字 API 的占位符。它实现了浏览器与服务器全双工 (full-duplex) 通信。其本质是保持 TCP 连接,在浏览器和服务端通过 Socket 进行通信。
二:对比:
Http:
socket 实现,单工通道(浏览器只发起,服务端只做响应),短连接,请求响应
WebSocket:
socket 实现,双工通道,请求响应,推送。socket 创建连接,不断开
三:socket 实现步骤
服务端:
1. 服务端开启 socket,监听 IP 和端口 3. 允许连接 * 5. 服务端接收到特殊值【加密 sha1, 特殊值,migic string="258EAFA5-E914-47DA-95CA-C5AB0DC85B11"】 * 6. 加密后的值发送给客户端
客户端:
2. 客户端发起连接请求(IP 和端口) * 4. 客户端生成一个 xxx,【加密 sha1, 特殊值,migic string="258EAFA5-E914-47DA-95CA-C5AB0DC85B11"】, 向服务端发送一段特殊值 * 7. 客户端接收到加密的值
注意:这个魔数是固定的 258EAFA5-E914-47DA-95CA-C5AB0DC85B11
四:简单实现,实现连接
服务端:
# coding:utf8 # __author: Administrator # date: 2018/6/29 0029 # /usr/bin/env python import socket,base64,hashlibdef get_headers(data):
'''将请求头转换为字典'''
header_dict = {}
data = str(data,encoding="utf-8")header,body </span>= data.split(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\r\n\r\n</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">) header_list </span>= header.split(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\r\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span> range(<span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">,len(header_list)): </span><span style="color: rgba(0, 0, 255, 1)">if</span> i == <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">: </span><span style="color: rgba(0, 0, 255, 1)">if</span> len(header_list[<span style="color: rgba(128, 0, 128, 1)">0</span>].split(<span style="color: rgba(128, 0, 0, 1)">"</span> <span style="color: rgba(128, 0, 0, 1)">"</span>)) == <span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">: header_dict[</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">method</span><span style="color: rgba(128, 0, 0, 1)">'</span>],header_dict[<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">url</span><span style="color: rgba(128, 0, 0, 1)">'</span>],header_dict[<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">protocol</span><span style="color: rgba(128, 0, 0, 1)">'</span>] = header_list[<span style="color: rgba(128, 0, 128, 1)">0</span>].split(<span style="color: rgba(128, 0, 0, 1)">"</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: k,v</span>=header_list[i].split(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">:</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">) header_dict[k]</span>=<span style="color: rgba(0, 0, 0, 1)">v.strip() </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> header_dict
sock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sock.bind(("127.0.0.1",8080))
sock.listen(5)#等待用户连接
conn,addr = sock.accept()
print("conn from ",conn,addr)
#获取握手消息,magic string ,sha1 加密
#发送给客户端
#握手消息
data = conn.recv(8096)
headers = get_headers(data)对请求头中的 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(bytes(response_str, encoding='utf-8'))
''' b' GET / HTTP/1.1\r\n Host: 127.0.0.1:8080\r\n Connection: Upgrade\r\n Pragma: no-cache\r\n Cache-Control: no-cache\r\n Upgrade: websocket\r\n Origin: http://localhost:63342\r\n Sec-WebSocket-Version: 13\r\n User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36\r\n Accept-Encoding: gzip, deflate, br\r\n Accept-Language: zh-CN,zh;q=0.8\r\n Sec-WebSocket-Key: +uL/aiakjNABjEoMzAqm6Q==\r\n Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n\r\n''''
浏览器:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body></body>
</html>
<script>
ws =new WebSocket("ws://127.0.0.1:8080");
ws.onopen = function (ev) { //若是连接成功,onopen 函数会执行
console.log(22222)
}
</script>
五:数据接收规则
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | #Payload len(第二个字节的前七位,最大 127)决定头部的长度 |I|S|S|S| (4) |A| (7) | (16/64) | # 若是小于 126:Extended payload length 扩展头部长度为 0 字节,后面全部为主体数据 |N|V|V|V| |S| | (if payload len==126/127) | # 若是等于 126:Extended payload length 扩展头部长度为 2 字节,后面全部为主体数据 | |1|2|3| |K| | | # 若是等于 127:Extended payload length 扩展头部长度为 8 字节,后面全部为主体数据 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | # 注意:主体数据中的前四位为 mask 掩码,用于后面的消息的解码,解码方式为循环异或操作 + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | # 数据过长,需要分部发送,这时需要 FIN 和 opcode +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+
The MASK bit simply tells whether the message is encoded. Messages from the client must be masked, so your server should expect this to be 1. (In fact, section 5.1 of the spec says that your server must disconnect from a client if that client sends an unmasked message.) When sending a frame back to the client, do not mask it and do not set the mask bit. We'll explain masking later. Note: You have to mask messages even when using a secure socket.RSV1-3 can be ignored, they are for extensions. The opcode field defines how to interpret the payload data: 0x0 for continuation, 0x1 for text (which is always encoded in UTF-8), 0x2 for binary, and other so-called "control codes" that will be discussed later. In this version of WebSockets, 0x3 to 0x7 and 0xB to 0xF have no meaning.The FIN bit tells whether this is the last message in a series. If it's 0, then the server will keep listening for more parts of the message; otherwise, the server should consider the message delivered. More on this later.
Decoding Payload LengthTo read the payload data, you must know when to stop reading. That's why the payload length is important to know. Unfortunately, this is somewhat complicated. To read it, follow these steps:
Read bits 9-15 (inclusive) and interpret that as an unsigned integer. If it's 125 or less, then that's the length; you're done. If it's 126, go to step 2. If it's 127, go to step 3.
Read the next 16 bits and interpret those as an unsigned integer. You're done.
Read the next 64 bits and interpret those as an unsigned integer (The most significant bit MUST be 0). You're done.
Reading and Unmasking the DataIf the MASK bit was set (and it should be, for client-to-server messages), read the next 4 octets (32 bits); this is the masking key. Once the payload length and masking key is decoded, you can go ahead and read that number of bytes from the socket. Let's call the data ENCODED, and the key MASK. To get DECODED, loop through the octets (bytes a.k.a. characters for text data) of ENCODED and XOR the octet with the (i modulo 4)th octet of MASK. In pseudo-code (that happens to be valid JavaScript):
var DECODED = "";
for (var i = 0; i < ENCODED.length; i++) {
DECODED[i] = ENCODED[i] ^ MASK[i % 4];
}Now you can figure out what DECODED means depending on your application.
数据帧格式:
FIN:1 个比特。
如果是 1,表示这是消息(message)的最后一个分片(fragment),如果是 0,表示不是是消息(message)的最后一个分片(fragment)。
RSV1, RSV2, RSV3:各占 1 个比特。
一般情况下全为 0。当客户端、服务端协商采用 WebSocket 扩展时,这三个标志位可以非 0,且值的含义由扩展进行定义。如果出现非零的值,且并没有采用 WebSocket 扩展,连接出错。
Opcode: 4 个比特。
操作代码,Opcode 的值决定了应该如何解析后续的数据载荷(data payload)。如果操作代码是不认识的,那么接收端应该断开连接(fail the connection)。可选的操作代码如下:
%x0:表示一个延续帧。当 Opcode 为 0 时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片。
%x1:表示这是一个文本帧(frame)
%x2:表示这是一个二进制帧(frame)
%x3-7:保留的操作代码,用于后续定义的非控制帧。
%x8:表示连接断开。
%x9:表示这是一个 ping 操作。
%xA:表示这是一个 pong 操作。
%xB-F:保留的操作代码,用于后续定义的控制帧。
Mask: 1 个比特。
表示是否要对数据载荷进行掩码操作。从客户端向服务端发送数据时,需要对数据进行掩码操作;从服务端向客户端发送数据时,不需要对数据进行掩码操作。
如果服务端接收到的数据没有进行过掩码操作,服务端需要断开连接。
如果 Mask 是 1,那么在 Masking-key 中会定义一个掩码键(masking key),并用这个掩码键来对数据载荷进行反掩码。所有客户端发送到服务端的数据帧,Mask 都是 1。
掩码的算法、用途在下一小节讲解。
Payload length:数据载荷的长度,单位是字节。为 7 位,或 7+16 位,或 1+64 位。
假设数 Payload length === x,如果
x 为 0~126:数据的长度为 x 字节。
x 为 126:后续 2 个字节代表一个 16 位的无符号整数,该无符号整数的值为数据的长度。
x 为 127:后续 8 个字节代表一个 64 位的无符号整数(最高位为 0),该无符号整数的值为数据的长度。
此外,如果 payload length 占用了多个字节的话,payload length 的二进制表达采用网络序(big endian,重要的位在前)。
Masking-key:0 或 4 字节(32 位)
所有从客户端传送到服务端的数据帧,数据载荷都进行了掩码操作,Mask 为 1,且携带了 4 字节的 Masking-key。如果 Mask 为 0,则没有 Masking-key。
备注:载荷数据的长度,不包括 mask key 的长度。
Payload data:(x+y) 字节
载荷数据:包括了扩展数据、应用数据。其中,扩展数据 x 字节,应用数据 y 字节。
扩展数据:如果没有协商使用扩展的话,扩展数据数据为 0 字节。所有的扩展都必须声明扩展数据的长度,或者可以如何计算出扩展数据的长度。此外,扩展如何使用必须在握手阶段就协商好。如果扩展数据存在,那么载荷数据长度必须将扩展数据的长度包含在内。
应用数据:任意的应用数据,在扩展数据之后(如果存在扩展数据),占据了数据帧剩余的位置。载荷数据长度 减去 扩展数据长度,就得到应用数据的长度。
实现规则解码:
def get_data(info): #info 是我们连接后,接受的数据 payload_len = info[1] & 127 if payload_len == 126: extend_payload_len = info[2:4] mask = info[4:8] decoded = info[8:] elif payload_len == 127: extend_payload_len = info[2:10] mask = info[10:14] decoded = info[14:] else: extend_payload_len = None mask = info[2:6] decoded = info[6:]<strong><span style="color: rgba(255, 0, 0, 1)">bytes_list </span></strong></span><strong><span style="color: rgba(255, 0, 0, 1)">= bytearray() #这里我们使用字节将数据全部收集,再去字符串编码,这样不会导致中文乱码 </span></strong><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> range(len(decoded)): chunk </span>= decoded[i] ^ mask[i % <span style="color: rgba(128, 0, 128, 1)">4</span><span style="color: rgba(0, 0, 0, 1)">] bytes_list.append(chunk) body </span>= str(bytes_list, encoding=<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">utf-8</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">) return body</span></pre>
实现循环获取数据
import socket,base64,hashlib
def get_headers(data):
'''将请求头转换为字典'''
header_dict = {}
data = str(data,encoding="utf-8")
header,body </span>= data.split(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\r\n\r\n</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">)
header_list </span>= header.split(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\r\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span> range(<span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">,len(header_list)):
</span><span style="color: rgba(0, 0, 255, 1)">if</span> i == <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 0, 255, 1)">if</span> len(header_list[<span style="color: rgba(128, 0, 128, 1)">0</span>].split(<span style="color: rgba(128, 0, 0, 1)">"</span> <span style="color: rgba(128, 0, 0, 1)">"</span>)) == <span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">:
header_dict[</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">method</span><span style="color: rgba(128, 0, 0, 1)">'</span>],header_dict[<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">url</span><span style="color: rgba(128, 0, 0, 1)">'</span>],header_dict[<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">protocol</span><span style="color: rgba(128, 0, 0, 1)">'</span>] = header_list[<span style="color: rgba(128, 0, 128, 1)">0</span>].split(<span style="color: rgba(128, 0, 0, 1)">"</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">:
k,v</span>=header_list[i].split(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">:</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">)
header_dict[k]</span>=<span style="color: rgba(0, 0, 0, 1)">v.strip()
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> header_dict
def get_data(info):
payload_len = info[1] & 127
if payload_len == 126:
extend_payload_len = info[2:4]
mask = info[4:8]
decoded = info[8:]
elif payload_len == 127:
extend_payload_len = info[2:10]
mask = info[10:14]
decoded = info[14:]
else:
extend_payload_len = None
mask = info[2:6]
decoded = info[6:]
bytes_list </span>=<span style="color: rgba(0, 0, 0, 1)"> bytearray() #这里我们使用字节将数据全部收集,再去字符串编码,这样不会导致中文乱码
</span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> range(len(decoded)):
chunk </span>= decoded[i] ^ mask[i % <span style="color: rgba(128, 0, 128, 1)">4</span><span style="color: rgba(0, 0, 0, 1)">] #解码方式
bytes_list.append(chunk)
body </span>= str(bytes_list, encoding=<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">utf-8</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> body
sock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sock.bind(("127.0.0.1",8080))
sock.listen(5)
#等待用户连接
conn,addr = sock.accept()
print("conn from ",conn,addr)
#获取握手消息,magic string ,sha1 加密
#发送给客户端
#握手消息
data = conn.recv(8096)
headers = get_headers(data)
对请求头中的 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(bytes(response_str, encoding='utf-8'))
#可以进行通信
while True:
data = conn.recv(8096)
data = get_data(data)
print(data)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body></body>
</html>
<script>
ws =new WebSocket("ws://127.0.0.1:8080");
ws.onopen = function (ev) { //若是连接成功,onopen 函数会执行
console.log(22222);
ws.send("你好");
}
</script>
注意:使用控制台完成发送,而不是刷新页面,会报错,因为我们关闭了连接,试图将关闭信号字节编码出错。这里我们需要利用 mask(第二字节中,1 表示连接,0 断开)
六:数据发送规则(需要发送二进制包 struct 模块)
def send_msg(conn, msg_bytes): """ WebSocket 服务端向客户端发送消息 :param conn: 客户端连接到服务器端的 socket 对象, 即: conn,address = socket.accept() :param msg_bytes: 向客户端发送的字节 :return: """ import structtoken </span>= b<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\x81</span><span style="color: rgba(128, 0, 0, 1)">" #接收的第一字节,一般都是x81不变</span><span style="color: rgba(0, 0, 0, 1)"> length </span>=<span style="color: rgba(0, 0, 0, 1)"> len(msg_bytes) </span><span style="color: rgba(0, 0, 255, 1)">if</span> length < <span style="color: rgba(128, 0, 128, 1)">126</span><span style="color: rgba(0, 0, 0, 1)">: token </span>+= <span style="color: rgba(0, 0, 255, 1)">struct</span>.pack(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">B</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, length) elif length </span><= <span style="color: rgba(128, 0, 128, 1)">0xFFFF</span><span style="color: rgba(0, 0, 0, 1)">: token </span>+= <span style="color: rgba(0, 0, 255, 1)">struct</span>.pack(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">!BH</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 128, 1)">126</span><span style="color: rgba(0, 0, 0, 1)">, length) </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: token </span>+= <span style="color: rgba(0, 0, 255, 1)">struct</span>.pack(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">!BQ</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 128, 1)">127</span><span style="color: rgba(0, 0, 0, 1)">, length) msg </span>= token +<span style="color: rgba(0, 0, 0, 1)"> msg_bytes conn.send(msg) </span><span style="color: rgba(0, 0, 255, 1)">return</span> True</pre>
实现发送数据
# coding:utf8 # __author: Administrator # date: 2018/6/29 0029 # /usr/bin/env python import socket,base64,hashlibdef get_headers(data):
'''将请求头转换为字典'''
header_dict = {}
data = str(data,encoding="utf-8")header,body </span>= data.split(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\r\n\r\n</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">) header_list </span>= header.split(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\r\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span> range(<span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">,len(header_list)): </span><span style="color: rgba(0, 0, 255, 1)">if</span> i == <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">: </span><span style="color: rgba(0, 0, 255, 1)">if</span> len(header_list[<span style="color: rgba(128, 0, 128, 1)">0</span>].split(<span style="color: rgba(128, 0, 0, 1)">"</span> <span style="color: rgba(128, 0, 0, 1)">"</span>)) == <span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">: header_dict[</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">method</span><span style="color: rgba(128, 0, 0, 1)">'</span>],header_dict[<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">url</span><span style="color: rgba(128, 0, 0, 1)">'</span>],header_dict[<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">protocol</span><span style="color: rgba(128, 0, 0, 1)">'</span>] = header_list[<span style="color: rgba(128, 0, 128, 1)">0</span>].split(<span style="color: rgba(128, 0, 0, 1)">"</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: k,v</span>=header_list[i].split(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">:</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">) header_dict[k]</span>=<span style="color: rgba(0, 0, 0, 1)">v.strip() </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> header_dict
def get_data(info):
payload_len = info[1] & 127
if payload_len == 126:
extend_payload_len = info[2:4]
mask = info[4:8]
decoded = info[8:]
elif payload_len == 127:
extend_payload_len = info[2:10]
mask = info[10:14]
decoded = info[14:]
else:
extend_payload_len = None
mask = info[2:6]
decoded = info[6:]bytes_list </span>=<span style="color: rgba(0, 0, 0, 1)"> bytearray() #这里我们使用字节将数据全部收集,再去字符串编码,这样不会导致中文乱码 </span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> range(len(decoded)): chunk </span>= decoded[i] ^ mask[i % <span style="color: rgba(128, 0, 128, 1)">4</span><span style="color: rgba(0, 0, 0, 1)">] #解码方式 bytes_list.append(chunk) body </span>= str(bytes_list, encoding=<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">utf-8</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> body
def send_msg(conn, msg_bytes):
"""
WebSocket 服务端向客户端发送消息
:param conn: 客户端连接到服务器端的 socket 对象, 即: conn,address = socket.accept()
:param msg_bytes: 向客户端发送的字节
:return:
"""
import structtoken </span>= b<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\x81</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)"> #接收的第一字节,一般都是x81不变 length </span>=<span style="color: rgba(0, 0, 0, 1)"> len(msg_bytes) </span><span style="color: rgba(0, 0, 255, 1)">if</span> length < <span style="color: rgba(128, 0, 128, 1)">126</span><span style="color: rgba(0, 0, 0, 1)">: token </span>+= <span style="color: rgba(0, 0, 255, 1)">struct</span>.pack(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">B</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, length) elif length </span><= <span style="color: rgba(128, 0, 128, 1)">0xFFFF</span><span style="color: rgba(0, 0, 0, 1)">: token </span>+= <span style="color: rgba(0, 0, 255, 1)">struct</span>.pack(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">!BH</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 128, 1)">126</span><span style="color: rgba(0, 0, 0, 1)">, length) </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">: token </span>+= <span style="color: rgba(0, 0, 255, 1)">struct</span>.pack(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">!BQ</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 128, 1)">127</span><span style="color: rgba(0, 0, 0, 1)">, length) msg </span>= token +<span style="color: rgba(0, 0, 0, 1)"> msg_bytes conn.send(msg) </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> True
sock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sock.bind(("127.0.0.1",8080))
sock.listen(5)#等待用户连接
conn,addr = sock.accept()
#获取握手消息,magic string ,sha1 加密
#发送给客户端
#握手消息
data = conn.recv(8096)headers = get_headers(data)
对请求头中的 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(bytes(response_str, encoding='utf-8'))
#可以进行通信
while True:
data = conn.recv(8096)
data = get_data(data)
print(data)
send_msg(conn,bytes(data+"geah",encoding="utf-8"))
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body></body>
</html>
<script>
ws =new WebSocket("ws://127.0.0.1:8080");
ws.onopen = function (ev) { //若是连接成功,onopen 函数会执行
console.log(22222);
ws.send("你好");
}
ws.onmessage = function (ev) {
console.log(ev);
}
</script>
七:tornado 实现 websocket 聊天室
tornado 服务端
import tornado.ioloop
import tornado.web
import tornado.websocket
import datetime
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">s1.html</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
def post(self, </span>*args, **<span style="color: rgba(0, 0, 0, 1)">kwargs):
pass
users = set()
class ChatHandler(tornado.websocket.WebSocketHandler):
def open(self, *args, **kwargs):
'''客户端连接'''
print("connect....")
print(self.request)
users.add(self)
def on_message(self, message):
</span><span style="color: rgba(128, 0, 0, 1)">'''</span><span style="color: rgba(128, 0, 0, 1)">有消息到达</span><span style="color: rgba(128, 0, 0, 1)">'''</span><span style="color: rgba(0, 0, 0, 1)">
now </span>=<span style="color: rgba(0, 0, 0, 1)"> datetime.datetime.now()
content </span>= self.render_string(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">recv_msg.html</span><span style="color: rgba(128, 0, 0, 1)">"</span>,date=now.strftime(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">%Y-%m-%d %H:%M:%S</span><span style="color: rgba(128, 0, 0, 1)">"</span>),msg=<span style="color: rgba(0, 0, 0, 1)">message)
</span><span style="color: rgba(0, 0, 255, 1)">for</span> client <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> users:
</span><span style="color: rgba(0, 0, 255, 1)">if</span> client ==<span style="color: rgba(0, 0, 0, 1)"> self:
</span><span style="color: rgba(0, 0, 255, 1)">continue</span><span style="color: rgba(0, 0, 0, 1)">
client.write_message(content)
def on_close(self):
</span><span style="color: rgba(128, 0, 0, 1)">'''</span><span style="color: rgba(128, 0, 0, 1)">客户端主动关闭连接</span><span style="color: rgba(128, 0, 0, 1)">'''</span><span style="color: rgba(0, 0, 0, 1)">
users.remove(self)
st ={
"template_path": "template",# 模板路径配置
"static_path":'static',
}
#路由映射 匹配执行,否则 404
application = tornado.web.Application([
("/index",MainHandler),
("/wschat",ChatHandler),
],**st)
if name=="main":
application.listen(8080)
#io多路复用
tornado.ioloop.IOLoop.instance().start()</span></pre>
前端模板
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="/static/css/bootstrap.min.css" rel="stylesheet"> <link href="/static/css/nifty.min.css" rel="stylesheet"> <link href="/static/css/demo/nifty-demo-icons.min.css" rel="stylesheet"> <link href="/static/css/demo/nifty-demo.min.css" rel="stylesheet"> <link href="/static/plugins/pace/pace.min.css" rel="stylesheet"> <script src="/static/js/jquery-2.2.4.min.js"></script> <script src="/static/plugins/pace/pace.min.js"></script> <script src="/static/js/bootstrap.min.js"></script> <script src="/static/js/nifty.min.js"></script> <script src="/static/js/demo/nifty-demo.min.js"></script> <script src="/static/plugins/flot-charts/jquery.flot.min.js"></script> <script src="/static/plugins/flot-charts/jquery.flot.resize.min.js"></script> <script src="/static/plugins/gauge-js/gauge.min.js"></script> <script src="/static/plugins/skycons/skycons.min.js"></script> <script src="/static/plugins/easy-pie-chart/jquery.easypiechart.min.js"></script> <script src="/static/js/demo/widgets.js"></script> </head> <body> <div id="container" class="effect aside-bright mainnav-sm aside-right aside-in"> <div class="boxed"> <div id="content-container"> <div class="row"> <div class="col-md-8 col-lg-8 col-sm-8"><!--Chat widget--> <!--===================================================--> <div <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">panel</span><span style="color: rgba(128, 0, 0, 1)">"</span> style=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">height: 640px</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <!--Heading--> <div <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">panel-heading</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <h3 <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">panel-title</span><span style="color: rgba(128, 0, 0, 1)">"</span>>Chat</h3> </div> <!--Widget body--> <div style=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">height:510px;padding-top:0px;</span><span style="color: rgba(128, 0, 0, 1)">"</span> <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">widget-body</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <div <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">nano</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <div <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">nano-content pad-all</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <ul <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">list-unstyled media-block</span><span style="color: rgba(128, 0, 0, 1)">"</span>> </ul> </div> </div> <!--Widget footer--> <div <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">panel-footer</span><span style="color: rgba(128, 0, 0, 1)">"</span> style=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">height: 90px;</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <div <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">row</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <div <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">col-xs-9</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <input type=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">text</span><span style="color: rgba(128, 0, 0, 1)">"</span> placeholder=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Enter your text</span><span style="color: rgba(128, 0, 0, 1)">"</span> <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">form-control chat-input</span><span style="color: rgba(128, 0, 0, 1)">"</span>> </div> <div <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">col-xs-3</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <button <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">btn btn-primary btn-block</span><span style="color: rgba(128, 0, 0, 1)">"</span> onclick=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">sendMsg(this);</span><span style="color: rgba(128, 0, 0, 1)">"</span> type=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">submit</span><span style="color: rgba(128, 0, 0, 1)">"</span>>Send</button> </div> </div> </div> </div> </div> <!--===================================================--> <!--Chat widget--> </div> <div <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">col-md-4 col-lg-4 col-sm-4</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <aside id=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">aside-container</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <div id=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">aside</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <div <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">nano has-scrollbar</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <div <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">nano-content</span><span style="color: rgba(128, 0, 0, 1)">"</span> tabindex=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">0</span><span style="color: rgba(128, 0, 0, 1)">"</span> style=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">right: -17px;</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <!--Nav tabs--> <!--================================--> <ul <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">nav nav-tabs nav-justified</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <li <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">active</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <a href=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">#demo-asd-tab-1</span><span style="color: rgba(128, 0, 0, 1)">"</span> data-toggle=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">tab</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <i <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">demo-pli-speech-bubble-7</span><span style="color: rgba(128, 0, 0, 1)">"</span>></i> </a> </li> </ul> <!--================================--> <!--End nav tabs--> <!-- Tabs Content --> <!--================================--> <div <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">tab-content</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <div <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">tab-pane fade in active</span><span style="color: rgba(128, 0, 0, 1)">"</span> id=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">demo-asd-tab-1</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <p <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">pad-hor text-semibold text-main</span><span style="color: rgba(128, 0, 0, 1)">"</span>> <span <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">pull-right badge badge-success</span><span style="color: rgba(128, 0, 0, 1)">"</span>><span style="color: rgba(128, 0, 128, 1)">0</span></span><span style="color: rgba(0, 0, 0, 1)"> Friends </span></p> </div> </div> </div> <div <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">nano-pane</span><span style="color: rgba(128, 0, 0, 1)">"</span> style=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">display: none;</span><span style="color: rgba(128, 0, 0, 1)">"</span>><div <span style="color: rgba(0, 0, 255, 1)">class</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">nano-slider</span><span style="color: rgba(128, 0, 0, 1)">"</span> style=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">height: 4059px; transform: translate(0px, 0px);</span><span style="color: rgba(128, 0, 0, 1)">"</span>></div></div></div> </div> </aside> </div> </div> </div> </div>
</div>
</body>
</html><script>
ws = new WebSocket("ws://127.0.0.1:8080/wschat");function sendMsg(ths) { </span><span style="color: rgba(0, 0, 255, 1)">var</span> dt = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date() </span><span style="color: rgba(0, 0, 255, 1)">var</span> now_time =<span style="color: rgba(0, 0, 0, 1)"> dt.toLocaleString(); </span><span style="color: rgba(0, 0, 255, 1)">var</span> msg = $(ths).parents(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">.row</span><span style="color: rgba(128, 0, 0, 1)">"</span>).find(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">.chat-input</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">).val(); $(ths).parents(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">.row</span><span style="color: rgba(128, 0, 0, 1)">"</span>).find(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">.chat-input</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">).empty(); </span><span style="color: rgba(0, 0, 255, 1)">var</span> li = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)"><li class="mar-btm"><div class="media-right"><img src="" class="img-circle img-sm" alt="Profile Picture"></div></span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">; li </span>+= <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)"><div class="media-body pad-hor speech-right"><div class="speech"><a href="#" class="media-heading">游客</a></span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">; li </span>+= <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)"><p></span><span style="color: rgba(128, 0, 0, 1)">'</span>+msg+<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)"></p></span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">; li </span>+= <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)"><p class="speech-time"></span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">; li </span>+= <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)"><i class="demo-pli-clock icon-fw"></i></span><span style="color: rgba(128, 0, 0, 1)">'</span>+<span style="color: rgba(0, 0, 0, 1)">now_time; li </span>+= <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)"></p></div></div></li></span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">; $(ths).parents(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">.widget-body</span><span style="color: rgba(128, 0, 0, 1)">"</span>).find(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">.list-unstyled</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">).append(li); $(ths).parents(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">.panel-footer</span><span style="color: rgba(128, 0, 0, 1)">"</span>).find(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">.chat-input</span><span style="color: rgba(128, 0, 0, 1)">"</span>).val(<span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">); ws.send(msg); } ws.onmessage</span>=<span style="color: rgba(0, 0, 0, 1)">function (ev) { $(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">.list-unstyled</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">).append(ev.data); }
</script>
消息插件
<li class="mar-btm"> <div class="media-left"> <img src="img/profile-photos/1.png" class="img-circle img-sm" alt="Profile Picture"> </div> <div class="media-body pad-hor"> <div class="speech"> <a href="#" class="media-heading"> 游客 </a> <p>{{msg}}</p> <p class="speech-time"> <i class="demo-pli-clock icon-fw"></i>{{date}} </p> </div> </div> </li>
实现效果
游客一:
游客二
作者:山上有风景
欢迎任何形式的转载,但请务必注明出处。
限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。