猿创征文|[自己做个游戏服务器三]将二进制流转换为具体的 protobuf消息
Posted 香菜+
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了猿创征文|[自己做个游戏服务器三]将二进制流转换为具体的 protobuf消息相关的知识,希望对你有一定的参考价值。
继续写这个系列,并不是为了真的做个服务器,只是为了展示服务器是怎么从零做起来,只是骨架,如果真的用来开发,还是有很多需要处理的。
废话不多说,今天写一下protobuf的转换和数据的分发
1、设计思路
因为协议使用protobuf传输,具体的内容可以看下上篇文章
[自己做个游戏服务器一]搞清楚游戏通信协议之protobuf的方方面面,评论继续送书_香菜聊游戏的博客-CSDN博客_游戏通信协议
这里没有考虑对字节流的加密,所以只是简单的说下协议的定义:
这是一个最简单的定义,当然后面这个可以使用netty 自带的LengthxxxDecoder(记不清了,我就不去查了)
我想做的就是根据headId 将数据流转换为具体的protobuf消息,然后分发到对应的handler
这样整个消息的流转基本上就形成了,后面的日志,什么监控啥的再说
2、设计代码
为了保持代码的一致性,这里定义一个抽象的handler ,向下约束,留给具体的handler实现,同时使用泛型进行泛化
import com.google.protobuf.AbstractMessage;
public abstract class AbsHandler<Msg_IN extends AbstractMessage,Msg_OUT extends AbstractMessage>
public abstract Msg_OUT handle(Msg_IN in);
为了在项目启动后对所有的handler 进行索引
创建一个注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HandlerAnno
int headerId() default 0;//默认值为0
在项目启动之后进行读取所有的handler,并且进行保存。
package com.xin.mgr;
import com.google.protobuf.Parser;
import com.xin.core.HandlerAnno;
import com.xin.handler.AbsHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class MsgMgr implements ApplicationContextAware
// headerId -> Parser
public static Map<Integer, Parser> msgMap = new ConcurrentHashMap<>();
// headerId -> Handler
public static Map<Integer, AbsHandler> handlerMap = new ConcurrentHashMap<>();
@Override
public void setApplicationContext(ApplicationContext applicationContext)
Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(HandlerAnno.class);
for (Object value : beansWithAnnotation.values())
AbsHandler<?,?> handler = (AbsHandler) value;
Class<? extends AbsHandler> aClass = handler.getClass();
try
// 获取泛型数据
ParameterizedType superGenericSuperclass = (ParameterizedType) aClass.getGenericSuperclass();
// 这里只用了第一个泛型,如果严格一些可以做参数的检查,这里就不处理了
Class protoClass = (Class) superGenericSuperclass.getActualTypeArguments()[0];
// 反射获取对应的Parser ,类似SimpleMessage.parser()
Method parser = protoClass.getMethod("parser");
Parser invoke = (Parser) parser.invoke(null);
HandlerAnno annotation = aClass.getAnnotation(HandlerAnno.class);
int headId = annotation.headerId();
// 这里可以做一个消息重复的检测
msgMap.put(headId, invoke);
handlerMap.put(headId, handler);
System.out.println();
catch (Exception e)
throw new RuntimeException(e);
从上面的代码可以看到
MsgMap 主要保存了headerId 和对应输入的Parser,将来用来转换二进制流到protobuf msg
HandlerMap 保存了headerId 和对应的handler 实例,将来用来调用handler handler.handle(msg)
定义一个handler
@HandlerAnno(headerId = 123)
@Component
public class TestHandler extends AbsHandler<SimpleMessage,SimpleMessage>
@Override
public SimpleMessage handle(SimpleMessage in)
return null;
proto 定义像下面
syntax = "proto3";
option java_multiple_files = true;
package com.xin.msg.login;
message SimpleMessage
int32 id = 1;
bool is_simple = 2;
string name = 3;
repeated int32 sample_list = 4;
启动项目可以看下,已经是具体的消息Parser
总结
前面一堆东西,其实重点就是整理出MsgMap 和 HandlerMap,至于什么手段,只要能实现都行,这里提供一个方式
而且这只是我临时写的一些代码,仅供参考,服务器就这么回事
以上是关于猿创征文|[自己做个游戏服务器三]将二进制流转换为具体的 protobuf消息的主要内容,如果未能解决你的问题,请参考以下文章
猿创征文|[自己做个游戏服务器三]将二进制流转换为具体的 protobuf消息
猿创征文|云原生之Docker使用Docker部署Flare个人导航网页
猿创征文|2022 互联网从业心得:“但愿人长久,千里共婵娟”,中秋先学会与自己内心团圆