使用Django频道意外收到WebSocket断开连接 [英] WebSocket disconnect received unexpectedly by using Django Channels

查看:104
本文介绍了使用Django频道意外收到WebSocket断开连接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用Django渠道向用户更新可能长期运行的任务的当前状态,我正面临要跟踪的 WebSocket DISCONNECT .

Using Django channels to update the user on the current status of a potentially long running task, I'm facing a WebSocket DISCONNECT that I would like to trace down.

设置看起来很简单. settings.py 定义渠道层:

The setup looks pretty straight forward. settings.py defines the channel layer:

ASGI_APPLICATION = "config.routing.application"
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            "hosts": [('127.0.0.1', 6379)],
        },
    },
}

routing.py 中,我们基本上遵循Channels文档中的默认建议(该模式仅与UUID匹配,我们将其用作面向公众的标识符):

In routing.py we basically follow the default suggestion from the Channels doc (the pattern is simply matching a UUID, which we use as public facing identifier):

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.conf.urls import url
from lektomat.consumers import analysis

application = ProtocolTypeRouter({
    # (http->django views is added by default)
    "websocket": AuthMiddlewareStack(
        URLRouter([
            # Use a regex to match the UUID as the Django version with its '<uuid:analysis_id>' does not work for Channels.
            url(r"^ws/analysis/(?P<analysis_id>[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12})/$",
                analysis.AnalysisConsumer),
        ])),
})

消费者只不过是通过新建立的websockets连接发送一些初始数据并在各处记录信息:

The consumer does not much more than sending some initial data via a newly established websockets connection and logging info all over the place:

import logging
import json
from channels.db import database_sync_to_async
from uuid import UUID
from typing import Tuple
from django.utils import timezone
from channels.generic.websocket import AsyncWebsocketConsumer
from myapp.models import AnalysisResult

logger = logging.getLogger(__name__)


class AnalysisConsumer(AsyncWebsocketConsumer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.analysis_id = None
        self.analysis_group_name = 'analysis_'

    async def connect(self):
        self.analysis_id = self.scope['url_route']['kwargs']['analysis_id']
        self.analysis_group_name = "analysis_{}".format(self.analysis_id)
        await self.channel_layer.group_add(self.analysis_group_name, self.channel_name)
        logger.info("%s – AnalysisConsumer: connect()ed and joined group %s.", str(timezone.now()), self.analysis_group_name)
        dummy_percent = 11
        dummy_text = 'Dummy'
        logger.debug("%s – Sending initial channel update to group %s with a %d percent and status_text=%s", str(timezone.now()), self.analysis_group_name, dummy_percent, dummy_text)
        await self.send(text_data=json.dumps({
            'progress_percent': progress_percent,
            'status_text': status_text
        }))
        await self.accept()

    async def disconnect(self, code):
        logger.info("%s – AnalysisConsumer: disconnecting with code=%s (internal group name=%s).", str(timezone.now()), code, self.analysis_group_name)
        await self.channel_layer.group_discard(self.analysis_group_name, self.channel_name)
        logger.info("%s – AnalysisConsumer: disconnect(%s)ed and left room %s", str(timezone.now()), code, self.analysis_group_name)

    async def receive(self, text_data=None, bytes_data=None):
        logger.info("%s – unexpectedly received data from the websocket, text_data=%s, bytes_data=%s", str(timezone.now()), text_data, str(bytes_data))

最后,客户端的javascript正在连接到websocket端点:

And finally, the client's javascript is connecting to the websocket endpoint:

var ws_scheme = window.location.protocol == "https:" ? "wss" : "ws";
var ws_uri = ws_scheme + '://' + window.location.host + '/ws/analysis/' + '{{ result_id }}' + '/';
var socket = new WebSocket(ws_uri);
socket.onopen = function open() {
    let now = new Date();
    console.info(now.toLocaleString() + ':' + now.getMilliseconds() + ' – WebSocket connection created.');
};

socket.onmessage = function(e) {
    console.log("WebSocket message received.")
    const data = JSON.parse(e.data);
    console.log("WebSocket message: " + data.status_text + " at " + data.progress_percent + " percent.");
};

socket.onclose = function(e) {
    let now = new Date();
    console.error(now.toLocaleString() + ':' + now.getMilliseconds() + ' – Analysis socket closed with event code = ' + e.code + ' and reason=' + e.reason);
};

socket.onerror = function(error) {
    let now = new Date();
    let msg = now.toLocaleString() + ':' + now.getMilliseconds() + ' – WebSocket error: ' + error;
    console.error(msg);
}

Redis后端已启动并正在运行.

The Redis backend is up and running.

但是:websocket连接在启动后立即关闭.更准确地说,浏览器的JS控制台日志(德语,翻译错误是我的):

But: The websocket connection is closed right after its start. More precisely, the browser's JS console logs (in German, faulty translation is mine):

(!) Firefox cannot connect to server at ws://localhost:8000/ws/analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b/
(!) 10.8.2020, 22:30:21:317 – WebSocket error: [object Event]
    onerror http://localhost:8000/analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b:149
    (Async: EventHandlerNonNull)
    <anonym> http://localhost:8000/analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b:146
(!) 10.8.2020, 22:30:21:319 – Analysis socket closed with event code = 1006 and reason=
    onclose http://localhost:8000/analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b:143
    (Async: EventHandlerNonNull)
    <anonym> http://localhost:8000/analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b:141

服务器控制台显示:

request: <AsgiRequest: GET '/analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b'>
HTTP GET /analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b 200 [1.37, 127.0.0.1:51562]
WebSocket HANDSHAKING /ws/analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b/ [127.0.0.1:51574]
2020-08-10 20:30:20.549519+00:00 – AnalysisConsumer: connect()ed and joined group analysis_d222ebe1-5a2a-4797-9466-24db1de5d24b.
2020-08-10 20:30:20.610167+00:00 – Sending initial channel update to group analysis_d222ebe1-5a2a-4797-9466-24db1de5d24b with a 11 percent and status_text=Dummy
WebSocket DISCONNECT /ws/analysis/d222ebe1-5a2a-4797-9466-24db1de5d24b/ [127.0.0.1:51574]

因此,服务器接收到连接请求,建立了websocket连接并立即断开连接.客户端在大约半秒钟后响应,仅出现代码1006错误(说明不多).永远不会调用使用者的 disconnect().

Thus, the server receives the connecting request, establishes the websocket connection and immediately disconnects. The client responds about half a second later with a mere error with code 1006 (which doesn't tell much). The consumer's disconnect() is never called.

谁可以启动 WebSocket DISCONNECT ?它似乎不是任何应用程序代码.感谢您指出此处缺少的内容.

Who might be initiating the WebSocket DISCONNECT? It doesn't seem to be any of the application code. Pointers on what's missing here are appreciated.

推荐答案

该使用者正在尝试在接受连接之前发送消息.

That consumer is trying to send a message before accepting the connection.

我刚刚测试过,达芙妮抛出了这个异常:

I just tested and Daphne throws this exception:

 File "...site-packages/daphne/ws_protocol.py", line 193, in handle_reply
 "Socket has not been accepted, so cannot send over it"

客户端收到代码为1006的CloseEvent

And the clients receive a CloseEvent with code 1006

这篇关于使用Django频道意外收到WebSocket断开连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆