如何在特定时间通过 websocket 发送消息

Posted

技术标签:

【中文标题】如何在特定时间通过 websocket 发送消息【英文标题】:How to send a message over websocket at a certain time 【发布时间】:2019-09-17 22:34:25 【问题描述】:

我是 websockets 的新手,刚刚在我的应用程序中获得了一个有效的 websocket 连接。我试图让服务器每分钟在数据库中检查一次以查找即将到来的锦标赛,并且对于连接到从该分钟开始的锦标赛中注册的 websocket 的每个玩家,发送一条消息,表明 ID 为 xxxxx 的锦标赛现在开始。我有以下

比赛/consumers.py:

from channels.generic.websocket import WebsocketConsumer
import json

class TournamentLobbyConsumer(WebsocketConsumer):

    def connect(self):
        self.accept()

    def disconnect(self, close_code):
        pass

    def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']
        print("The websocket received a message: '%s'" % message)

比赛/routing.py:

from django.conf.urls import url

from . import consumers

websocket_urlpatterns = [
    url('ws/tournaments/$', consumers.TournamentLobbyConsumer),
]

比赛/模板/index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>Tournament Lobby</title>
</head>

<script>
    var chatSocket = new WebSocket(
        'ws://' + window.location.host +
        '/ws/tournaments/');

    chatSocket.onmessage = function(e) 
        // var data = JSON.parse(e.data);
        // var message = data['message'];
        alert("message received from websocket")
    ;

    chatSocket.onclose = function(e) 
        console.error('Chat socket closed unexpectedly');
    ;
</script>

% if tournaments %

  <ul>
    % for tournament in tournaments %
      <li><a href="% url 'tournament_detail' tournament.id %">  tournament.name   tournament.start_time </a></li>
    % endfor %
  </ul>
% else %

  <p>No tournaments are available</p>

% endif %



</html>

当我进入这个锦标赛大厅时,我在服务器上收到一条消息,说发生了“websocket 握手”。所以 websocket 连接正在工作。我现在对如何在正在运行的服务器上运行一个循环感到困惑,该循环每分钟检查一次新锦标赛,然后将消息发送到这些连接的客户端。我做的教程只展示了一个服务器响应客户端请求,但是一个 websocket 应该能够在任何一个方向上。

【问题讨论】:

【参考方案1】:

查看apscheduler 以安排您的工作。您的代码如下所示:

scheduler = BackgroundScheduler()
scheduler.add_job(check, 'cron', second='*/60')
scheduler.start()

# Function to run every 60 seconds
def check():
    pass

【讨论】:

【参考方案2】:

您必须首先调用负责发送通知(通道)的消费者方法 https://channels.readthedocs.io/en/latest/topics/channel_layers.html(在消费者之外使用)

import channels.layers
from asgiref.sync import async_to_sync

def SimpleShipping(data, **kwargs):

  group_name = 'notifications'
  channel_layer = channels.layers.get_channel_layer()

  async_to_sync(channel_layer.group_send)(
    group_name,
    
      'type': 'notify_event',
      'data': data,
      # other: data,
    
  )

在consumer中声明方法(将consumer添加到通知通道)

import json

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

class TournamentLobbyConsumer(WebsocketConsumer):

  room_group_name = 'notifications'

  def connect(self):
    # Join room group
    async_to_sync(self.channel_layer.group_add)(
      self.room_group_name,
      self.channel_name
    )

    self.accept()

  def disconnect(self, close_code):
    # Leave room group
    async_to_sync(self.channel_layer.group_discard)(
      self.room_group_name,
      self.channel_name
    )

  # Receive message from WebSocket
  def receive(self, text_data):
    # ...
    pass

  # Receive message from room group (notifications)
  def notify_event(self, event):
    data = event['data']
    # Send message to WebSocket
    self.send(text_data=json.dumps(
      'data': data,
    ))

现在你必须选择后台任务的方法(推荐Celery http://docs.celeryproject.org/en/latest/django/first-steps-with-django.html) (看这个问题Django Celery Periodic Task at specific time)


from projectname.appname.modulename import SimpleShipping

@shared_task()
def daily_reports():
  # Run the query in the database.
  # ...
  data =  'results': 'results' 
  # notify consumers (results)
  SimpleShipping(data)

注意:希望对您有所帮助,因为您要实现的任务相当广泛,您不应掉以轻心,尽管此摘要可以让您看到数据的流动

【讨论】:

以上是关于如何在特定时间通过 websocket 发送消息的主要内容,如果未能解决你的问题,请参考以下文章

如何通过spring websocket STOMP向特定订阅发送消息?

如何在 websocket 服务器上向特定用户发送消息

如何使用java服务器将消息发送到特定的websocket连接

在 Spring Websocket 上向特定用户发送消息

Spring4.0 WebSocket怎么向指定用户发送信息

播放框架 websocket 发生时不会向前端发送消息