Django集成channels实现websocket微信群聊(前端+后台)

首先要有一个聊天界面,没有的话可以到bootstrapmb.com上扒一个

这里我使用的是类似微信的界面:

模板源码:

<html class="js cssanimations"><head>
  <meta charset="utf-8">
  <title>聊天室</title>
  <link rel="stylesheet" href="css/amazeui.min.css">
  <link rel="stylesheet" href="css/main.css">
</head>
<body style="">
<div class="box">
  <div class="wechat">

    <div class="sidestrip">
      <div class="am-dropdown" data-am-dropdown="">
        <!--头像插件-->
        <div class="own_head am-dropdown-toggle"></div>
        <div class="am-dropdown-content">
          <div class="own_head_top">
            <div class="own_head_top_text">
              <p class="own_name">彭于晏丶plus<img src="images/icon/head.png" alt=""></p>
              <p class="own_numb">微信号:123456</p>
            </div>
            <img src="images/own_head.jpg" alt="">
          </div>
          <div class="own_head_bottom">
            <p><span>地区</span>江西 九江</p>
            <div class="own_head_bottom_img">
              <a href=""><img src="images/icon/head_1.png"></a>
              <a href=""><img src="images/icon/head_2.png"></a>
            </div>
          </div>
        </div>
      </div>
      <!--三图标-->
      <div class="sidestrip_icon">
        <a id="si_1" style="background: url(images/icon/head_2_1.png) no-repeat;"></a>
        <a id="si_2"></a>
        <a id="si_3"></a>
      </div>

      <!--底部扩展键-->
      <div id="doc-dropdown-justify-js">
        <div class="am-dropdown" id="doc-dropdown-js" style="position: initial;">
          <div class="sidestrip_bc am-dropdown-toggle"></div>
          <ul class="am-dropdown-content" style="">
            <li>
              <a href="#" data-am-modal="{target: '#doc-modal-1', closeViaDimmer: 0, width: 400, height: 225}">意见反馈</a>
              <div class="am-modal am-modal-no-btn" tabindex="-1" id="doc-modal-1">
                <div class="am-modal-dialog">
                  <div class="am-modal-hd">
                    Modal 标题
                    <a href="javascript: void(0)" class="am-close am-close-spin" data-am-modal-close="">×</a>
                  </div>
                  <div class="am-modal-bd">
                    Modal 内容。本 Modal 无法通过遮罩层关闭。
                  </div>
                </div>
              </div>
            </li>

            <li><a href="#">备份与恢复</a></li>
            <li><a href="#">设置</a></li>
          </ul>
        </div>
      </div>
    </div>

    <!--聊天列表-->
    <div class="middle on">
      <div class="wx_search">
        <input type="text" placeholder="搜索">
        <button><span class="glyphicon glyphicon-search form-control-feedback"></span></button>
      </div>
      <div class="office_text" style="overflow: hidden;">
        <ul class="user_list" style="top: 0px; position: absolute;">

<!--          群聊列表-->
          <li class="user_active">
            <div class="user_head"><img src="images/head/15.jpg"></div>
            <div class="user_text">
              <p class="user_name">早安无恙</p>
              <p class="user_message">我是(⊙﹏⊙)!</p>
            </div>
            <div class="user_time">下午 2:54</div>
          </li>
          <li>
            <div class="user_head"><img src="images/head/2.jpg"></div>
            <div class="user_text">
              <p class="user_name">夏继涛</p>
              <p class="user_message">[小程序]</p>
            </div>
            <div class="user_time">上午 11:03</div>
          </li>
<!--          群聊列表/-->
        </ul>
        <div style="position: absolute; display: none; line-height: 0; height: 610px;" class="zUIpanelScrollBox"></div>
        <div style="position: absolute; display: none; line-height: 0; height: 491px; top: 4px;" class="zUIpanelScrollBar"></div>
      </div>
    </div>

    <!--好友列表-->
    <div class="middle">
      <div class="wx_search">
        <input type="text" placeholder="搜索">
        <span class="glyphicon glyphicon-search form-control-feedback"></span>
      </div>
      <div class="office_text" style="overflow: hidden;">
        <ul class="friends_list" style="top: 0px; position: absolute;">
          <li>
            <p>新的朋友</p>
            <div class="friends_box">
              <div class="user_head"><img src="images/head/1.jpg"></div>
              <div class="friends_text">
                <p class="user_name">新的朋友</p>
              </div>
            </div>
          </li>
          <li>
            <p>公众号</p>
            <div class="friends_box">
              <div class="user_head"><img src="images/head/2.jpg"></div>
              <div class="friends_text">
                <p class="user_name">公众号</p>
              </div>
            </div>
          </li>
          <li>
            <p>A</p>
            <div class="friends_box">
              <div class="user_head"><img src="images/head/3.jpg"></div>
              <div class="friends_text">
                <p class="user_name">彭于晏丶plus</p>
              </div>
            </div>
            <div class="friends_box">
              <div class="user_head"><img src="images/head/4.jpg"></div>
              <div class="friends_text">
                <p class="user_name">陈依依</p>
              </div>
            </div>
            <div class="friends_box">
              <div class="user_head"><img src="images/head/5.jpg"></div>
              <div class="friends_text">
                <p class="user_name">毛毛</p>
              </div>
            </div>
          </li>
          <li>
            <p>B</p>
            <div class="friends_box">
              <div class="user_head"><img src="images/head/6.jpg"></div>
              <div class="friends_text">
                <p class="user_name">苏笑言</p>
              </div>
            </div>
            <div class="friends_box">
              <div class="user_head"><img src="images/head/7.jpg"></div>
              <div class="friends_text">
                <p class="user_name">往事不再提</p>
              </div>
            </div>
          </li>
          <li>
            <p>C</p>
            <div class="friends_box">
              <div class="user_head"><img src="images/head/8.jpg"></div>
              <div class="friends_text">
                <p class="user_name">夏继涛</p>
              </div>
            </div>
            <div class="friends_box">
              <div class="user_head"><img src="images/head/9.jpg"></div>
              <div class="friends_text">
                <p class="user_name">早安无恙</p>
              </div>
            </div>
            <div class="friends_box">
              <div class="user_head"><img src="images/head/10.jpg"></div>
              <div class="friends_text">
                <p class="user_name">王鹏</p>
              </div>
            </div>
          </li>
          <li>
            <p>D</p>
            <div class="friends_box">
              <div class="user_head"><img src="images/head/11.jpg"></div>
              <div class="friends_text">
                <p class="user_name">涨了潮了</p>
              </div>
            </div>
            <div class="friends_box">
              <div class="user_head"><img src="images/head/12.jpg"></div>
              <div class="friends_text">
                <p class="user_name">Ktz丶中融资</p>
              </div>
            </div>
          </li>
        </ul>
        <div style="position:absolute;display:none;line-height:0;" class="zUIpanelScrollBox"></div><div style="position: absolute; display: none; line-height: 0;" class="zUIpanelScrollBar"></div></div>
    </div>

    <!--程序列表-->
    <div class="middle">
      <div class="wx_search">
        <input type="text" placeholder="搜索收藏内容">
        <span class="glyphicon glyphicon-search form-control-feedback"></span>
      </div>
      <div class="office_text" style="overflow: hidden;">
        <ul class="icon_list" style="top: 0px; position: absolute;">
          <li class="icon_active">
            <div class="icon"><img src="images/icon/icon.png" alt=""></div>
            <span>全部收藏</span>
          </li>
          <li>
            <div class="icon"><img src="images/icon/icon1.png" alt=""></div>
            <span>链接</span>
          </li>
          <li>
            <div class="icon"><img src="images/icon/icon2.png" alt=""></div>
            <span>相册</span>
          </li>
          <li>
            <div class="icon"><img src="images/icon/icon3.png" alt=""></div>
            <span>笔记</span>
          </li>
          <li>
            <div class="icon"><img src="images/icon/icon4.png" alt=""></div>
            <span>文件</span>
          </li>
          <li>
            <div class="icon"><img src="images/icon/icon5.png" alt=""></div>
            <span>音乐</span>
          </li>
          <li>
            <div class="icon"><img src="images/icon/icon6.png" alt=""></div>
            <span>标签</span>
          </li>
        </ul>
        <div style="position:absolute;display:none;line-height:0;" class="zUIpanelScrollBox"></div><div style="position: absolute; display: none; line-height: 0;" class="zUIpanelScrollBar"></div></div>
    </div>

    <!--聊天窗口-->
    <div class="talk_window">
      <div class="windows_top">
        <div class="windows_top_box">
          <span>早安无恙</span>
          <ul class="window_icon">
            <li><a href=""><img src="images/icon/icon7.png"></a></li>
            <li><a href=""><img src="images/icon/icon8.png"></a></li>
            <li><a href=""><img src="images/icon/icon9.png"></a></li>
            <li><a href=""><img src="images/icon/icon10.png"></a></li>
          </ul>
          <div class="extend" data-am-offcanvas="{target: '#doc-oc-demo3'}"></div>
        </div>
      </div>

      <!--聊天内容-->
      <div class="windows_body">
        <div class="office_text" style="height: 100%; overflow: hidden;">
          <ul class="content" id="chatbox" style="top: 0px; position: absolute;">
            <li class="me"><img src="images/own_head.jpg" title="金少凯"><span>疾风知劲草,板荡识诚臣</span></li>
            <li class="other"><img src="images/head/15.jpg" title="张文超"><span>勇夫安知义,智者必怀仁</span></li>
            <li class="me"><img src="images/own_head.jpg"><span>驱蚊器恶气</span></li></ul>
          <div style="position: absolute; display: none; line-height: 0; height: 0px;" class="zUIpanelScrollBox"></div><div style="position: absolute; display: none; line-height: 0; height: 0px;" class="zUIpanelScrollBar"></div></div>
      </div>

      <div class="windows_input" id="talkbox" style="">
        <div class="input_icon">
          <a href="javascript:;"></a>
          <a href="javascript:;"></a>
          <a href="javascript:;"></a>
          <a href="javascript:;"></a>
          <a href="javascript:;"></a>
          <a href="javascript:;"></a>
        </div>
        <div class="input_box">
          <textarea name="" rows="" cols="" id="input_box" style=""></textarea>
          <button id="send">发送(S)</button>
        </div>
      </div>
    </div>
  </div>
</div>

<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/amazeui.min.js"></script>
<script type="text/javascript" src="js/zUI.js"></script>
<script type="text/javascript" src="js/wechat.js"></script>

</body>
</html>

我改造后需要的:

<!-- 内容区域 -->
    <div class="content-wrapper">

        <!-- 正文区域 -->
        <section class="content">
            <div class="wechat row">
                <!--聊天列表-->
                <div class="middle on col-lg-2">
                    <div class="wx_search">
                        <input type="text" placeholder="搜索" id="search_name">
<!--                        <span class="glyphicon glyphicon-search form-control-feedback"></span>-->
                    </div>
                    <div class="office_text" style="overflow: hidden;">
                        <ul class="user_list" style="top: 0px; position: absolute;" id="chat_list"></ul>
                    </div>
                </div>

                <!--聊天窗口-->
                <div class="talk_window col-lg-10">
                    <div class="windows_top">
                        <div class="windows_top_box">
                            <span id="chat_title"></span>
                        </div>
                    </div>

                    <!--聊天内容-->
                    <div class="windows_body">
                        <div class="office_text" style="height: 100%; overflow: hidden;">
                            <ul class="content" id="chatbox" style="top: 0px; position: absolute;"></ul>
<!--                                <li class="me"><img src="/static/img/own_head.jpg" title="金少凯"><span>疾风知劲草,板荡识诚臣</span></li>-->
<!--                                <li class="other"><img src="/static/img/head/15.jpg" title="张文超"><span>勇夫安知义,智者必怀仁</span></li>-->
<!--                                <li class="me"><img src="/static/img/own_head.jpg"><span>嗨嗨嗨</span></li></ul>-->
                            <div style="position: absolute; display: none; line-height: 0; height: 0px;" class="zUIpanelScrollBox"></div><div style="position: absolute; display: none; line-height: 0; height: 0px;" class="zUIpanelScrollBar"></div></div>
                    </div>

                    <div class="windows_input" id="talkbox" style="">
                        <div class="input_icon">
                            <a href="javascript:;"></a>
                            <a href="javascript:;"></a>
                            <a href="javascript:;"></a>
                            <a href="javascript:;"></a>
                            <a href="javascript:;"></a>
                            <a href="javascript:;"></a>
                        </div>
                        <div class="input_box">
                            <textarea id="input_box"></textarea>
                            <button id="send" onclick="sendMessage()">发送(S)</button>
                        </div>
                    </div>
                </div>
            </div>

        </section>
        <!-- 正文区域 /-->

    </div>

后台代码配置:

引入channels包,直接在终端引入即可:pip install channels (可能不叫这个名)

进入settings.py修改配置:注册app、添加asgi和包   

INSTALLED_APPS = [
    'channels',
]

ASGI_APPLICATION = 'x.asgi.application'

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

asgi.py文件修改配置:(http请求不变、websocket请求修改调用路径)

"""
ASGI config for cloud_services project.

It exposes the ASGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/
"""

import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from . import routings

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'x.settings')

application = ProtocolTypeRouter({
    'http': get_asgi_application(),
    'websocket': URLRouter(routings.urlpatterns)
})

同级目录下创建routings.py,配置请求路径的标识:(调用consumers的消息处理方法)

from django.urls import re_path

from app import consumers

urlpatterns = [
    re_path(r'room/(?P<group>\w+)/$', consumers.ChatConsumer.as_asgi()),
]

创建consumers.py (里面定义了客户端收发消息的方法)

from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
from asgiref.sync import async_to_sync

class ChatConsumer(WebsocketConsumer):

    def websocket_connect(self, message):

        # 接受客户端的连接
        self.accept()

        # 获取群号
        group = self.scope['url_route']['kwargs'].get('group')

        # 将这个客户端连接对象加入特定地方 (内存 or redis)
        async_to_sync(self.channel_layer.group_add)(group, self.channel_name)

    def websocket_receive(self, message):

        # 获取群号
        group = self.scope['url_route']['kwargs'].get('group')

        # 通知组内所有客户端,执行函数
        async_to_sync(self.channel_layer.group_send)(group, {"type": "send_msg", "message": message})

    def websocket_disconnect(self, message):

        # 获取群号
        group = self.scope['url_route']['kwargs'].get('group')

        async_to_sync(self.channel_layer.group_discard)(group, self.channel_name)

    def send_msg(self, event):
        text = event['message']['text']
        self.send(text)

到此后台代码基本完成,等待前端的调用啦。

我这里实现的效果是点击列表内的群里进入,并关闭其他的群聊连接,主要代码:

function judgeTime(data){
    var date = data.toString();
    var year = date.substring(0, 4);
    var month = date.substring(4, 6);
    var day = date.substring(6, 8);
    var d1 = new Date(year + '/' + month + '/' + day);
    var dd = new Date();
    var y = dd.getFullYear();
    var m = dd.getMonth() + 1;
    var d = dd.getDate();
    var d2 = new Date(y + '/' + m + '/' + d);
    var iday = parseInt(d2 - d1) / 1000 / 60 / 60 / 24;
    return iday;
}

function chat_select(e){
    if (chat_id != $(e).attr('value')){
        //清空聊天界面
        $('#chatbox').html('');
        $('#input_box').removeAttr('disabled');
        closeConn();  //关闭其他群聊连接,如果不关闭,消息收发会重复
        $('#chat_list li').removeClass('user_active');
        $(e).addClass('user_active');
        $('#chat_title').text($(e).find('p:first').html());
        chat_id = $(e).attr('value')
        // 创建聊天室连接
        web_socket = new WebSocket("ws://192.168.125.153:9081/room/"+chat_id+"/");
        // 初始化聊天室内容
        $.ajax({
            async: true,
            type: 'post',
            url: '/drill/get_chat_message_by_chatID/',
            data: {
                "chat_id": chat_id,
                "isGroup":0,
            },
            success: function (data) {
                var res = data.data;
                if (res.length > 0){
                    for (var i = res.length-1;i>=0;i--){
                        var img_url = res[i].photo == null||res[i].photo == ''?'/static/img/user2-160x160.jpg':res[i].photo;
                        var user_name = res[i].name;
                        var message = res[i].text;
                        if (res[i].sender_id == login_id){   //判断是不是自己的消息,用于展示在群里里的效果
                            $('#chatbox').append($('<li class="me"><div style="height: 18px" align="right">' +
                                user_name +
                                '</div><img src="' +
                                img_url +
                                '" title="' +
                                user_name +
                                '"><span>' +
                                message +
                                '</span></li>'));
                        }else {
                            $('#chatbox').append($('<li class="other"><div style="height: 18px" align="left">' +
                                user_name +
                                '</div><img src="' +
                                img_url +
                                '" title="' +
                                user_name +
                                '"><span>' +
                                message +
                                '</span></li>'));
                        }
                    }
                    $('.office_text').scrollTop($('#chatbox')[0].scrollHeight);
                }
            }
        });
     //下面为进入聊天室和退出执行的方法,如果需要可以搞一些事情
// socket.onopen = function (e){ // $('#chatbox').append($('<li class="me"><img src="/static/img/own_head.jpg" title="金少凯"><span>连接成功</span></li>')); // } // socket.onclose = function (e){ // $('#chatbox').append($('<li class="me"><img src="/static/img/own_head.jpg" title="金少凯"><span>断开连接</span></li>')); // } web_socket.onmessage = function (e){ var data_str = e.data; var sender_id = data_str.slice(0,data_str.indexOf('|')); var message = data_str.slice(data_str.indexOf('|')+1); $.ajax({ async: true, type: 'post', url: '/drill/get_personal_by_id/', data:{ "login_id":sender_id, }, success: function (res) { var user_msg = res.data; var img_url = user_msg.photo == null||user_msg.photo == ''?'/static/img/user2-160x160.jpg':user_msg.photo; var user_name = user_msg.name; if (login_id == sender_id){ $('#chatbox').append($('<li class="me"><div style="height: 18px" align="right">' + user_name + '</div><img src="' + img_url + '" title="' + user_name + '"><span>' + message + '</span></li>')); }else { $('#chatbox').append($('<li class="other"><div style="height: 18px" align="left">' + user_name + '</div><img src="' + img_url + '" title="' + user_name + '"><span>' + message + '</span></li>')); } $('.office_text').scrollTop($('#chatbox')[0].scrollHeight); } }); } } } function sendMessage(){ if ($('#input_box').val() != ""&&$('#input_box').val() != null){ $.ajax({ async: true, type: 'post', url: '/drill/save_chat_message/', data: { 'text':$('#input_box').val(), 'sender_id':login_id, 'chat_id':chat_id, 'isGroup':0, }, success: function (res) { web_socket.send(login_id + "|" + $('#input_box').val()); $('#input_box').val(''); }, error:function(e){ $('#message_tips').modal('show'); $('#message').text(e.data); } }); } } $('#input_box').keydown(function (event) { if (event.keyCode == 13){ event.preventDefault(); sendMessage(); } }); function closeConn(){ web_socket.close(); }

 效果图:

 

 

 
posted @ 2022-08-05 11:31  skr_skr~  阅读(278)  评论(0)    收藏  举报