websocket
理论
WebSocket协议是基于TCP的一种新的协议。WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符。它实现了浏览器与服务器全双工(full-duplex)通信。其本质是保持TCP连接,在浏览器和服务端通过Socket进行通信。
本文将使用Python编写Socket服务端,一步一步分析请求过程!!!
1. 启动服务端
import socket
sock =socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 8002))
sock.listen(5)# 等待用户连接conn, address = sock.accept().........
启动Socket服务器后,等待用户【连接】,然后进行收发数据。
- 客户端连接
<script type="text/javascript">
var socket = new WebSocket("ws://127.0.0.1:8002/xxoo");
...</script>
当客户端向服务端发送连接请求时,不仅连接还会发送【握手】信息,并等待服务端响应,至此连接才创建成功!
3. 建立连接【握手】
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 8002))
sock.listen(5) # 获取客户端socket对象
conn, address = sock.accept() # 获取客户端的【握手】信息
data = conn.recv(1024).........
conn.send('响应【握手】信息')
请求和响应的【握手】信息需要遵循规则:
- 从请求【握手】信息中提取 Sec-WebSocket-Key
- 利用magic_string 和 Sec-WebSocket-Key 进行hmac1加密,再进行base64加密
- 将加密结果响应给客户端
注:magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11
请求【握手】信息为:
GET /chatsocket HTTP/ 1.1 Host: 127.0.0.1:8002
Connection: Upgrade
Pragma: no-cache
Cache-Control: no`cache
Upgrade: websocket
Origin: http://
localhost:63342
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: mnwFxiOlctXFN/DeMt1Amg==Sec-
WebSocket-Extensions: permessage-deflate;
client_max_window_bits......
提取Sec-WebSocket-Key值并加密:
import socket
import base64
import hashlib
def get_headers(data):
"""将请求头格式化成字典:param data:
return"""
header_dict = {}
data = str(data, encoding='utf-8')
for i in data.split('\r\n'):
print(i)
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[i].split(' ')) ==3:
header_dict['method'], header_dict['url'], header_dict['protocol'] =header_list[i].split(' ')
else:
k, v = header_list[i].split(':', 1)
header_dict[k] =v.strip()
return header_dict
sock =socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 8002))
sock.listen(5)
conn, address = sock.accept()
data = conn.recv(1024)
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_stringac =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'``))``...``...``...`
4.客户端和服务端收发数据
客户端和服务端传输数据时,需要对数据进行【封包】和【解包】。客户端的JavaScript类库已经封装【封包】和【解包】过程,但Socket服务端需要手动实现。
第一步:获取客户端发送的数据【解包】
info = conn.recv(8096)
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 = bytearray()
for i in range(len(decoded)):
chunk = decoded[i] ^ mask[i % 4]
bytes_list.append(chunk)
body = str(bytes_list, encoding='utf-8')
print(body)
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,
0x1for text (which is always encoded in UTF-8),0x2for binary, and other so-called "control codes" that will be discussed later. In this version of WebSockets,0x3to0x7and0xBto0xFhave 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 Length
To 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 Data
If 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.
第二步:向客户端发送数据【封包】
def send_msg(conn, msg_bytes):
"""
WebSocket服务端向客户端发送消息
:param conn: 客户端连接到服务器端的socket对象,即: conn,address = socket.accept()
:param msg_bytes: 向客户端发送的字节
:return:
"""
import struct
token = b"\x81"
length = len(msg_bytes)
if length < 126:
token += struct.pack("B", length)
elif length <= 0xFFFF:
token += struct.pack("!BH", 126, length)
else:
token += struct.pack("!BQ", 127, length)
msg = token + msg_bytes
conn.send(msg)
return True
5. 基于Python实现简单示例
a. 基于Python socket实现的WebSocket服务端:
# !/usr/bin/env python# -*- coding:utf-8 -*-
import socket
import base64
import hashlib
def get_headers(data):
""" 将请求头格式化成字典
:param data:
:return:
"""
header_dict = {}
data = str(data, encoding='utf-8')
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[i].split(' ')) == 3:
header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ') else:
k, v = header_list[i].split(':', 1)
header_dict[k] = v.strip()
return header_dict
def send_msg(conn, msg_bytes):
"""
WebSocket服务端向客户端发送消息
:param conn: 客户端连接到服务器端的socket对象,即: conn,address = socket.accept()
:param msg_bytes: 向客户端发送的字节
:return:
"""
import struct
token = b"\x81"
length = len(msg_bytes)
if length < 126:
token += struct.pack("B", length)
elif length <= 0xFFFF:
token += struct.pack("!BH", 126, length)
else:
token += struct.pack("!BQ", 127, length)
msg = token + msg_bytes
conn.send(msg)
return True
def run():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 8003))
sock.listen(5)
conn, address = sock.accept()
data = conn.recv(1024)
headers = get_headers(data)
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"
value = headers['Sec-WebSocket-Key'] + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
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:
try:
info = conn.recv(8096)
except Exception as e:
info = None
if not info:
break
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 = bytearray()
for i in range(len(decoded)):
chunk = decoded[i] ^ mask[i % 4]
bytes_list.append(chunk)
body = str(bytes_list, encoding='utf-8')
send_msg(conn, body.encode('utf-8'))
sock.close()
if __name__ == '__main__':
run()
b. 利用JavaScript类库实现客户端
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title></head>
<body>
<div><input type="text" id="txt"/> <input type="button" id="btn" value="提交" onclick="sendMsg();"/> <input type="button"
id="close"
value="关闭连接"
onclick="closeConn();"/>
</div>
<div id="content"></div>
<script type="text/javascript">
var socket = new WebSocket("ws://127.0.0.1:8003/chatsocket");
socket.onopen = function () { /* 与服务器端连接成功后,自动执行 */
var newTag = document.createElement('div');
newTag.innerHTML = "【连接成功】";
document.getElementById('content').appendChild(newTag);
};
socket.onmessage = function (event) { /* 服务器端向客户端发送数据时,自动执行 */
var response = event.data;
var newTag = document.createElement('div');
newTag.innerHTML = response;
document.getElementById('content').appendChild(newTag);
};
socket.onclose = function (event) { /* 服务器端主动断开连接时,自动执行 */
var newTag = document.createElement('div');
newTag.innerHTML = "【关闭连接】";
document.getElementById('content').appendChild(newTag);
};
function sendMsg() {
var txt = document.getElementById('txt');
socket.send(txt.value);
txt.value = "";
}
function closeConn() {
socket.close();
var newTag = document.createElement('div');
newTag.innerHTML = "【关闭连接】";
document.getElementById('content').appendChild(newTag);
}
</script>
</body>
</html>
6. 基于Tornado框架实现Web聊天室
Tornado是一个支持WebSocket的优秀框架,其内部原理正如1~5步骤描述,当然Tornado内部封装功能更加完整。
以下是基于Tornado实现的聊天室示例:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import uuid
import json
import tornado.ioloop
import tornado.web
import tornado.websocket
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html')
class ChatHandler(tornado.websocket.WebSocketHandler):
# 用户存储当前聊天室用户
waiters = set()
# 用于存储历时消息
messages = []
def open(self):
"""
客户端连接成功时,自动执行
:return:
"""
ChatHandler.waiters.add(self)
uid = str(uuid.uuid4())
self.write_message(uid)
for msg in ChatHandler.messages:
content = self.render_string('message.html', **msg)
self.write_message(content)
def on_message(self, message):
"""
客户端连发送消息时,自动执行
:param message:
:return:
"""
msg = json.loads(message)
ChatHandler.messages.append(message)
for client in ChatHandler.waiters:
content = client.render_string('message.html', **msg)
client.write_message(content)
def on_close(self):
"""
客户端关闭连接时,,自动执行
:return:
"""
ChatHandler.waiters.remove(self)
def run():
settings = {
'template_path': 'templates',
'static_path': 'static',
}
application = tornado.web.Application([
(r"/", IndexHandler),
(r"/chat", ChatHandler),
], **settings)
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
run()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Python聊天室</title>
</head>
<body>
<div>
<input type="text" id="txt"/>
<input type="button" id="btn" value="提交" onclick="sendMsg();"/>
<input type="button" id="close" value="关闭连接" onclick="closeConn();"/>
</div>
<div id="container" style="border: 1px solid #dddddd;margin: 20px;min-height: 500px;">
</div>
<script src="/static/jquery-2.1.4.min.js"></script>
<script type="text/javascript">
$(function () {
wsUpdater.start();
});
var wsUpdater = {
socket: null,
uid: null,
start: function() {
var url = "ws://127.0.0.1:8888/chat";
wsUpdater.socket = new WebSocket(url);
wsUpdater.socket.onmessage = function(event) {
console.log(event);
if(wsUpdater.uid){
wsUpdater.showMessage(event.data);
}else{
wsUpdater.uid = event.data;
}
}
},
showMessage: function(content) {
$('#container').append(content);
}
};
function sendMsg() {
var msg = {
uid: wsUpdater.uid,
message: $("#txt").val()
};
wsUpdater.socket.send(JSON.stringify(msg));
}
</script>
</body>
</html>
应用
轮询
通过setinterval 实现的,在前端,通过ajax创建一个定时任务,每隔n秒就向服务端发送一个请求,当发送的人数很多时,服务端的压力也会非常大,当我第一次向后端请求数据没有时,过了一秒钟消息到了,但是定时任务还需要n秒才会发送第二次请求,这样数据就会有延迟.
<script>
function getInfo(){
console.log(1);
$.ajax({
})
}
setInterval(getInfo,2000);
</script>
长轮询
当用户请求发送到来时,利用queue(想从队列中获取数据,但队列中没有时会夯住的特性),为每个用户维护一个队列,只要有了数据,后端将数据copy成n份,放入队列中,队列中有了数据,会立刻返回给前端进行展示,并再次发送请求,当队列中没有数据时,用户请求夯住,夯n秒,会报一个queue.Empty的错误,然后断开连接,
我们可以利用js中的事件轮回(python中的递归,但没有次数限制),将用户请求持续发送
"""当用户发送GET请求时,先设置全局变量自带,判断有没有携带用户名,如果携带了会为此用户创建一个用户名为键,消息队列为值的字典,然后返回html页面和用户名,前端拿到用户名在前端进行渲染,的到html,当用户发送ajax请求发送数据时,data中携带当前用户名,以及用户想要发送的消息,后端拿到数据将数据添加到每个用户的消息队列中,同时前端向后端递归取数据,只要消息队列中有数据,就会返回给前端进行渲染,如果没有数据,消息队列如果n秒没有数据,就会报一个queue.Empty的错误,通过try捕捉返回给前端一个状态码,前端收到状态码,判断没有成功会通过事件轮回持续向后端发送请求"""
利用Q实现网页版聊天室
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>聊天室({{ username }})</h1>
<div class="form">
<input id="txt" type="text" placeholder="请输入文字">
<input id="btn" type="button" value="发送">
</div>
<div id="content">
</div>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
$(function () {
$('#btn').click(function () {
var text = $("#txt").val();
$.ajax({
url:'/send/msg/',
type:'POST',
data: {text:text},
success:function (arg) {
console.log(arg);
}
})
});
getMessage();
});
function getMessage() {
$.ajax({
url:'/get/msg/',
type:'GET',
data:{user:"{{ username }}" },
dataType:"JSON",
success:function (info) {
{# var info = JSON.parse(info) #}
console.log(info);
if(info.status){
var tag = document.createElement('div');
tag.innerHTML = info.data;
$('#content').append(tag);
}
getMessage();
}
})
}
</script>
</body>
</html>
后端
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
import queue
QUEUE_DICT = {}
def index(request):
username = request.GET.get('username')
if not username:
return HttpResponse('请输入名字')
QUEUE_DICT[username] = queue.Queue()
return render(request,'index.html',{'username':username})
def send_msg(request):
"""
接受用户发来的消息
:param request:
:return:
"""
text = request.POST.get('text')
for k,v in QUEUE_DICT.items():
v.put(text)
return HttpResponse('ok')
def get_msg(request):
"""
想要来获取消息
:param request:
:return:
"""
ret = {'status':True,'data':None}
username = request.GET.get('user')
user_queue = QUEUE_DICT.get(username)
try:
message = user_queue.get(timeout=10)
ret['data'] = message
except queue.Empty:
ret['status'] = False
return JsonResponse(ret)
websocket
https://www.cnblogs.com/qidaii/articles/11959541.html
原理
WebSocket协议是基于TCP的一种新的协议。
(WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符。它实现了浏览器与服务器全双工(full-duplex)通信。其本质是保持TCP连接,在浏览器和服务端通过Socket进行通信。)
"""握手环节"""
当浏览器向服务端创建连接后会先进行验证,即发送一个随机字符串,在http的请求头中加(Sec_WebSocket-Key),服务端将魔法字符串(magic string 网上可以搜到固定的全球公认的字符串)和浏览器的随机字符串拼接起来加密(先进行hashlib.sha1加密之后再做base64加密)返回给浏览器,放在响应头中给用户返回 (Sec-WebSocket-Accept),浏览器会通过魔法字符串+随机字符串拼接并加密(先进行hashlib.sha1加密之后再做base64加密),如果量值相等,那么服务端则支持websocket请求,校验不成功,报错,服务端不支持websocket,此为websocket的握手环节
"""解密环节"""
当浏览器向服务端发送数据时会进行加密,服务端进行解密,拿到第二个字节(8位),取后七位,最大为127(称为Payload Size),这个值分三种情况,
<=125时,前面的两个字节就是头部信息 后4字节 -- maskingkey 后面字节为加密数据
如果=126,前4个字节是头,后4字节 -- maskingkey 后面字节为加密数据
=127,前10个字节是头 后4字节 -- maskingkey 后面字节为加密数据
剩下的是加密后的数据 每个字节依次跟maskingkey的每个字节做位运算,才会得到真正的明文数据,
magic_string
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
手动创建支持websocket的服务端
佩奇博客路径:www.cnblogs.com/wupeiqi/p/6558766.html
websocket只能通过js来实现
client(前端需手动调用ws.send())
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="button" value="开始" onclick="startConnect();">
<script>
var ws = null;
function startConnect() {
// 1. 内部会先发送随机字符串
// 2. 内部会校验加密字符串
ws = new WebSocket('ws://127.0.0.1:8002')
}
</script>
</body>
</html>
server
import socket
import hashlib
import base64
def get_headers(data):
"""
将请求头格式化成字典
:param data:
:return:
"""
header_dict = {}
data = str(data, encoding='utf-8')
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[i].split(' ')) == 3:
header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ')
else:
k, v = header_list[i].split(':', 1)
header_dict[k] = v.strip()
return 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 = bytearray()
for i in range(len(decoded)):
chunk = decoded[i] ^ mask[i % 4]
bytes_list.append(chunk)
body = str(bytes_list, encoding='utf-8')
return body
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 8002))
sock.listen(5)
# 等待用户连接
conn, address = sock.accept()
# 握手环节
header_dict = get_headers(conn.recv(1024))
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
random_string = header_dict['Sec-WebSocket-Key']
value = random_string + magic_string
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
response = "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://127.0.0.1:8002\r\n\r\n"
response = response %ac.decode('utf-8')
print(response)
conn.send(response.encode('utf-8'))
# 接受数据
while True:
data = conn.recv(1024)
msg = get_data(data)
print(msg)
企业应用
我们学过django和flask框架,内部基于wsgi做的socket,默认他俩都不支持websocket协议,只支持http协议。
-
flask中应用
-
建议3.6版本
pip3 install gevent-websocket -
django中应用
以前用 pip3 install dwebsocket pip3 install channels
channels应用
https://www.cnblogs.com/qidaii/p/11959622.html
1. 首先要在apps中注册
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
'channels',
]
2.在settings中配置ASGI_APPLICATION
ASGI_APPLICATION = "django_channels_demo.routing.application"
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer",
}
}
3. 创建websocket应用和路由
routing.py
from channels.routing import ProtocolTypeRouter, URLRouter
from django.conf.urls import url
from app01 import consumers
application = ProtocolTypeRouter({
'websocket': URLRouter([
url(r'^x1/$', consumers.ChatConsumer),
])
})
4.consumers.py
from channels.exceptions import StopConsumer
from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync
class ChatConsumer(WebsocketConsumer):
def websocket_connect(self, message):
""" websocket连接到来时,自动执行 """
print('有人来了')
async_to_sync(self.channel_layer.group_add)('22922192', self.channel_name)
'''连接'''
self.accept()
def websocket_receive(self, message):
""" websocket浏览器给发消息时,自动触发此方法 """
print('接收到消息', message)
async_to_sync(self.channel_layer.group_send)('22922192', {
'type': 'xxx.ooo',
'message': message['text']
})
def xxx_ooo(self, event):
message = event['message']
self.send(message)
def websocket_disconnect(self, message):
""" 断开连接 """
print('客户端主动断开连接了')
async_to_sync(self.channel_layer.group_discard)('22922192', self.channel_name)
raise StopConsumer()
class NewChatConsumer(WebsocketConsumer):
def connect(self):
print('有人来了')
async_to_sync(self.channel_layer.group_add)('22922192', self.channel_name)
self.accept()
def receive(self, text_data=None, bytes_data=None):
print('接收到消息', text_data)
async_to_sync(self.channel_layer.group_send)('22922192', {
'type': 'xxx.ooo',
'message': text_data
})
def xxx_ooo(self, event):
message = event['message']
self.send(message)
def disconnect(self, code):
print('客户端主动断开连接了')
async_to_sync(self.channel_layer.group_discard)('22922192', self.channel_name)
4. client
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Web聊天室:<span id="tips"></span></h1>
<div class="form">
<input id="txt" type="text" placeholder="请输入文字">
<input id="btn" type="button" value="发送" onclick="sendMessage();">
</div>
<div id="content">
</div>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
var ws;
$(function () {
initWebSocket();
});
function initWebSocket() {
ws = new WebSocket("ws://127.0.0.1:8000/x1/");
ws.onopen = function(){
$('#tips').text('连接成功');
};
ws.onmessage = function (arg) {
var tag = document.createElement('div');
tag.innerHTML = arg.data;
$('#content').append(tag);
};
ws.onclose = function () {
ws.close();
}
}
function sendMessage() {
ws.send($('#txt').val());
}
</script>
</body>
</html>
5. server
from django.shortcuts import render
def index(request):
return render(request,'index.html')

浙公网安备 33010602011771号