当前位置: 首页 > article >正文

7.即时通讯

文章目录

  • 即时通讯
    • 1.类型
    • 2.需求场景
    • 3.传统的推送实现
    • 4.websocket
    • 5.websocket优点
    • 6.websocket库Socket.IO
      • 1.安装
      • 2.创建服务
      • 3.常见方法
    • 7.Django+channel
      • 1.安装
      • 2.配置

即时通讯

定义: 即时通讯(Instant Messaging)

1.类型

  • 在线push适用于web页面和APP

    • 使用websocket
    • 使用Socket.IO
    • 自己封装socket
  • 离线push:APP

    • ios:APNs
    • andorid:FCM/第三方服务(网易云信/融云/环信/LeanCloud)

2.需求场景

  • 即时聊天
  • 用户A关注了用户B,系统需要向用户B推送提示消息
  • 用户下了订单,需要在运营管理后台向运营人员推送新订单通知

3.传统的推送实现

HTTP/1.x 不支持服务器主动推送,HTTP/2支持服务器主动推送,但HTTP/2 还未全面实施

  • 轮询:

    • 定义:浏览器每隔一段时间就向浏览器发送http请求,不论是否有数据更新都直接响应,
    • 缺点:效率低下,浪费资源
  • 长轮训(基于长连接Comet):

    • 定义:服务端收到客户端的请求不会直接响应,而是先将这个请求挂起来,然后判断服务端数据是否有更新,如果有更新,则响应,如果没有数据,则达到一定时间限制才返回
    • 缺点:依然需要反复发出请求效率低下,浪费资源

4.websocket

WebSocket协议定义:

  • 是基于html5(HyperText Markup Language)定义的协议

  • 单个TCP连接上进行全双工通信的协议

  • Websocket 通过 HTTP/1.1 协议的101状态码进行握手

  • ws或wss的统一资源标志符

    ws://example.com/wsapi
    wss://secure.example.com/
    
  • 端口:80/443

  • 报文

    # 客户端
    GET / HTTP/1.1
    Upgrade: websocket   # 必须设置Websocket,表示希望升级到Websocket协议
    Connection: Upgrade  # 客户端希望连接升级
    Host: example.com   
    Origin: http://example.com
    # Sec-WebSocket-Key与magic_string进行hmac1加密,再进行base64加密
    Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ== # 随机的字符串
    Sec-WebSocket-Version: 13
    
    # 服务端
    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
    Sec-WebSocket-Location: ws://example.com/
    

5.websocket优点

  • 较少的控制开销
  • 更强的实时性
  • 保持连接状态
  • 可以支持扩展
  • 更好压缩效果
  • 没有的压缩效果
  • 没有同源限制
  • 可以发送文本

6.websocket库Socket.IO

1.安装

pip install python-socketio

2.创建服务

方式一:协程方式访问

import eventlet

eventlet.monkey_patch()

import socketio
import eventlet.wsgi

sio = socketio.Server(async_mode='eventlet')
app = socketio.Middleware(sio)
eventlet.wsgi.server(eventlet.listen(('', 8000)), app)

为什么不使用多进程或多线程

  • 受限于服务能性能有限创建的进程数和线程数,导致并发连接客户端不会很高,

3.常见方法

  • @sio.on(‘connect’),connect 为特殊事件,当客户端连接后自动执行

    @sio.on('connect')
    def on_connect(sid, environ):
        """
        与客户端建立好连接后被执行
        :param sid: string sid是socketio为当前连接客户端生成的识别id
        :param environ: dict 在连接握手时客户端发送的握手数据(HTTP报文解析之后的字典)
        """
        pass
    
  • @sio.on(‘disconnect’),disconnect 为特殊事件,当客户端断开连接后自动执行

    @sio.on('disconnect')
    def on_disconnect(sid):
        """
        与客户端断开连接后被执行
        :param sid: string sid是断开连接的客户端id
        """
        pass
    
  • @sio.on(‘自定义事件’) ,connect,disconnect与自定义事件处理方法的函数传入参数不同

    # 以字符串的形式表示一个自定义事件,事件的定义由前后端约定
    @sio.on('my custom event')  
    def my_custom_event(sid, data):
        """
        自定义事件消息的处理方法
        :param sid: string sid是发送此事件消息的客户端id
        :param data: data是客户端发送的消息数据
        """
        pass
    
  • sio.emit(),:发送事件消息

    #群发特定事件消息
    sio.emit('my event', {'data': 'foobar'})
    #给指定用户特定事件消息发送
    sio.emit('my event', {'data': 'foobar'}, room=user_sid)
    #群发所有人
    sio.send({'data': 'foobar'})
    # 群发给指定房间的人
    sio.send({'data': 'foobar'}, room=user_sid)
    
    
  • sio.rooms(sid):查询sid客户端所在的所有房间

  • sio.enter_room(sid, room_name):将连接的客户端添加到一个room

  • sio.leave_room(sid, room_name):将客户端从一个room中移除

7.Django+channel

1.安装

pip install channels==4.2.0
pip install daphne==4.1.2

2.配置

asgi.py

import os

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

from app1 import routings

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

application = ProtocolTypeRouter({
    'http': get_asgi_application(),  # 支持http请求
    'websocket': URLRouter(routings.websocket_urlpatterns),  # 支持websocket请求
})

settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'daphne',  # 添加daphne
    'django.contrib.staticfiles',
    'corsheaders',
    'channels',# 添加channels
    'app1'

]
WSGI_APPLICATION = 'djangoProject.wsgi.application'
ASGI_APPLICATION = "djangoProject.asgi.application"

app1/urls.py

from django.contrib import admin
from django.urls import path
from app1.views import chat

urlpatterns = [
    path('admin/', admin.site.urls),
    path("chatone", chat)
]

app1/views.py

from django.shortcuts import render


def chat(request):
    return render(request, "chatting.html")

app1/routings.py

from django.urls import re_path
from app1 import consumers as consm1

websocket_urlpatterns = [
    re_path(r'room/', consm1.ChatConsumer.as_asgi())
]

app1/consumers.py

from channels.exceptions import StopConsumer
from channels.generic.websocket import WebsocketConsumer


class ChatConsumer(WebsocketConsumer):
    def websocket_connect(self, message):
        """
        客户端向服务端发送websocket连接的请求时自动触发。
        """
        print("1 > 客户端和服务端开始建立连接")
        self.accept()

    def websocket_receive(self, message):
        """
        客户端基于websocket向服务端发送数据时,自动触发接收消息。
        """
        print(f"2 > 服务端接收客户端的消息, message is {message}")
        recv_data = message["text"]

        if recv_data == "exit":  # 服务端主动关闭websocket连接时,前端会执行对应的 onclose
            self.close()
            # raise StopConsumer()    # raise主动抛异常后,websocket_disconnect 就不在执行了,多用于`只处理服务端向客户端断开`的场景
            return

        send_data = f"服务端主动推送消息:{recv_data}"
        self.send(text_data=send_data)

    def websocket_disconnect(self, message):
        """
        客户端与服务端断开websocket连接时自动触发(不管是客户端向服务端断开还是服务端向客户端断开都会执行)
        """
        print("3 > 客户端和服务端断开连接")
        self.close()
        raise StopConsumer()

chatting.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .message {
            height: 300px;
            border: 1px solid #dddddd;
            width: 100%;
        }
    </style>
</head>
<body>
<div class="message" id="message"></div>
<div>
    <input type="text" placeholder="请输入聊天内容:" id="txt">
    <input type="button" value="点击发送" onclick="sendMessage()">
    <input type="button" value="关闭连接" onclick="closeConn()">
</div>

<script>

    // 实例化websocket对象,并客户端主动以websocket方式连接服务端
    wbsocket = new WebSocket("ws://127.0.0.1:1010/room/123/");

    // 创建好websocket连接成功后自动触发(服务端执行self.accept()后)
    wbsocket.onopen = function (event) {
        var tag = document.createElement("div");
        tag.innerText = '[连接成功!]';
        document.getElementById("message").appendChild(tag);
    };

    // 创建连接失败后自动触发
    wbsocket.onerror = function (event) {
        var tag = document.createElement("div");
        tag.innerText = '[连接失败!]';
        document.getElementById("message").appendChild(tag);
    };

    // 当websocket接收到服务器发来的消息时会自动触发
    wbsocket.onmessage = function (event) {
        var tag = document.createElement("div");
        tag.innerText = event.data;
        document.getElementById("message").appendChild(tag);
    };

    // 当服务端主动断开客户端时自动触发(服务端执行self.close()后)
    wbsocket.onclose = function (event) {
        var tag = document.createElement("div");
        tag.innerText = '[连接已断开!]';
        document.getElementById("message").appendChild(tag);
    };

    // 页面上客户端点击向服务端"关闭连接"时触发
    function closeConn() {
        wbsocket.close();       // 客户端主动断开连接,服务端会执行 websocket_disconnect()
        var tag = document.createElement("div");
        tag.innerText = '[连接已断开啦!]';
        document.getElementById("message").appendChild(tag);
    }

    // 页面上客户端点击向服务端"发送消息"时触发
    function sendMessage() {
        var info = document.getElementById("txt");
        wbsocket.send(info.value);   // 客户端给服务端发数据
    }

</script>

</body>
</html>


http://www.kler.cn/a/457261.html

相关文章:

  • Druid连接Oracle数据库,连接失效导致SQL无法执行
  • 为什么需要设置 `NCCL_P2P_DISABLE=1` 和 `NCCL_IB_DISABLE=1`?
  • 机器学习随机森林回归时间序列预模型中时间滑动窗口作用以及参数设置
  • 微服务-Sentinel新手入门指南
  • c#中的事件和委托
  • tar.gz压缩文件在linux上解压异常问题:gzip:stdin:invalid compressed data
  • 深度学习中batch_size
  • MySQL并发问题区别-MVCC如何解决的
  • Linux 下 Mamba 环境安装踩坑问题汇总(重置版)
  • 【前端】Vue3 父传子 Dialog 显示问题:解决方案与最佳实践
  • 狼人杀.转载
  • 神经网络初学总结(一)
  • 国密算法SM3的GmSSL代码Android实现Demo
  • 【Leecode】Leecode刷题之路第93天之复原IP地址
  • 使用Python实现智能交通信号控制系统
  • 深度学习笔记(12)——深度学习概论
  • CDN如何抵御DDoS攻击
  • 如何在 Ubuntu 22.04 上使用 systemctl 管理 systemd 服务教程
  • Pytorch | 利用MIG针对CIFAR10上的ResNet分类器进行对抗攻击
  • python lambda函数用法
  • Android `android.graphics.drawable` 包深度解析:架构与设计模式
  • zentao ubuntu上安装
  • EMNLP'24 最佳论文解读 | 大语言模型的预训练数据检测:基于散度的校准方法
  • 探索鸿蒙的蓝牙A2DP与访问API:从学习到实现的开发之旅
  • 从零开始采用命令行创建uniapp vue3 ts springboot项目
  • 《PHP Switch》