在 Django 视图中使用 Websocket 不工作
Using Websocket in Django View Not Working
问题总结
我正在使用 Django 和网络套接字将数据发送到前端(React 组件)。当我 运行 应用程序并从我的控制台发送数据时,一切正常。当我使用前端按钮触发 Django 视图 运行 具有相同功能时,它不起作用并生成令人困惑的错误消息。
我希望能够单击开始将数据发送到 websocket 的前端按钮。
我是 Django、websockets 和 React 的新手,敬请耐心等待。
概览
- Django 后端和 React 前端使用 Django Channels(网络套接字)连接。
- 用户在前端点击按钮,在 Django REST API 端点上
fetch()
。
- [不工作] 上述端点的 view 开始通过网络套接字发送数据。
- 前端使用此值更新。
简短错误描述
错误Traceback很长,所以放在最后post。开头为:
Internal Server Error: /api/run-create
结尾为:
ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host
我试过的
发送数据外部 Django 视图
- 下面的函数将数据发送到网络套接字。
- 当我在我的控制台中 运行 时它完美地工作 - 前端按预期更新。
- 注意:当 运行 来自 inside Django 视图时,相同的函数会导致附加错误。
import json
import time
import numpy as np
import websocket
def gen_fake_path(num_cities):
path = list(np.random.choice(num_cities, num_cities, replace=False))
path = [int(num) for num in path]
return json.dumps({"path": path})
def fake_run(num_cities, limit=1000):
ws = websocket.WebSocket()
ws.connect("ws://localhost:8000/ws/canvas_data")
while limit:
path_json = gen_fake_path(num_cities)
print(f"Sending {path_json} (limit: {limit})")
ws.send(path_json)
time.sleep(3)
limit -= 1
print("Sending complete!")
ws.close()
return
其他详细信息
相关文件和配置
consumer.py
class AsyncCanvasConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.group_name = "dashboard"
await self.channel_layer.group_add(self.group_name, self.channel_name)
await self.accept()
async def disconnect(self, close_code):
await self.channel_layer.group_discard(self.group_name, self.channel_name)
async def receive(self, text_data=None, bytes_data=None):
print(f"Received: {text_data}")
data = json.loads(text_data)
to_send = {"type": "prep", "path": data["path"]}
await self.channel_layer.group_send(self.group_name, to_send)
async def prep(self, event):
send_json = json.dumps({"path": event["path"]})
await self.send(text_data=send_json)
相关views.py
@api_view(["POST", "GET"])
def run_create(request):
serializer = RunSerializer(data=request.data)
if not serializer.is_valid():
return Response({"Bad Request": "Invalid data..."}, status=status.HTTP_400_BAD_REQUEST)
# TODO: Do run here.
serializer.save()
fake_run(num_cities, limit=1000)
return Response(serializer.data, status=status.HTTP_200_OK)
相关settings.py
WSGI_APPLICATION = 'evolving_salesman.wsgi.application'
ASGI_APPLICATION = 'evolving_salesman.asgi.application'
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer"
}
}
相关routing.py
websocket_url_pattern = [
path("ws/canvas_data", AsyncCanvasConsumer.as_asgi()),
]
完全错误
编辑:解决方案
Kunal Solanke 的建议解决了这个问题。我没有使用 fake_run()
,而是使用了以下内容:
layer = get_channel_layer()
for i in range(10):
path = list(np.random.choice(4, 4, replace=False))
path = [int(num) for num in path]
async_to_sync(layer.group_send)("dashboard", {"type": "prep", "path": path})
time.sleep(3)
我建议您使用 get_channel_layer 实用程序,而不是创建从同一服务器到自身的新连接。因为您最终会通过打开如此多的连接来增加服务器负载。
一旦获得通道层,就可以像我们通常发送 evnet 那样简单地进行群发。
您可以阅读更多关于
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
def media_image(request,chat_id) :
if request.method == "POST" :
data = {}
if request.FILES["media_image"] is not None :
item = Image.objects.create(owner = request.user,file=request.FILES["media_image"])
message=Message.objects.create(item =item,user=request.user )
chat = Chat.objects.get(id=chat_id)
chat.messages.add(message)
layer = get_channel_layer()
item = {
"media_type": "image",
"url" : item.file.url,
"user" : request.user.username,
'caption':item.title
}
async_to_sync(layer.group_send)(
'chat_%s'%str(chat_id),
#this is the channel group name,which is defined inside your consumer"
{
"type":"send_media",
"item" : item
}
)
return HttpResponse("media sent")
在错误日志中,我可以看到第一次迭代握手成功,第二次失败。您可以通过在 for 循环中打印一些内容来检查。如果是这种情况,握手很可能由于多个连接而失败。我不知道 Inmemrorycache 支持多少来自同一来源的连接,但这可能是第二个连接断开连接的原因。你可以在频道 docs 中得到一些想法。如果你不想更改你的代码,请尝试使用 redis,如果你使用 linux .
,这很容易
问题总结
我正在使用 Django 和网络套接字将数据发送到前端(React 组件)。当我 运行 应用程序并从我的控制台发送数据时,一切正常。当我使用前端按钮触发 Django 视图 运行 具有相同功能时,它不起作用并生成令人困惑的错误消息。
我希望能够单击开始将数据发送到 websocket 的前端按钮。
我是 Django、websockets 和 React 的新手,敬请耐心等待。
概览
- Django 后端和 React 前端使用 Django Channels(网络套接字)连接。
- 用户在前端点击按钮,在 Django REST API 端点上
fetch()
。 - [不工作] 上述端点的 view 开始通过网络套接字发送数据。
- 前端使用此值更新。
简短错误描述
错误Traceback很长,所以放在最后post。开头为:
Internal Server Error: /api/run-create
结尾为:
ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host
我试过的
发送数据外部 Django 视图
- 下面的函数将数据发送到网络套接字。
- 当我在我的控制台中 运行 时它完美地工作 - 前端按预期更新。
- 注意:当 运行 来自 inside Django 视图时,相同的函数会导致附加错误。
import json
import time
import numpy as np
import websocket
def gen_fake_path(num_cities):
path = list(np.random.choice(num_cities, num_cities, replace=False))
path = [int(num) for num in path]
return json.dumps({"path": path})
def fake_run(num_cities, limit=1000):
ws = websocket.WebSocket()
ws.connect("ws://localhost:8000/ws/canvas_data")
while limit:
path_json = gen_fake_path(num_cities)
print(f"Sending {path_json} (limit: {limit})")
ws.send(path_json)
time.sleep(3)
limit -= 1
print("Sending complete!")
ws.close()
return
其他详细信息
相关文件和配置
consumer.py
class AsyncCanvasConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.group_name = "dashboard"
await self.channel_layer.group_add(self.group_name, self.channel_name)
await self.accept()
async def disconnect(self, close_code):
await self.channel_layer.group_discard(self.group_name, self.channel_name)
async def receive(self, text_data=None, bytes_data=None):
print(f"Received: {text_data}")
data = json.loads(text_data)
to_send = {"type": "prep", "path": data["path"]}
await self.channel_layer.group_send(self.group_name, to_send)
async def prep(self, event):
send_json = json.dumps({"path": event["path"]})
await self.send(text_data=send_json)
相关views.py
@api_view(["POST", "GET"])
def run_create(request):
serializer = RunSerializer(data=request.data)
if not serializer.is_valid():
return Response({"Bad Request": "Invalid data..."}, status=status.HTTP_400_BAD_REQUEST)
# TODO: Do run here.
serializer.save()
fake_run(num_cities, limit=1000)
return Response(serializer.data, status=status.HTTP_200_OK)
相关settings.py
WSGI_APPLICATION = 'evolving_salesman.wsgi.application'
ASGI_APPLICATION = 'evolving_salesman.asgi.application'
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer"
}
}
相关routing.py
websocket_url_pattern = [
path("ws/canvas_data", AsyncCanvasConsumer.as_asgi()),
]
完全错误
编辑:解决方案
Kunal Solanke 的建议解决了这个问题。我没有使用 fake_run()
,而是使用了以下内容:
layer = get_channel_layer()
for i in range(10):
path = list(np.random.choice(4, 4, replace=False))
path = [int(num) for num in path]
async_to_sync(layer.group_send)("dashboard", {"type": "prep", "path": path})
time.sleep(3)
我建议您使用 get_channel_layer 实用程序,而不是创建从同一服务器到自身的新连接。因为您最终会通过打开如此多的连接来增加服务器负载。
一旦获得通道层,就可以像我们通常发送 evnet 那样简单地进行群发。
您可以阅读更多关于
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
def media_image(request,chat_id) :
if request.method == "POST" :
data = {}
if request.FILES["media_image"] is not None :
item = Image.objects.create(owner = request.user,file=request.FILES["media_image"])
message=Message.objects.create(item =item,user=request.user )
chat = Chat.objects.get(id=chat_id)
chat.messages.add(message)
layer = get_channel_layer()
item = {
"media_type": "image",
"url" : item.file.url,
"user" : request.user.username,
'caption':item.title
}
async_to_sync(layer.group_send)(
'chat_%s'%str(chat_id),
#this is the channel group name,which is defined inside your consumer"
{
"type":"send_media",
"item" : item
}
)
return HttpResponse("media sent")
在错误日志中,我可以看到第一次迭代握手成功,第二次失败。您可以通过在 for 循环中打印一些内容来检查。如果是这种情况,握手很可能由于多个连接而失败。我不知道 Inmemrorycache 支持多少来自同一来源的连接,但这可能是第二个连接断开连接的原因。你可以在频道 docs 中得到一些想法。如果你不想更改你的代码,请尝试使用 redis,如果你使用 linux .
,这很容易