【从零开始学Skynet】实战篇《球球大作战》(六):gateway代码设计(中)

news2025/1/11 20:41:25

1、编码和解码

        我们来实现两个辅助方法str_unpackstr_pack,用于消息的解码和编码。

(1)str_unpack代码

local str_unpack = function(msgstr)
    local msg = {}

    while true do
        local arg, rest = string.match( msgstr, "(.-),(.*)")
        if arg then
            msgstr = rest
            table.insert(msg, arg)
        else
            table.insert(msg, msgstr)
            break
        end
    end
    return msg[1], msg
end

str_unpack是一个解码方法:

  • 参数msgstr代表消息字符串。
  • 内部是个循环结构,每次循环都由string.match匹配逗号前的字符。

例如:传入的msgstr为“login, 101, 134”,则匹配后arg的值为“login”、rest的值“101,134”;               传入的msgstr为“101, 134”,则匹配后arg的值为“101”、rest的值为“134”。

  • 每次取值后,它会把参数插入msg表,msg表用作协议对象,方便后续取值。
  • str_unpack会返回两个值,第一个值msg[1]是协议名称(协议对象第一个元素),第二个值即为协议对象

如下图所示:

        图中msgstr的值为“login,101,134”。第一个返回值cmd是字符串“login”,第二个返回值msg是一个Lua表。

 (2)str_pack代码

local str_pack = function(cmd, msg)
    return table.concat( msg, ",").."\r\n"
end

str_pack实现了与str_unpack相反的功能,如下图所示:

它将协议对象转换成字符串,并添加分隔符“\r\n”

 2、消息分发

 消息处理方法process_msg如下代码所示:

local process_msg = function(fd, msgstr)
    local cmd, msg = str_unpack(msgstr)
    skynet.error("recv "..fd.." ["..cmd.."] {"..table.concat( msg, ",").."}")

    local conn = conns[fd]
    local playerid = conn.playerid
    --尚未完成登录流程
    if not playerid then
        local node = skynet.getenv("node")
        local nodecfg = runconfig[node]
        local loginid = math.random(1, #nodecfg.login)
        local login = "login"..loginid
		skynet.send(login, "lua", "client", fd, cmd, msg)
    --完成登录流程
    else
        local gplayer = players[playerid]
        local agent = gplayer.agent
		skynet.send(agent, "lua", "client", cmd, msg)
    end
end

虽然代码只有十多行,但还是有点复杂,可通过如下四个部分理解这个方法:

1、消息解码

    通过str_unpack解码消息,相关变量的含义如下。

  • msgstr:切分后的消息,如“login,101,123”。  
  • cmd:消息名,如login。   
  • msg:消息对象,如Lua表{[1]="login", [2]="101", [3]="123"}。         

2、 如果尚未登录

     对于代码“if not playerid”为真的部分,程序将随机选取同节点的一个登录服务器转发消息,相关变量的含义如下。

  • conn:定义的连接对象。
  • playerid:如果完成登录,那么它会保存着玩家id,否则为空。
  • node:中配置文件的节点名,如“node1”。
  • nodecfg:中配置文件的节点配置,如{gateway={...},login={..}}。
  • loginid:随机的login服务编号。
  • login:随机的login服务名称,如“login2”。

3、 如果已登录

   将消息转发给对应的agent,相关变量的含义如下。

  • gpalayer:定义的gateplayer对象。
  • agent:该连接对应的代理服务id。

4、client消息

        消息转发使用了skynet.send(srv,"lua","client", ...)的形式,其中的client是自定义的消息名(skynet中的概念,指服务间传递的消息名字,它与cmd的区别是cmd是客户端协议的名字)。在我们之前封装好的service模块中,loginagent可以用s.resp.client接收转发的消息,再根据cmd做不同处理。

         下图是process_msg方法的示意图,gateway收到客户端协议后,如果玩家已登录,它会将消息转发给对应的代理(阶段③);如果未登录,gateway会随机选取一个登录服务器,并将消息转发给它处理。gateway保持着轻量级的功能,它只转发协议,不做具体处理。

 

         读者可以先屏蔽掉process_msg中分发消息的代码,用telnet等客户端测试gateway能否正常工作。由于在telnet换行即为输入分隔符“\r\n”,因此直接用换行分割消息即可。

 3、发送消息接口

        gateway将消息传给loginagentloginagent也需要给客户端回应。比如,客户端发送登录协议,login校验失败后,要给客户端回应“账号或密码错误”,这个过程如下图所示,它先将消息发送给gateway(阶段③),再由gateway(阶段④)转发。

 下面编写login给客户端发送消息的代码。

(1)send_by_fd代码:

s.resp.send_by_fd = function(source, fd, msg)
    if not conns[fd] then
        return
    end

    local buff = str_pack(msg[1], msg)
    skynet.error("send "..fd.." ["..msg[1].."] {"..table.concat( msg, ",").."}")
	socket.write(fd, buff)
end

        send_by_fd方法用于login服务的消息转发,功能是将消息发送到指定fd的客户端。它先用str_pack编码消息,然后使用socket.write将它发送给客户端。

  • 参数source:消息发送方,比如来自“login1”,
  • 参数fd和:客户端fd
  • 参数msg:消息内容。

 (2)send代码:

s.resp.send = function(source, playerid, msg)
	local gplayer = players[playerid]
    if gplayer == nil then
		return
    end
    local c = gplayer.conn
    if c == nil then
		return
    end
   
    s.resp.send_by_fd(nil, c.fd, msg)
end

        send方法用于agent的消息转发,功能是将消息发送给指定玩家id的客户端。它先根据玩家id(playerid)查找对应客户端连接,再调用send_by_fd发送。

        这两个接口会在后续实现login和agent时调用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/416489.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TCP/IP协议及配置

文章目录一、TCP/IP概述1. TCP/IP协议族2. 主机与主机之间通信的三个要素二、什么是IP地址1. 用来标识一个网络节点的互联网地址(如同电话的号码)2. IPv4地址组成三、IP地址分类1. 常用的IP地址2. 组播及科研专用四、IP地址分类(续&#xff0…

wsl下安装cuda各种踩坑记录.assets

执行nvcc -V, cuda版本位11.5 删除cuda sudo apt-get --purge remove "*cublas*" "*cufft*" "*curand*" \"*cusolver*" "*cusparse*" "*npp*" "*nvjpeg*" "cuda*" "nsight*"选择对…

打造自己特色远程桌面:SmartCode ViewerX Crack

SmartCode ViewerX VNC 查看器 ActiveX 毫不费力地将 VNC 查看器功能添加到您的应用程序 SmartCode ViewerX VNC 查看器 ActiveX 使开发人员可以使用一组直观的 ActiveX 属性和方法完全访问 VNC 查看器功能。借助ViewerX 控件,开发人员可以轻松地为其应用程序提供…

<呕心沥血>一文总结数据结构八大排序(持续更新)

目录 一、常见的八大排序 二、八大排序的算法思想 1、冒泡排序 2、选择排序 3、插入排序 4、希尔排序 5、归并排序 6、快速排序 7、堆排序 8、计数排序 三、八大排序的算法实现 一、常见的八大排序 常见的八大排序算法如下: 冒泡排序(Bubble …

Android中的接口回调机制

文章目录1.回调的含义和用途2.java实现接口回调3.Android中接口回调的体现4.接口回调在异步任务中的体现1.回调的含义和用途 一般来说,模块之间都存在一定的调用关系,从调用方式上看,可以分为三类同步调用、异步调用和回调。同步调用是一种阻…

JAVAWeb04-DOM

1. DOM 1.1 概述 1.1.1 官方文档 地址: https://www.w3school.com.cn/js/js_htmldom.asp 1.1.2 DOM 介绍 DOM 全称是 Document Object Model 文档对象模型就是把文档中的标签,属性,文本,转换成为对象来管理 1.2 HTML DOM(文档…

为什么现代企业都在使用ERP系统 它有哪些优势

随着科技的不断发展,企业管理方式也在不断地发生改变。在这个信息化的时代,企业要想取得成功,必须要善于利用先进的信息化技术工具。其中,ERP系统是企业管理中不可或缺的重要工具。本文将探讨现代企业为什么会使用ERP系统&#xf…

CPU占用率高怎么办?正确解决方法在这里!

案例:CPU占用率高怎么解决 【各位朋友,我的电脑现在运行太慢了,同事说可能是CPU占用率太高了,但对本电脑小白来说,完全不知道怎么处理,大家有什么好的方法可以解决这个问题吗?】 在计算机中&a…

快看这些wireshark 命令,必须得会!

wireshark捕获命令 捕获器表达式语法: 限定词三类 Type:host、net、prot 指出其后数字或名字的意义(主机,网段,端口) Direction:src、dst 指出传输方向 (源 、目的) …

GcExcel Java Edition 6.0.6 Crack

概述 GrapeCity Documents for Excel,Java 版,是一个编程接口,允许 Java 开发人员以编程方式大规模创建和操作 Excel 文档。GrapeCity Documents for Excel 是一种解决方案,允许开发人员跨 Java 应用程序导入/导出、创建报告和模板…

论文阅读《GlueStick: Robust Image Matching by Sticking Points and Lines Together》

论文地址:https://arxiv.org/abs/2304.02008 源码地址:https://github.com/cvg/GlueStick 概述 针对视角变化时在闭塞、无纹理、重复纹理区域的线段匹配难的问题,本文提出一种新的匹配范式(GlueStick),该方…

MySQL性能优化(三)事务与锁详解

文章目录什么是数据库事务?事务的四大特性:ACID事务的开启与结束案例表结构与数据案例事务并发的三大问题:脏读(一个事务读取到了其他事务未提交的数据)不可重复读(一个事务读取到其他事务已提交的数据造成…

Qt在安卓手机输出‘hello,world‘

我也想实现这样的功能。 最开始的参考文章: (2条消息) Qt android 开发环境搭建_逝水流年丶轻染尘的博客-CSDN博客 方案1:(失败) 我之前已经下载过 Qt5.14.2了,所以我想直接添加组件 中间过程参考: (2条消息) Qt更新组件出现&…

敏捷开发模式下如何用 PingCode 这类工具迭代管理

迭代(Sprint)是 Scrum 敏捷开发框架的核心,正确的迭代可以帮助敏捷团队提高工作交付速度。Scrum 团队以完成迭代待办列表为工作目标,并在迭代结束时交付一定的产品增量。PingCode 以产品待办列表作为迭代管理的核心,因…

java并发编程之美第二章读书笔记

并发编程的其他基础知识 什么是多线程的并发编程 并发: 同一时间段内多个任务同时都在执行,且执行都没有执行结束,强调的是在一个时间段内同时执行,而一个时间段由多个时间积累而成的,所以并发的多个任务在单位时间内并不一定同时执行 并行: 单位时间内多个任务同时在执行…

基于GPT-4免费生成代码的工具!小游戏,管理系统都能生成!

Cursor支持Python、Java、C、JavaScript、C#等等,可AI生成代码,功能非常强大!这篇教程将教你如何下载安装,带你玩转Cursor 目录 话不多说,先看能力: 只需要三步,就可以AI出你想要的代码&#x…

测试基础知识

开发模型和测试模型 软件的生命周期 软件的生命周期指的是产品从设想开始到软件不再使用的时间。 软件的生命周期可以分为6个阶段:需求分析,计划,设计,编码,测试,运行维护。 瀑布模型 适用项目&#xf…

[考研数据结构]第3章之栈的基本知识与操作

文章目录 栈的基本概念 栈的实现 顺序栈 共享栈 链栈 栈的基本概念 栈的定义 栈(Stack)是只允许在一端进行插入或删除操作的线性表 相关术语 栈顶(Top)线性表允许进行插入或删除的那一端称之为栈顶栈底(Bottom&…

JAVASE基础(二)

这里写目录标题JAVASE基础11.科学计数法12.编码和字符集12.编译格式问题13.类型转换类型级别自动类型转换强制类型转换特殊情况14.final修饰符a.修饰变量b.修饰方法c.修饰类15.scanner使用16.两个数交换引入中间变量位运算数学数方法一数学计数方法二17.扩展赋值运算符&#xf…

递归算法_字符串反转_20230412

递归算法-字符串反转 前言 递归算法对解决重复的子问题非常有效,字符串反转也可以用递归算法加以解决,递归算法设计的关键是建立子问题和原问题之间的相关性,同时需要确立递归退出的条件;如果递归退出的条件无法确定&#xff0c…