jforgame-socket快速入门

1.jforgame-socket开发背景

1.1socket介绍

Socket是用于在计算机网络中实现通信的编程接口。它提供了一种通过网络在不同计算机之间传输数据的方式。不同http基于请求-响应模式,socket是全双工的,允许服务器、客户端同时向另外一端发送数据。由于socket工作在TCP/IP协议中的运输层,而不是像http这种工作在应用层,因此使用socket通信需要建立自己的私有协议栈。通过私有协议栈定义网络传输的字节流的具体意义。

1.2原生netty/mina复杂性与缺陷

Netty/Mina都是用于构建高性能、可扩展网络应用程序的Java框架。它们提供了一套抽象的、事件驱动的异步网络编程模型,使得开发者可以轻松地构建各种网络应用,例如服务器、客户端、代理等。

尽管Netty/Mina是一个强大而灵活的框架,但是对于初学者来说,可能会感到一些复杂性。下面是一些可能与Netty相关的复杂性因素:

  1. 编解码器和处理器:Netty/Mina提供了一套强大的编解码器和处理器,用于处理不同的协议和数据格式。选择合适的编解码器和处理器,并正确配置它们,可能需要一些学习和实践。特别是处理数据流粘包/拆包问题。

  2. 业务消息路由器:Netty/Mina只提供关于网络消息IO方面的内容,对于业务消息的路由处理(类似于SpringMVC的DispatchServlet机制),需要项目自行封装。

  3. 更高层次的统一:当项目引入Netty或者Mina之后,业务代码就与框架强耦合。如果中途需要切换顶层网关,需要修改项目的很多代码。

2.jforgame-socket框架介绍

jforgame-socket是一个通用的网络工具,底层对Netty/Mina进行了封装,屏蔽了私有协议栈定制,消息编解码,消息粘包/拆包问题。jforgame-socket传输层使用了TCP协议可用于任何需要socket通信的应用。例如游戏服务器,聊天服务器等等。

2.1jforgame-socket结构

jforgame-socket主要有三个组件组成,分别是

  • jforgame-socket-api: 底层通用接口,封装了Session接口,服务器/客户端通信接口,消息路由,客户端rpc接口等重要接口。
  • jforgame-socket-mina:socket的mina实现,提供了服务器/客户端大部分默认组件,以及私有协议栈设计,提供TcpSocketServerBuilder工具类用于快速启动游戏服务器
  • jforgame-socket-netty:socket的netty实现,提供了服务器/客户端大部分默认组件,以及私有协议栈设计,提供TcpSocketServerBuilder工具类用于快速启动游戏服务器

2.2jforgame-socket其他依赖组件

jforgame-socket提供了默认的私有协议栈,但对于一个完整消息的编解码是没有强制绑定的,运行客户端代码自行设计,例如可以使用protobuf,json,xml,protostuf等等。

jforgame项目提供了两种默认编解码工具

  • jforgame-codec-protobuf:使用protobuf进行消息编解码,为了省略.protobuf文件的编写,引入JProtobuf基于注解的编解码。
  • jforgame-codec-struct:基于jdk反射实现的编解码,对于一个简单的javabean,该工具基于反射解析该类每个字段的类型以及顺序,动态生成消息实体以及注入属性。使用简单方便,推荐!

3.使用案例

3.1添加中央仓库地址(国内其他云镜像仓库,可能还未同步)

<repository><id>repo2</id><name>Mirror from Maven Repo2</name><url>http://repo2.maven.org/maven2/</url>
</repository>

3.2添加socket依赖(以jforgame-socket-netty为例)

<dependency><groupId>io.github.jforgame</groupId><artifactId>jforgame-socket-netty</artifactId><version>1.0.0</version>
</dependency>

3.3添加消息编解码依赖(以jforgame-codec-struct为例)

<dependency><groupId>io.github.jforgame</groupId><artifactId>jforgame-codec-struct</artifactId><version>1.0.0</version>
</dependency>

3.4自行设计线程模型(1.1将提供默认实现)

游戏业务由于类型非常多,像棋牌,MMORPG,策略游戏,h5小游戏等,所用的业务线程模型是不一致的,作为socket框架无法提供通用实现。

public class DispatchThreadModel implements ThreadModel {private static final Logger logger = LoggerFactory.getLogger(DispatchThreadModel.class);private final int CORE_SIZE = Runtime.getRuntime().availableProcessors();/*** task worker pool*/private final Worker[] workerPool = new Worker[CORE_SIZE];private static final AtomicBoolean running = new AtomicBoolean(true);public DispatchThreadModel() {ThreadFactory threadFactory = new NamedThreadFactory("message-business");for (int i = 0; i < CORE_SIZE; i++) {Worker w = new Worker();workerPool[i] = w;threadFactory.newThread(w).start();}}private static class Worker implements Runnable {LinkedBlockingQueue<BaseGameTask> taskQueue = new LinkedBlockingQueue<>();void receive(BaseGameTask task) {taskQueue.add(task);}@Overridepublic void run() {while (running.get()) {try {BaseGameTask task = taskQueue.take();task.run();} catch (InterruptedException e) {// TODO other way?Thread.currentThread().interrupt();} catch (Exception e) {logger.error("", e);}}}}/*** when receiving a task, the executor will calculate the thread index based on the {@link BaseGameTask#getDispatchKey()}* for example, if the executor group has N threads, the task will be dispatched to the thread which index is (dispatchKey() % N)* @param task command task*/@Overridepublic void accept(BaseGameTask task) {if (task == null) {throw new NullPointerException("task is null");}if (!running.get()) {return;}int distributeKey = (int) (task.getDispatchKey() % CORE_SIZE);workerPool[distributeKey].receive(task);}/*** when this executor shuts down, it will no longer accept new task* and the remained tasks will be abandoned either.*/@Overridepublic void shutDown() {running.compareAndSet(true, false);}}

3.5消息分发器

对网络session的创建,摧毁,消息接受等提供一个钩子接口,允许(必须)业务代码自行设计。如此方可适用于所有socket应用。

public class MessageIoDispatcher extends ChainedMessageDispatcher {private MessageHandlerRegister handlerRegister;private MessageParameterConverter msgParameterConverter;private MessageFactory messageFactory = GameMessageFactory.getInstance();public MessageIoDispatcher(String scanPath) {this.msgParameterConverter = new DefaultMessageParameterConverter(messageFactory);this.handlerRegister = new CommonMessageHandlerRegister(scanPath, messageFactory);MessageHandler messageHandler = (session, message) -> {int cmd = GameMessageFactory.getInstance().getMessageId(message.getClass());MessageExecutor cmdExecutor = handlerRegister.getMessageExecutor(cmd);if (cmdExecutor == null) {logger.error("message executor missed,  cmd={}", cmd);return true;}Object[] params = msgParameterConverter.convertToMethodParams(session, cmdExecutor.getParams(), message);Object controller = cmdExecutor.getHandler();int sessionId = (int) session.getAttribute(SessionProperties.DISTRIBUTE_KEY);MessageTask task = MessageTask.valueOf(session, sessionId, controller, cmdExecutor.getMethod(), params);task.setRequest(message);// 丢到任务消息队列,不在io线程进行业务处理GameServer.getMonitorGameExecutor().accept(task);return true;};addMessageHandler(messageHandler);}@Overridepublic void onSessionCreated(IdSession session) {session.setAttribute(SessionProperties.DISTRIBUTE_KEY, SessionManager.INSTANCE.getNextSessionId());}@Overridepublic void onSessionClosed(IdSession session) {long playerId = SessionManager.INSTANCE.getPlayerIdBy(session);if (playerId > 0) {logger.info("角色[{}]close session", playerId);PlayerEnt player = GameContext.playerManager.get(playerId);BaseGameTask closeTask = new BaseGameTask() {@Overridepublic void action() {GameContext.playerManager.playerLogout(playerId);}};GameServer.getMonitorGameExecutor().accept(closeTask);}}}

3.6一句话启动服务器

除了自行设计两个无法提供默认实现的组件之外,其他的功能jforgame-socet框架都帮你完成啦。只需引入builder工具类即可。

		TcpSocketServerBuilder.newBuilder().bindingPort(HostAndPort.valueOf(ServerConfig.getInstance().getServerPort())).setMessageFactory(GameMessageFactory.getInstance()).setMessageCodec(new StructMessageCodec()).setSocketIoDispatcher(new MessageIoDispatcher(ServerScanPaths.MESSAGE_PATH)).build().start();

好了,开始你的网络通信项目吧。

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

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

相关文章

电脑windows 蓝屏【恢复—无法加载操作系统,原因是关键系统驱动程序丢失或包含错误。.......】

当你碰到下图这种情况的电脑蓝屏&#xff0c;先别急着重装系统&#xff0c;小编本来也是想重装系统的&#xff0c;但是太麻烦&#xff0c;重装系统后你还得重装各种软件&#xff0c;太麻烦了&#xff01;&#xff01; 这种情况下&#xff0c;你就拿出你的启动U盘&#xff0c;进…

2016国赛-路径之谜

分析&#xff1a; 看到n*n以及四个方向移动&#xff0c;那么就直接使用dfs即可。根据题意可知起始位置是(0,0)&#xff0c;终点位置是(n-1,n-1)。 又有要求靶子上的箭数决定了走的路径&#xff0c;那么我们就要加一个判断各个方位的箭数是否符合要求。 示例代码&#xff1a; …

「CISP题库精讲」CISP题库习题解析精讲4道

第一题 以下关于互联网协议安全(InternetProtocolSecurity,IPSec)协议,说法错误的是?() A.在传送模式中,保护的是IP负载。 B.验证头协议(AuthenticationHeader,AH)和IP封装安全负载协议(EncapsulatingSecurityPayload,ESP)都能以传输模式和隧道模式工作。 C.在隧道模…

JVM之堆

堆的核心概述 一个JVM实例只存在一个堆内存&#xff0c;堆也是内存管理的核心区域。 Java堆区在JVM启动的时候即被创建&#xff0c;其空间大小也就确定了。是JVM管理的最大一块内存空间。 堆内存的大小是可以调节的。 《JVM虚拟机规范》规定&#xff0c;堆可以处于物理上不连…

Pillow教程04:学习ImageDraw+Font字体+alpha composite方法,给图片添加文字水印

---------------Pillow教程集合--------------- Python项目18&#xff1a;使用Pillow模块&#xff0c;随机生成4位数的图片验证码 Python教程93&#xff1a;初识Pillow模块&#xff08;创建Image对象查看属性图片的保存与缩放&#xff09; Pillow教程02&#xff1a;图片的裁…

盲盒小程序开发,互联网盲盒下的潜在发展优势

近几年&#xff0c;我国潮玩市场经历了爆发式的发展阶段&#xff0c;尤其是盲盒市场屡创新高&#xff01;盲盒商品主打IP衍生品、周边等具有收藏价值的商品&#xff0c;深受市场的追捧&#xff0c;满足了不同年龄群体的需求。面对盲盒的蓝海市场&#xff0c;众多的品牌也纷纷加…

Altium Designer的差分对布线走线技巧及规则设置

AD的PCB页面是有差分对布线的工具的&#xff0c;这种工具的使用首先需要自己添加差分对&#xff0c;才能进行交互式差分对布线&#xff1a; 在原理图中放置差分对标识&#xff0c;其中差分对要以_P和_N结尾来命名&#xff1a; 在原理图中放置差分对&#xff1a; 差分对在PCB中的…

浏览器导出excel

做java web项目时&#xff0c;经常遇到需要在页面上点击导出按钮&#xff0c;然后直浏览器接下载下来一个excel文档。 比如一个List<Person>的集合&#xff0c;需要将每个Person当做一行&#xff0c;输出到excel中去。其中Person实体类如下&#xff1a; import lombok.…

selenium元素定位--xpath定位--层级与逻辑组合定位

其他元素非唯一时&#xff0c;又不想用xpath绝对定位时&#xff0c;需要用到层级与逻辑定位. 一、层级属性结合定位&#xff1a; 遇到元素没有class、name、id等或属性动态变化情况时&#xff0c;可以找父节点元素&#xff0c;父级节点没有id时&#xff0c;可以继续往上找id&…

✨一键释放手机空间,让生活更流畅——手机清理大师超实用体验分享

&#x1f4dd;亲爱的朋友们&#xff0c;你是否也曾为手机里堆积如山的照片、杂乱无章的相册和不断提醒存储不足的问题而头疼不已呢&#xff1f;今天给大家安利一款我近期爱不释手的神器——手机清理大师&#xff0c;它就如同你的手机专属大扫除小能手&#xff0c;让你的手机瞬间…

Python爬虫:爬虫基本概念、流程及https协议

本文目录&#xff1a; 一、爬虫的基本概念1.为什么要学习爬虫1.1 数据的来源1.2 爬取到的数据用途 2.什么是爬虫3. 爬虫的更多用途 二、爬虫的分类和爬虫的流程1.爬虫的分类2.爬虫的流程3.robots协议 三、爬虫http和https1.http和https的概念2.浏览器发送HTTP请求的过,2.1 http…

数据分析面试题(41~50)

41、lstm的原理、lstm和rnn的区别 ①LSTM是一种常用于处理序列数据的循环神经网络&#xff08;RNN&#xff09;架构&#xff0c;特别适用于长序列的建模。其主要特点是通过门控机制来控制信息的流动&#xff0c;从而有效地解决了传统RNN在处理长序列时的梯度消失或爆炸的问题。…

Git学习笔记之基础

本笔记是阅读《git pro》所写&#xff0c;仅供参考。 《git pro》网址https://git-scm.com/book/en/v2 git官网 https://git-scm.com/ 一、git起步 1.1、检查配置信息 git config --list查看所有的配置以及它们所在的文件 git config --list --show-origin可能有重复的变量名…

云原生最佳实践系列 3:基于 SpringCloud 应用玩转 MSE

概述 随着业务不断创新&#xff0c;大型的单个应用和服务会被拆分为数个甚至数十个微服务&#xff0c;微服务架构已经被广泛应用。微服务的好处在于快速迭代&#xff0c;迭代过程保障线上流量不受损。依赖开源产品缺少专业运维工具&#xff0c;常常需要投入较大的运维人力和成…

SCI一区 | Matlab实现WOA-TCN-BiGRU-Attention鲸鱼算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测

SCI一区 | Matlab实现WOA-TCN-BiGRU-Attention鲸鱼算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测 目录 SCI一区 | Matlab实现WOA-TCN-BiGRU-Attention鲸鱼算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测预测效果基本介绍模型描述程序…

用Power BI,原始表格包括订单的付款时间,付款金额,统计今年的总付款金额况

可以使用DAX函数来计算今年的总付款金额。假设你的表格名为"Orders"&#xff0c;包括字段"PaymentTime"和"PaymentAmount"&#xff0c;可以使用以下DAX函数来计算今年的总付款金额&#xff1a; TotalPaymentAmount SUMX(FILTER(Orders, YEAR(O…

Matter - nordic 自定义开发(4)

nRF Connect SDK 和 Matter SDK 的 matter 协议版本 nRF Connect SDK&#xff08;NCS&#xff09;是 Nordic 官方维护的&#xff0c;里面包含某个版本的 Matter SDK。Matter SDK 为 CSA 联盟维护的&#xff0c;里面包含各平台的SDK&#xff0c;其中包含了某个版本的 NCS。 需…

调用第三方接口:springBoot整合forest

Forest是什么&#xff1f; Forest是一个高层的、极简的轻量级 HTTP调用API框架&#xff0c;让Java发送HTTP/HTTPS请求不再难。它比OkHttp和HttpClient更高层&#xff0c;比Feign更轻量&#xff0c;是封装调用第三方restful api client接口的好帮手。 相比于直接使用Httpclien…

抖音视频关键词批量采集工具|无水印视频爬虫提取软件

抖音视频关键词批量采集工具&#xff1a; 我们很高兴地介绍最新推出的抖音视频关键词批量采集工具&#xff0c;该工具集成了多项强大功能&#xff0c;让您轻松实现视频内容的批量提取和下载。以下是详细的功能解析和操作说明&#xff1a; 主要功能&#xff1a; 关键词批量提取…

数据库与缓存一致性如何保证

最近建了一个技术交流群&#xff0c;欢迎志同道合的同学加入&#xff0c;群里主要讨论&#xff1a;分享业务解决方案、深度分析面试题并解答工作中遇到的问题&#xff0c;同时也能为我提供写作的素材。 欢迎加Q&#xff1a;312519302&#xff0c;进群讨论 前言 在工作中&#…