如何修复无法使用Python和ASGI与Django Channels服务器建立WebSocket连接的错误?

huangapple go评论89阅读模式
英文:

How can I fix the error of not being able to establish a connection to a Django Channels server with WebSockets using Python and ASGI?

问题

Django channels & websockets:无法建立与服务器的连接。

我正在尝试使用Django Channels、Websockets和p5创建实时绘图应用程序。

我唯一遇到的问题是:Firefox无法建立与服务器的连接,连接地址为ws://XXX.XXX.XXX.XXX:8090/ws/room/1/。

我所做的:

settings.py:

INSTALLED_APPS = [
    ...
    'channels',
]

ASGI_APPLICATION = 'DrawMatch.asgi.application'

CHANNEL_LAYERS = {
    'default': {
        "BACKEND": "channels.layers.InMemoryChannelLayer"
    }
}

asgi.py:

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DrawMatch.settings')
django_asgi_app = get_asgi_application()

application = ProtocolTypeRouter({
    "http": django_asgi_app,
    "websocket": AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter(
                drawmatch_app.routing.websocket_urlpatterns
            )
        )
    ),
})

consumers.py:

class DrawConsumer(AsyncJsonWebsocketConsumer):
    room_code: str = None
    room_group_name: str = None

    async def connect(self):
        self.room_code = self.scope['url_route']['kwargs']['room_code']
        self.room_group_name = f'room_{self.room_code}'
        print(f"room_code: {self.room_code}")
        print(f"room_group_name: {self.room_group_name}")

        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )
        await self.accept()

    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    async def receive(self, text_data: str = None, _: Any = None) -> None:
        data = json.loads(text_data)
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'draw',
                'data': data
            }
        )

    async def send_message(self, res):
        await self.send(text_data=json.dumps({
            "payload": res
        }))

routing.py:

websocket_urlpatterns = [
    url(r'^ws/room/(?P<room_code>\w+)/$', DrawConsumer.as_asgi()),
]

views.py:

def room(request, room_code):
    context = {
        'room_code': room_code
    }
    return render(request, 'room.html', context)

urls.py:

urlpatterns = [
    path('', views.home),
    ...
    path('room/<room_code>/', views.room),
    path('predict', draw_guess.main),
]

room.html:

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <title>DrawMatch - Room {{ room_code }}</title>
    <link rel="stylesheet" href="{% static 'styles/style.css' %}">
    <script src="{% static 'scripts/index.mjs' %}" type="module"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.min.js"></script>
    <script>
        const csrftoken = '{{ csrf_token }}';
        const room_code = {{ room_code }};
        const connectionString = `ws://${window.location.host}/ws/room/{{room_code}}/`;
        const gameSocket = new WebSocket(connectionString);
    </script>
</head>
<body>
    <h1>Room {{ room_code }}</h1>
    <div class="drawings-container"></div>
</body>
</html>

index.js:

const WIDTH = 500;
const HEIGHT = 500;
const STROKE_WEIGHT = 3;
const drawingsContainer = document.querySelector(".drawings-container");

console.log(gameSocket);

gameSocket.onmessage = (e) => {
    console.log(`Server: ${e.data}`)
    const data = JSON.parse(e.data);
    if (data.type === "draw") {
        const {
            canvas, x, y, px, py
        } = data.data;
        canvas.line(x, y, px, py);
    }
}

gameSocket.onopen = (e) => {
    console.log("Connected to websocket");
}

gameSocket.onclose = (e) => {
    console.log("Disconnected from websocket");
}

function setupCanvas(canvas, id) {
    console.log(connectionString);
    let timeout;
    let drawing = false;

    canvas.setup = () => {
        canvas.createCanvas(WIDTH, HEIGHT);
        canvas.strokeWeight(STROKE_WEIGHT);
        canvas.stroke("black");
        canvas.background("#FFFFFF");
        canvas.canvas.id = id;
        drawingsContainer.appendChild(canvas.canvas);
    }

    canvas.draw = () => {
        if (!drawing) return;
        canvas.line(canvas.mouseX, canvas.mouseY, canvas.pmouseX, canvas.pmouseY);

        if (timeout) return;
        timeout = setTimeout(async () => {
            const image = canvas.canvas.toDataURL();
            const response = await fetch("/predict", {
                method: "POST", body: JSON.stringify({
                    image
                }), headers: {
                    "X-CSRFToken": csrftoken, "Content-Type": "application/json"
                }
            })
            const data = await response.text();
            console.log(data);
            gameSocket.send(JSON.stringify({
                type: "draw", data: {
                    canvas: canvas.id, x: canvas.mouseX, y: canvas.mouseY, px: canvas.pmouseX, py: canvas.pmouseY
                }
            }));
            timeout = null;
        }, 200);
    }

    canvas.mousePressed = () => drawing = canvas.mouseX > 0 && canvas.mouseX <= WIDTH && canvas.mouseY > 0 && canvas.mouseY <= HEIGHT;

    canvas.mouseReleased = () => drawing = false
}

new p5(leftCanvas => {
    setupCanvas(leftCanvas, "leftCanvas");
})

new p5(rightCanvas => {
    setupCanvas(rightCanvas, "rightCanvas");
})
英文:

Django channels & websockets : can’t establish a connection to the server.

I am trying to do a real-time drawing app using django channels, websockets & p5.

The only problem I've got is : Firefox can’t establish a connection to the server at ws://XXX.XXX.XXX.XXX:8090/ws/room/1/.

What I've done :

settings.py :

INSTALLED_APPS = [
...
&#39;channels&#39;,
]
ASGI_APPLICATION = &#39;DrawMatch.asgi.application&#39;
CHANNEL_LAYERS = {
&#39;default&#39;: {
&quot;BACKEND&quot;: &quot;channels.layers.InMemoryChannelLayer&quot;
}
}

asgi.py :

os.environ.setdefault(&#39;DJANGO_SETTINGS_MODULE&#39;, &#39;DrawMatch.settings&#39;)
django_asgi_app = get_asgi_application()
application = ProtocolTypeRouter({
&quot;http&quot;: django_asgi_app,
&quot;websocket&quot;: AllowedHostsOriginValidator(
AuthMiddlewareStack(
URLRouter(
drawmatch_app.routing.websocket_urlpatterns
)
)
),
})

consumers.py :

class DrawConsumer(AsyncJsonWebsocketConsumer):
room_code: str = None
room_group_name: str = None
async def connect(self):
self.room_code = self.scope[&#39;url_route&#39;][&#39;kwargs&#39;][&#39;room_code&#39;]
self.room_group_name = f&#39;room_{self.room_code}&#39;
print(f&quot;room_code: {self.room_code}&quot;)
print(f&quot;room_group_name: {self.room_group_name}&quot;)
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
async def receive(self, text_data: str = None, _: Any = None) -&gt; None:
data = json.loads(text_data)
await self.channel_layer.group_send(
self.room_group_name,
{
&#39;type&#39;: &#39;draw&#39;,
&#39;data&#39;: data
}
)
async def send_message(self, res):
await self.send(text_data=json.dumps({
&quot;payload&quot;: res
}))

routing.py :

websocket_urlpatterns = [
url(r&#39;^ws/room/(?P&lt;room_code&gt;\w+)/$&#39;, DrawConsumer.as_asgi()),
]

views.py :

def room(request, room_code):
context = {
&#39;room_code&#39;: room_code
}
return render(request, &#39;room.html&#39;, context)

urls.py :

urlpatterns = [
path(&#39;&#39;, views.home),
...
path(&#39;room/&lt;room_code&gt;/&#39;, views.room),
path(&#39;predict&#39;, draw_guess.main),
]

room.html :

{% load static %}
&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
&lt;title&gt;DrawMatch - Room {{ room_code }}&lt;/title&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;{% static &#39;styles/style.css&#39; %}&quot;&gt;
&lt;script src=&quot;{% static &#39;scripts/index.mjs&#39; %}&quot; type=&quot;module&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.min.js&quot;&gt;&lt;/script&gt;
&lt;script&gt;
const csrftoken = &#39;{{ csrf_token }}&#39;;
const room_code = {{ room_code }};
const connectionString = `ws://${window.location.host}/ws/room/{{room_code}}/`;
const gameSocket = new WebSocket(connectionString);
&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;Room {{ room_code }}&lt;/h1&gt;
&lt;div class=&quot;drawings-container&quot;&gt;&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;

index.js :

const WIDTH = 500;
const HEIGHT = 500;
const STROKE_WEIGHT = 3;
const drawingsContainer = document.querySelector(&quot;.drawings-container&quot;);
console.log(gameSocket);
gameSocket.onmessage = (e) =&gt; {
console.log(`Server: ${e.data}`)
const data = JSON.parse(e.data);
if (data.type === &quot;draw&quot;) {
const {
canvas, x, y, px, py
} = data.data;
canvas.line(x, y, px, py);
}
}
gameSocket.onopen = (e) =&gt; {
console.log(&quot;Connected to websocket&quot;);
}
gameSocket.onclose = (e) =&gt; {
console.log(&quot;Disconnected from websocket&quot;);
}
function setupCanvas(canvas, id) {
console.log(connectionString);
let timeout;
let drawing = false;
canvas.setup = () =&gt; {
canvas.createCanvas(WIDTH, HEIGHT);
canvas.strokeWeight(STROKE_WEIGHT);
canvas.stroke(&quot;black&quot;);
canvas.background(&quot;#FFFFFF&quot;);
canvas.canvas.id = id;
drawingsContainer.appendChild(canvas.canvas);
}
canvas.draw = () =&gt; {
if (!drawing) return;
canvas.line(canvas.mouseX, canvas.mouseY, canvas.pmouseX, canvas.pmouseY);
if (timeout) return;
timeout = setTimeout(async () =&gt; {
const image = canvas.canvas.toDataURL();
const response = await fetch(&quot;/predict&quot;, {
method: &quot;POST&quot;, body: JSON.stringify({
image
}), headers: {
&quot;X-CSRFToken&quot;: csrftoken, &quot;Content-Type&quot;: &quot;application/json&quot;
}
})
const data = await response.text();
console.log(data);
gameSocket.send(JSON.stringify({
type: &quot;draw&quot;, data: {
canvas: canvas.id, x: canvas.mouseX, y: canvas.mouseY, px: canvas.pmouseX, py: canvas.pmouseY
}
}));
timeout = null;
}, 200);
}
canvas.mousePressed = () =&gt; drawing = canvas.mouseX &gt; 0 &amp;&amp; canvas.mouseX &lt;= WIDTH &amp;&amp; canvas.mouseY &gt; 0 &amp;&amp; canvas.mouseY &lt;= HEIGHT;
canvas.mouseReleased = () =&gt; drawing = false
}
new p5(leftCanvas =&gt; {
setupCanvas(leftCanvas, &quot;leftCanvas&quot;);
})
new p5(rightCanvas =&gt; {
setupCanvas(rightCanvas, &quot;rightCanvas&quot;);
})

答案1

得分: 1

问题出在routing.py文件中:

websocket_urlpatterns = [
    url(r'^ws/room/(?P<room_code>\w+)/$', DrawConsumer.as_asgi()),
]

正确的版本是:

websocket_urlpatterns = [
    path('ws/room/<room_code>/', DrawConsumer.as_asgi()),
]
英文:

The problem was in routing.py :

websocket_urlpatterns = [
url(r&#39;^ws/room/(?P&lt;room_code&gt;\w+)/$&#39;, DrawConsumer.as_asgi()),
]

the correct version is :

websocket_urlpatterns = [
path(&#39;ws/room/&lt;room_code&gt;/&#39;, DrawConsumer.as_asgi()),
]

huangapple
  • 本文由 发表于 2023年5月30日 01:30:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76359287.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定