请教一个django websocket同步与异步、group分组与向特定用户发信息的问题。(语言-python)

我用django、websocket开发一个在线扑克游戏,遇到问题请教。
我的意图,也是扑克游戏的普通规则:
1、利用django自带的auth模块,建立有不同的3个用户;
2、3个用户不会同时进入牌室,等到最后(第3个用户登录并send准备好的指令)进入,后端发不同的牌给3个用户;
3、出牌时,所有用户都能看到。
这里涉及到几个概念:
1、关于群发与私发。对于发牌,是一对一向指定用户私发;对于出牌,则是群发。怎样实现群发与私发?
2、等待用户到齐,这个需要用异步模式吗?还是不一定?或者这个阶段用ajax请求?
脑袋一团雾水,请教各位的指点,最好有代码及注释说明!谢谢!

参考GPT和自己的思路:

对于你的问题,我建议使用django-socketio这个第三方库来实现websocket的功能。它可以支持异步、分组和向特定用户发送信息等功能。

对于你的需求,可以先使用django自带auth模块建立3个用户,然后在websocket连接时将用户所属的组信息传入。可以使用socketio的join_group和leave_group方法进行分组操作。

对于发牌,可以在服务器端维护一个牌局信息,然后根据用户所属的组向对应的用户发送发牌信息,例如:

from socketio.namespace import BaseNamespace

class PokerNamespace(BaseNamespace):

    def on_connect(self):
        # 将用户所属的组信息传入
        self.group = self.request.args.get('group')

    def on_ready(self, data):
        # 等待所有用户准备就绪
        ready_users = get_ready_users()
        if len(ready_users) == 3:
            # 发牌
            deal_cards()
            # 向各自分组的用户发送发牌信息
            self.socketio.emit('deal', cards, room=self.group)

对于出牌,可以使用socketio的broadcast方法向所有用户群发出牌信息,例如:

def on_play_card(self, data):
        # 出牌
        play_card(data['card'])
        # 群发出牌信息
        self.socketio.emit('play', {'player': self.request.sid, 'card': data['card']}, broadcast=True)

至于等待用户到齐的问题,可以在客户端发送一个ready事件告知服务器用户已准备就绪,然后在服务器端维护一个列表,判断列表中的用户数是否为3,若为3则说明所有用户都准备就绪,可以进行发牌操作。可以使用socketio的emit方法向单个用户发送信息,例如:

def on_ready(self, data):
        # 将准备就绪的用户添加到列表中
        self.ready_users.append(self.request.sid)
        # 发送准备就绪信息给用户
        self.socketio.emit('ready', {'player': self.request.sid})
        if len(self.ready_users) == 3:
            # 发牌
            deal_cards()
            # 向各自分组的用户发送发牌信息
            self.socketio.emit('deal', cards, room=self.group)

以上是代码示例,注意注释和自己的代码逻辑进行对比。希望可以帮助到你!

不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 这有个类似的问题, 你可以参考下: https://ask.csdn.net/questions/1070910
  • 你也可以参考下这篇文章:【完美解决方案】 Flask利用uWSGI和Nginx发布Flask应用【静态和动态部署】及兼容websocket【等待解决中】
  • 除此之外, 这篇博客: django之websocket连接中的 3.4、加入websocket通信之前端 部分也许能够解决你的问题, 你可以仔细阅读以下内容或者直接跳转源博客中阅读:
    
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
    </head>
    
    <body>
      <h1>Echo Test</h1>
      <input id="sendTxt" type="text">
      <button id="sendBtn">发送</button>
      <div id="recv"></div>
      <script type="text/javascript">
        var websocket = new WebSocket("ws://127.0.0.1:8000/socket/");
        // 引入websocket
        websocket.onopen = function () {
          console.log('websocket open');
          let obj = JSON.stringify({ type: "set_channel", content: "222",channel:"sasaas12"})
    	  setTimeout(()=>{websocket.send(obj)},5000)
          
          document.getElementById("recv").innerHTML = "Connected";
        }
        // 结束websocket
        websocket.onclose = function () {
          console.log('websocket close');
        }
        websocket.onerror = function () {
          console.log('websocket error')
        }
        // 接受到信息
        websocket.onmessage = function (e) {
          console.log(e.data);
          document.getElementById("recv").innerHTML = e.data;
        }
        // 点击发送webscoket
        document.getElementById("sendBtn").onclick = function () {
          var txt = document.getElementById("sendTxt").value;
          websocket.send(JSON.stringify({ type: "chagne", content: txt,channel:"sasaasa" }));
        }
      </script>
      <!-- <script>
        import { message } from 'antd';
    
        class WebSocketClass {
          constructor() {
            this.instance = null;
            this.connect();
          }
          static getInstance() {
            if (!this.instance) {
              this.instance = new WebSocketClass();
            }
            return this.instance;
          }
    
          connect() {
            this.ws = new WebSocket('ws://xxxx');
            this.ws.onopen = e => {
              this.status = 'open';
              message.info('连接成功');
              console.log(`连接成功`, e);
              this.heartCheck();
              this.getMessage();
            };
          }
    
          heartCheck() {
            // 心跳机制的时间可以自己与后端约定
            this.pingPong = 'ping'; // ws的心跳机制状态值
            this.pingInterval = setInterval(() => {
              if (this.ws.readyState === 1) {
                // 检查ws为链接状态 才可发送
                this.ws.send('ping'); // 客户端发送ping
              }
            }, 10000);
            this.pongInterval = setInterval(() => {
              if (this.pingPong === 'ping') {
                this.closeHandle('pingPong没有改变为pong'); // 没有返回pong 重启webSocket
              }
              // 重置为ping 若下一次 ping 发送失败 或者pong返回失败(pingPong不会改成pong),将重启
              console.log('返回pong');
              this.pingPong = 'ping';
            }, 20000);
          }
    
          closeHandle(e = 'err') {
            // 因为webSocket并不稳定,规定只能手动关闭(调closeMyself方法),否则就重连
            if (this.status !== 'close') {
              console.log(`断开,重连websocket`, e);
              if (this.pingInterval !== undefined && this.pongInterval !== undefined) {
                // 清除定时器
                clearInterval(this.pingInterval);
                clearInterval(this.pongInterval);
              }
              this.connect(); // 重连
            } else {
              console.log(`websocket手动关闭,或者正在连接`);
            }
          }
    
          getMessage() {
            this.ws.onmessage = e => {
              if (e.data === 'pong') {
                this.pingPong = 'pong'; // 服务器端返回pong,修改pingPong的状态
              } else {
                message.info(e.data);
              }
              console.log(e.data);
              return e.data;
            };
          }
    
          close() {
            clearInterval(this.pingInterval);
            clearInterval(this.pongInterval);
            this.status = 'close';
            this.ws.send('close');
            this.ws.close();
            message.info('已断开连接');
            console.log('close');
          }
        }
    
        export default new WebSocketClass();
      </script> -->
    </body>
    
    </html>
    

如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^