写在前面

  所谓Web即时通信,有点类似很久之前接触到的 3GQQ聊天,微信网页版。
  前几天,有个学弟请教Django实现网页在线聊天的功能,结合杨抒老师也曾提过马产业科技创新平台拍卖系统 WebSocket、心跳检测 相关的东西,觉得很有意思,因此利用业余休息时间,进行整理测试,最终实现了Web在线聊天的功能,将所得做些整理。
  现有CSDN博客的博主,多数都是通过自动回复、或者图灵聊天机器人进行回复,未涉及到两个真人即时聊天的功能,因此,在本文中将实现两个真人进行通信的功能。

  • 如果学习时间紧迫,最终完成代码将放在最后。

涉及误区

  通过学弟的功能介绍,进行了分析,很容易得出认为即时通信,即两个用户之间建立WebSocket的误区。经过功能实践,得出结论,实际上是:用户分别与服务器建立WebSocket通信,然后服务器通过判断来源用户和接受用户,以对来源用户的信息进行转发,发送给接受用户,以此实现即时通信的功能。

环境准备

Django: Python语言开发的一款Web开发框架。
dwebsocket:Django WebSocket通信第三方库,只支持django 2版本
LayIM:LayUI开发的网页即时聊天UI框架,该框架需要购买授权。

在承接某项目是购买全套LayUIAdmin后台框架授权时赠送。

1.引入LayIM

1、引入相应静态文件

2、核心通信函数【以A用户为例】

// 建立通信 A用户 和 B用户 建立通信前 走不同路由
var socket = new WebSocket("ws://" + "127.0.0.1:8000" + "/send/");

// 监听发送消息
layim.on('sendMessage', function (data) {
    var To = data.to;
    socket.send(JSON.stringify({
        type: 'chatMessage' //随便定义,用于在服务端区分消息类型
        , data: data['mine']['content']
    }));
    if (To.type === 'friend') {
        layim.setChatStatus('<span style="color:#FF5722;">对方正在输入...</span>');
    }
 });

// 监听接收消息
socket.onmessage = function (res) {
    var res = JSON.parse(res['data']);

    setTimeout(function () {
        //接受消息(如果检测到该socket)
        layim.getMessage({
            username: "用户B"
            , avatar: layui.cache.layimAssetsPath + "images/default.png"
            , id: "100001"
            , type: "friend"
            , content: res.data
        });

    }, 2000);

};

2.在Django中使用dwebsocket

Tips: Django 3+ 不再支持 dwebsocket,官方推荐采用 channels

后续本博客更新将采用channels

1、安装dwebsocket

pip3 install dwebsocket

2、在 INSTALLED_APPS 加入 dwebsocket

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    '......',
    'dwebsocket',
    '......',
]

3、在 settings.py 中进行相关设定

# dwebsocket 配置

import dwebsocket # 部分博主说将该语句放在该文件开头,个人测试不行

MIDDLEWARE_CLASSES=['dwebsocket.middleware.WebSocketMiddleware']

# 可以允许每一个单独的视图使用websockets
WEBSOCKET_ACCEPT_ALL=True

编写服务器通信方法

def user_a(request):
    """
    A用户 即时通信页面
    :param request:
    :return:
    """
    return render(request, 'user-a.html')


def user_b(request):
    """
    B用户 即时通信页面
    :param request:
    :return:
    """
    return render(request, 'user-b.html')


# 此处若只有两人通信,可以做以下操作
# 若用户有很多人,建议对建立通信的两人进行分组管理
userA = {}
userB = {}


@accept_websocket
def socket_a(request):
    """
    用户A 建立 Socket 通信
    :param request:
    :return:
    """
    if request.is_websocket():
        userid = str(uuid.uuid1())
        # 判断是否有客户端发来消息,若有则进行处理,若发来“test”表示客户端与服务器建立链接成功
        while True:
            message = request.websocket.wait()
            if not message:
                break
            else:
                print("客户端链接成功:" + str(message, encoding="utf-8"))
                # 保存客户端的ws对象,以便给客户端发送消息,每个客户端分配一个唯一标识
                userA[userid] = request.websocket
                # 发送消息
                send_msg(message, userB)


@accept_websocket
def socket_b(request):
    """
    用户B 建立 Socket 通信
    :param request:
    :return:
    """
    if request.is_websocket():
        userid = str(uuid.uuid1())
        # 判断是否有客户端发来消息,若有则进行处理,若发来“test”表示客户端与服务器建立链接成功
        while True:
            message = request.websocket.wait()
            if not message:
                break
            else:
                print("客户端链接成功:" + str(message, encoding="utf-8"))
                # 保存客户端的ws对象,以便给客户端发送消息,每个客户端分配一个唯一标识
                userB[userid] = request.websocket
                send_msg(message, userA)


def send_msg(msg, clients):
    """
    转发消息
    :param msg: 消息内容
    :param client: 接收用户唯一标识
    :return:
    """
    # 若多人中两人进行通信,无需遍历,指定分组管理的用户客户端即可
    for client in clients:
        clients[client].send(msg)
        return JsonResponse({"msg": "success"})

最终效果

image.png

程序下载

Demo下载地址

因LayIM需要额外购买授权,因此在项目中删除了LayIM相关文件,只供显示后端程序代码,如需授权,请点击购买授权:LayIM官网

遇到的坑

  因 Django 3+ 推荐使用channels, 且dwebsocket更新时间久远,因此在Django 2较新版本中不稳定,不建议在生产环境中使用。

后续计划

  将使用 channels 库在Django完成以上效果。