websocket前后端开发阶段总结

一、首先呢, 让我们的客户端发起请求

ws = new WebSocket("ws://192.168.199.204:7000/ws/101/"+
    xxtea.encode(JSON.stringify({openid:Global.openid,third_key:Global.third_key}))
);

这时服务器接收这个连接请求

# 目录: app/channels/application_cable/connection.rb

def connect
    self.current_user = valid_myid
end

valid_myid方法主要做下面几件事
1. 解密获取openid 和 third_key
2. 验证用户是否存在, 若存在发送welcome, 不存在拒绝连接

此时连接正常建立了.



二、订阅

接下来客户端会收到welcome消息, 然后向服务器发送订阅请求

ws.onmessage = function (event) {
    const d = JSON.parse(event.data);

    switch (d.type) {
        case "welcome" :
            // 发送订阅请求, 请求订阅WxDsqChannel这个频道
            ws.send(wsFunction.cmd_subscribe()); 
            break;
        //其他case.....
    }
}

服务器接收订阅请求

# 目录: app/channels/wx_dsq_channel.rb
class WxDsqChannel < WxGameCable::Channel
    def subscribed
        connection.gtype = GTYPE
        _subscribed  # 这个是父类的方法
    end
end

# 目录 : app/channels/application_cable/wx_game_cable.rb
module WxGameCable
    class Channel < ActionCable::Channel::Base
        def _subscribed
            # 给一条沟通的道路
            stream_from("ps_#{gtype}_#{current_user.id}")
            # 将本人信息存到redis中
            WxDB.set_user_info(pkey,current_user)
        end
    end
end

订阅成功后, 服务器会发送confirm_subscription消息

ws.onmessage = function (event) {
    const d = JSON.parse(event.data);
    switch (d.type) {
        case "confirm_subscription" :
             // 订阅之后,发送start命令 
            ws.send(wsFunction.cmd_message("start",""));
            break;
    }
}



三、 匹配

# 目录: app/channels/wx_dsq_channel.rb
def start
    _matching # 还是调用父类方法
end

# 目录 : app/channels/application_cable/wx_game_cable.rb
def _matching
    opponent = WxDB.get_opponent(gtype)

    if opponent == 0
        #队列中无人,将自己加入队列中,创建房间
        WxDB.add_opponent(gtype,cu.id)

        room = WxDB.create_room(cu.id,gtype)
        if room
            WxDB.set_user_room_id(pkey,room['r_id'])
        end
    else
        r_id = WxDB.get_user_room_id(pkey(opponent)) # 获取对手的房间号
        room_key = rkey(r_id)
        # 将房间设置成匹配成功状态
        room = _set_room_to_matched(room_key, cu.id)
        if room
            m_uid, s_uid = room['m_uid'].to_i, room['s_uid'].to_i

            WxDB.set_user_roomid_and_camp(pkey(m_uid),room['r_id'],B_BLUE)
            WxDB.set_user_roomid_and_camp(pkey(s_uid),room['r_id'],B_RED)
            players = {:m_user => WxDB.get_user_info(pkey(m_uid)), :s_user => WxDB.get_user_info(pkey(s_uid)) }
            hash = {cmd: 'matched',players:players,ts: Time.mts}.merge(room)
            # 发送匹配成功命令,将双方的信息和房间信息下发至客户端
            WxJob.perform_later Cmd.delay_time_run,"ps_#{gtype}_#{m_uid}", hash
            WxJob.perform_later Cmd.delay_time_run,"ps_#{gtype}_#{s_uid}", hash
        end
    end

end

接下来就是客户端接收匹配成功matched命令

ws.onmessage = function (event) {
    const d = JSON.parse(event.data);
    switch (d.type) {
        case "welcome" :
        ...;
        case "confirm_subscription" :
        ...;
        case "ping" :
        ...;
        default :
            switch (d.message.cmd) {
                case "matched" :
                    self.do_matched(d);// 绘制头像, 昵称 等等操作
                    ws.send(wsFunction.cmd_message("ready",""));// 发送准备命令
                    break;
            }
    }
}



四、准备

# 目录: app/channels/wx_dsq_channel.rb
def ready(msg)
    _ready(msg)
end

# 目录 : app/channels/application_cable/wx_game_cable.rb
def _ready(msg)
    room = _my_room_info
    room_key = rkey(room['r_id'])
    if room['status'].to_i == R_MATCH_SUCC
        stream_from(room_key)
        num = $wxgame.hincrby(room_key, "readied_num", 1)
        
        # 都准备好了
        if num == 2
            _set_room_to_gaming(room_key)
            # 发送开始命令
            WxJob.perform_later Cmd.delay_time_run,room_key, {cmd: "started", c_camp: room['c_camp']}
            check_time(room_key,room) # 检测超时时间, 由子类来实现
        end
    end
end

然后游戏就开始了...

接下来需要TODO的还有游戏逻辑,超时判断,结束游戏,再来一局,发起认输等等....

那么问题来了, 如何新建一个见缝插针channel 或 扫雷channel呢?



五、其他...
需要注意的一点:

  1. 发送消息要使用 Job.perform_later, 源码中有Provides behavior for enqueuing and retrying jobs.的提示, 发送消息会有丢失的情况, 可以用Job的 retry功能.
require 'active_job/arguments'

module ActiveJob
  # Provides behavior for enqueuing and retrying jobs.   注意这里
  module Enqueuing
    extend ActiveSupport::Concern

    # Includes the +perform_later+ method for job initialization.
    module ClassMethods
      # Push a job onto the queue. The arguments must be legal JSON types
      # (string, int, float, nil, true, false, hash or array) or
      # GlobalID::Identification instances. Arbitrary Ruby objects
      # are not supported.
      #
      # Returns an instance of the job class queued with arguments available in
      # Job#arguments.
      def perform_later(*args)
        job_or_instantiate(*args).enqueue
      end

      protected
        def job_or_instantiate(*args)
          args.first.is_a?(self) ? args.first : new(*args)
        end
    end
    # ...
    def retry_job(options={})
      enqueue options
    end




参考资料:

rails 参考资料: https://ruby-china.github.io/rails-guides/v5.0/action_cable_overview.html

redis命令参考官网文档:https://redis.io/commands

cocos creator网络文档: http://docs.cocos.com/creator/manual/zh/scripting/network.html

你可能感兴趣的:(websocket前后端开发阶段总结)