2

i have a problem with my app. My django channels and websocket works absolutely fine, when i send a message from JS(inside rendered html) the message is going into websocket and after that to the data base(asynchronously). But when i am trying send the message before POST request Render, it throws an error.

#here everything works fine:
def room(request, room_name):

    messages = message.objects.all()
    texts = []
    
    for i in messages:
        texts.append(i.text)
        
    last_lines = texts[-20:]
    
    vars = {
        "room_name": room_name,
        "messages": last_lines,
    }
    print()

    if request.method == "POST":
        vars["post_d"] = request.POST.get("desc")
        print(vars["post_d"])

        # ws = create_connection("ws://127.0.0.1:8000/ws/chat/loobby/")
        # time.sleep(2)
        # ws.send(json.dumps({"message": message}))

        return render(request, "chat/room.html", vars)

    return render(request, "chat/room.html", vars)
#=======================================================
System check identified no issues (0 silenced).
July 12, 2021 - 08:26:23
Django version 3.1.3, using settings 'onionChat.settings'
Starting ASGI/Channels version 3.0.3 development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

Post request from c# console app
HTTP POST /chat/loobby/ 200 [0.10, 127.0.0.1:3261]

My test.py file that sends python websocket message (also works well) changes shows in the db and websocket in the web browser.

import json
import time
import asyncio
from websocket import create_connection

async def send(message):
    ws = create_connection("ws://127.0.0.1:8000/ws/chat/loobby/")
    time.sleep(2)
    ws.send(json.dumps({"message": message}))
    print("here")

if __name__ == "__main__":
    asyncio.run(send("nothing"))

#==================================================================
System check identified no issues (0 silenced).
July 12, 2021 - 08:29:48
Django version 3.1.3, using settings 'onionChat.settings'
Starting ASGI/Channels version 3.0.3 development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
WebSocket HANDSHAKING /ws/chat/loobby/ [127.0.0.1:1032]
WebSocket CONNECT /ws/chat/loobby/ [127.0.0.1:1032]
WebSocket DISCONNECT /ws/chat/loobby/ [127.0.0.1:1032]
added message to db

But when i am trying to send websocket message AND after that return render it throws an error

def room(request, room_name):

    messages = message.objects.all()
    texts = []

    for i in messages:
        texts.append(i.text)

    last_lines = texts[-20:]

    vars = {
        "room_name": room_name,
        "messages": last_lines,
    }
    print()

    if request.method == "POST":
        vars["post_d"] = request.POST.get("desc")
        print(vars["post_d"])

        ws = create_connection("ws://127.0.0.1:8000/ws/chat/loobby/")
        time.sleep(2) 
        ws.send(json.dumps({"message": message}))

    return render(request, "chat/room.html", vars)
#==================================================================
System check identified no issues (0 silenced).
July 12, 2021 - 08:34:51
Django version 3.1.3, using settings 'onionChat.settings'
Starting ASGI/Channels version 3.0.3 development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

Post request from c# console app
WebSocket HANDSHAKING /ws/chat/loobby/ [127.0.0.1:30427]
WebSocket DISCONNECT /ws/chat/loobby/ [127.0.0.1:30427]
Internal Server Error: /chat/loobby/
Traceback (most recent call last):
  File "C:\Program Files\Python39\lib\site-packages\asgiref\sync.py", line 339, in thread_handler
    raise exc_info[1]
  File "C:\Program Files\Python39\lib\site-packages\django\core\handlers\exception.py", line 38, in inner
    response = await get_response(request)
  File "C:\Program Files\Python39\lib\site-packages\django\core\handlers\base.py", line 231, in _get_response_async
    response = await wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Program Files\Python39\lib\site-packages\asgiref\sync.py", line 304, in __call__
    ret = await asyncio.wait_for(future, timeout=None)
  File "C:\Program Files\Python39\lib\asyncio\tasks.py", line 442, in wait_for
    return await fut
  File "C:\Program Files\Python39\lib\concurrent\futures\thread.py", line 52, in run
    result = self.fn(*self.args, **self.kwargs)
  File "C:\Program Files\Python39\lib\site-packages\asgiref\sync.py", line 343, in thread_handler
    return func(*args, **kwargs)
  File "C:\Users\Алексей\Desktop\dev\OnionChat\onionChat\chat\views.py", line 48, in room
    ws = create_connection("ws://127.0.0.1:8000/ws/chat/loobby/")
  File "C:\Users\Алексей\AppData\Roaming\Python\Python39\site-packages\websocket\_core.py", line 595, in create_connection
    websock.connect(url, **options)
  File "C:\Users\Алексей\AppData\Roaming\Python\Python39\site-packages\websocket\_core.py", line 252, in connect
    self.handshake_response = handshake(self.sock, *addrs, **options)
  File "C:\Users\Алексей\AppData\Roaming\Python\Python39\site-packages\websocket\_handshake.py", line 59, in handshake
    status, resp = _get_resp_headers(sock)
  File "C:\Users\Алексей\AppData\Roaming\Python\Python39\site-packages\websocket\_handshake.py", line 143, in _get_resp_headers
    status, resp_headers, status_message = read_headers(sock)
  File "C:\Users\Алексей\AppData\Roaming\Python\Python39\site-packages\websocket\_http.py", line 300, in read_headers
    line = recv_line(sock)
  File "C:\Users\Алексей\AppData\Roaming\Python\Python39\site-packages\websocket\_socket.py", line 136, in recv_line
    c = recv(sock, 1)
  File "C:\Users\Алексей\AppData\Roaming\Python\Python39\site-packages\websocket\_socket.py", line 115, in recv
    bytes_ = _recv()
  File "C:\Users\Алексей\AppData\Roaming\Python\Python39\site-packages\websocket\_socket.py", line 92, in _recv
    return sock.recv(bufsize)
ConnectionResetError: [WinError 10054] Удаленный хост принудительно разорвал существующее подключение
HTTP POST /chat/loobby/ 500 [4.97, 127.0.0.1:30426]

And my c# code, but i don't think that the problem is here

static void post_command(string command)
        {
            using (var client = new WebClient())
            {
                client.Timeout = 600 * 60 * 1000;
                
                client.Headers["User-Agent"] = "Mozilla/5.0";
                var values = new NameValueCollection();
                values["desc"] = $"{command}"; 
                client.UploadValues(link, values);
                //var responseString = Encoding.Default.GetString(response);
                //Console.Write(responseString);
            }
        }

1 Answer 1

4

I know it's rather old question, though I faced now the same issue. It seems to be not possible to open a web-socket connection from the app that already serves such a connection. That's why your test.py works - because it's outside the Django app.

So I came up with the solution that picks up the relevant channel from currently opened socket:

views.py

from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer

def room(request, room_name):
    # your logic...

    if request.method == "POST":
        vars["post_d"] = request.POST.get("desc")
        print(vars["post_d"])

        channel_layer = get_channel_layer()
        async_to_sync(channel_layer.group_send)(
            f'chat_{room_name}',
            {
                'type': 'receive',
                'message': message
            }
        )

Assuming that in routing.py you have:

from django.urls import re_path

from . import consumers

websocket_urlpatterns = [
    re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]

In your consumers.py you have to match receive method (note type='receive') and your channel name accordingly (in your example it is lobby)

from channels.generic.websocket import AsyncWebsocketConsumer


class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):

        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = f'chat_{self.room_name}'

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

        await self.accept()

    async def receive(self, text_data=None, type='receive', **kwargs):
        if isinstance(text_data, dict):
            text_data_json = text_data
        else:
            text_data_json = json.loads(text_data)

        # all other logic on handling the message...
       
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.