Websocket Disconnected Connect Call Failed

痴心易碎 提交于 2020-07-23 07:31:08

问题


I am following a turtorial Django Channel 2, I followed all steps and i am having a failed connection in console. I noticed this problem occur when i add Channel_layer to settings, when i remove Channel_layer the connection becomes successful. How do i solve this problem?

Manager

class ThreadManager(models.Manager):
def by_user(self, user):
    qlookup = Q(first=user) | Q(second=user)
    qlookup2 = Q(first=user) & Q(second=user)
    qs = self.get_queryset().filter(qlookup).exclude(qlookup2).distinct()
    return qs

def get_or_new(self, user, other_username): # get_or_create
    username = user.username
    if username == other_username:
        return None
    qlookup1 = Q(first__username=username) & Q(second__username=other_username)
    qlookup2 = Q(first__username=other_username) & Q(second__username=username)
    qs = self.get_queryset().filter(qlookup1 | qlookup2).distinct()
    if qs.count() == 1:
        return qs.first(), False
    elif qs.count() > 1:
        return qs.order_by('timestamp').first(), False
    else:
        Klass = user.__class__
        user2 = Klass.objects.get(username=other_username)
        if user != user2:
            obj = self.model(
                    first=user, 
                    second=user2
                )
            obj.save()
            return obj, True
        return None, False

Model.py

class Profile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,blank=True,null=True) 
    profile_pic = models.ImageField(upload_to='ProfilePicture/', default="ProfilePicture/avatar.png", blank=True)



class Thread(models.Model):
    first        = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='chat_thread_first')
    second       = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='chat_thread_second')
    updated      = models.DateTimeField(auto_now=True)
    timestamp    = models.DateTimeField(auto_now_add=True)

    objects      = ThreadManager()

    @property
    def room_group_name(self):
        return f'chat_{self.id}'

    def broadcast(self, msg=None):
        if msg is not None:
            broadcast_msg_to_chat(msg, group_name=self.room_group_name, user='admin')
            return True
        return False


class ChatMessage(models.Model):
    thread      = models.ForeignKey(Thread, null=True, blank=True, on_delete=models.SET_NULL)
    user        = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='sender', on_delete=models.CASCADE)
    message     = models.TextField()
    timestamp   = models.DateTimeField(auto_now_add=True)

Views.py

class InboxView(LoginRequiredMixin, ListView):
    template_name = 'inbox.html'
    def get_queryset(self):
        return Thread.objects.by_user(self.request.user)

class ThreadView(LoginRequiredMixin, FormMixin, DetailView):
    template_name = 'thread.html'
    form_class = ComposeForm
    success_url = './'

    def get_queryset(self):
        return Thread.objects.by_user(self.request.user)

    def get_object(self):
        other_username  = self.kwargs.get("username")
        obj, created    = Thread.objects.get_or_new(self.request.user, other_username)
        if obj == None:
            raise Http404
        return obj

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['form'] = self.get_form()
        return context

    def post(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return HttpResponseForbidden()
        self.object = self.get_object()
        form = self.get_form()
        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

    def form_valid(self, form):
        thread = self.get_object()
        user = self.request.user
        message = form.cleaned_data.get("message")
        ChatMessage.objects.create(user=user, thread=thread, message=message)
        return super().form_valid(form)

urls.py

from django.urls import path

from pixmate.views import (
    InboxView,
    ThreadView,
)
app_name = 'pixmate'

urlpatterns = [
    path("inbox", InboxView.as_view(), name='inbox'),
    path('messages/<username>/', ThreadView.as_view(), name='messages'),
]

asgi.py

import os
import django
from channels.routing import get_default_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pixmateDjango.settings")
django.setup()
application = get_default_application()

consumers.py

import asyncio
import json
from django.contrib.auth import get_user_model
from channels.consumer import AsyncConsumer
from channels.db import database_sync_to_async
from pixmate.models import Thread, ChatMessage
from django.db.models import Q


class ChatConsumer(AsyncConsumer):
    async def websocket_connect(self, event):
        print('connected', event)
    
        other_user = self.scope['url_route']['kwargs']['username']
        me         = self.scope['user']
        thread_obj = await self.get_thread(me, other_user)
        self.thread_obj = thread_obj
        chat_room = f"thread_{thread_obj.id}"
        self.chat_room = chat_room
        await self.channel_layer.group_add(
            chat_room,
            self.channel_name
        )
        await self.send({
            "type": "websocket.accept"
        })
    

    async def websocket_receive(self, event):
        # when a message is recieved from the websocket
        print("receive", event)
        front_text = event.get('text', None)
        if front_text is not None:
            loaded_dict_data = json.loads(front_text)
            msg = loaded_dict_data.get('message')
            user = self.scope['user']
            username = 'default'
            if user.is_authenticated:
                username = user.username
            myResponse = {
                'message': msg,
                'username': username
            }
            await self.create_chat_message(user, msg)
            # broadcasts the message event to be sent, the group send layer
            # triggers the chat_message function for all of the group (chat_room)
            await self.channel_layer.group_send(
                self.chat_room,
                {
                    "type": "chat_message",
                    "text": json.dumps(myResponse)
                }
            )

    # chat_method is a custom method name that we made
    async def chat_message(self, event):
        #sends the actual message
        await self.send({
            "type": "websocket.send",
            "text": event['text']
        })

    async def websocket_disconnect(self, event):
        # when the socket disconnects
        print('disconnected', event)

    @database_sync_to_async
    def get_thread(self, user, other_username):
        return Thread.objects.get_or_new(user, other_username)[0]

    @database_sync_to_async
    def create_chat_message(self, me, msg):
        thread_obj = self.thread_obj
        return ChatMessage.objects.create(thread=thread_obj, user=me, message=msg)

routing.py

from django.urls import path
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from channels.security.websocket import AllowedHostsOriginValidator, OriginValidator

from pixmate.consumers import ChatConsumer

application = ProtocolTypeRouter({
    # Empty for now (http->django views is added by default)
    'websocket': AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter(
                [
                    path('messages/<username>/', ChatConsumer),
                ]
            )
        )
    )
})

settings.py

ASGI_APPLICATION = "pixmateDjango.routing.application"

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("localhost", 6379)],
        },
    },
}

Thread.html

<div class="px-4 py-3 chat-box bg-white" id='chat-items' style="height:435px;">
    {% for chat in object.chatmessage_set.all %}
     {% if request.user != chat.user %}
    <!-- Sender Message-->
    <div class="media w-50 mb-3">  
      <img src="{{ chat.user.profile.profile_pic.url }}" id="avatarImg" width="30" height="30" class="rounded-circle mt-1">         
      <div class="media-body ml-2">
        <div class="bg-light rounded py-2 px-3 mb-2">
          <p class="text-small mb-0 text-black">{{ chat.message|emoticons }}</p>
        </div>
        <p class="small text-muted">
        {{ chat.timestamp|date:'M j, Y,  g:i a' }}
        <i class="fa fa-ellipsis-h float-right"></i>
        </p>
      </div>
    </div>
    {% endif %}
    {% endfor %}

<form id='form' method='POST' action="." class="chatbox-form">
    {% csrf_token %}
    {{ form }}
    <input = "submit" value="send">
</form>


<!-- Channels Reconnecting Websocket -->
<script src='https://cdnjs.cloudflare.com/ajax/libs/reconnecting-websocket/1.0.0/reconnecting-websocket.js'></script> 

<script>
  // websocket scripts - client side*
  var loc = window.location
  var formData = $("#form")
  var msgInput = $("#id_message")
  var chatHolder = $('#chat-items')
  var me = $('#myUsername').val()

  var wsStart = 'ws://'
  if (loc.protocol == 'https:') {
    wsStart = 'wss://'
  }
  var endpoint = wsStart + loc.host + loc.pathname
  var socket = new ReconnectingWebSocket(endpoint)

  // below is the message I am receiving
  socket.onmessage = function(e) {
    console.log("message", e)
    var chatDataMsg = JSON.parse(e.data)

    var profile_pic = $('#avatarImg').attr("src");
    console.log(profile_pic);

    //alert(e.data)
   if(chatDataMsg.username == me) {
    chatHolder.append("<div class='media w-50 ml-auto mb-3'>" + "<div class='media-body'>" + "<div class='bg-primary rounded py-2 px-3 mb-2'>" + "<p class='text-small mb-0 text-white'>" + chatDataMsg.message + "</p>" + "</div>" + "<p class='small text-muted'>" + newMonth  +  newDate + "<i class='fa fa-ellipsis-h float-right'></i>" + "</p>" + "</div>" + "</div>")
   }
   else{
    chatHolder.append("<div class='media w-50 mb-3'>" + "<img src=" + profile_pic + " width='30' height='30' class='rounded-circle mt-1'/>" + "<div class='media-body ml-2'>" + "<div class='bg-light rounded py-2 px-3 mb-2'>" + "<p class='text-small mb-0 text-black'>" + chatDataMsg.message + "</p>" + "</div>" + "<p class='small text-muted'>" + newMonth  +  newDate + "<i class='fa fa-ellipsis-h float-right'></i>" + "</p>" + "</div>" + "</div>")
   }
      }
  // below is the message I am sending
  socket.onopen = function(e) {
    console.log("open", e)
    formData.submit(function(event){
        event.preventDefault()
        var msgText = msgInput.val()
        //chatHolder.append("<li>" + msgText + " via " + me + "</li>")
        var finalData = {
        'message': msgText
        }
        socket.send(JSON.stringify(finalData))
        formData[0].reset()
    })
  }
  socket.onerror = function(e) {
    console.log("error", e)
  }
  socket.onclose = function(e) {
    console.log("close", e)
  }
</script>

{% endblock %}

回答1:


I figured out the solution to the problem: Redis do not support in windows, so i installed Memurai and it worked.



来源:https://stackoverflow.com/questions/62750983/websocket-disconnected-connect-call-failed

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!