-
互联网安全法,互联网净网行动
-
”净网2020”落实好维护网络安全责任
-
关于端午节放假通知-宇众网络
-
宇众网络春节放假通知
-
关于公司收款银行账户变更通知函-宇众网络
-
关于网上有人冒充我公司名义进行诈骗的公告。
-
关于端午节放假通知,节日放假,但是我们业务不“放假”-宇众网络
-
工信部进一步加强未备案网站管理工作的通知-宇众网络
-
关于东莞市宇众网络科技有限公司香港数据中心(香港机房)路由优化通知
-
宇众网络庆祝五·一劳动节快乐
-
东莞东城机房网络升级通知
-
临近过年,互联网IDC贵圈也有被骗的,请认准宇众网络公司官方联系方式
-
我司已获得ISP/ICP/IDC三证资格,更好的为客户服务
-
关于浙江金华高防机房网络线路切割通知
-
工信部近日下发关于进一步规范域名备案工作的通知
手游服务端框架之跨服匹配服,手游服务器租用
如今的手游世界,如果没搞个跨服赛事,都不好意思说它是一个手游了。
说到跨服,就不得不说下匹配服了。比如一个跨服天梯赛事,需要满足不同服的玩家能够同屏PK。为了能够把实力接近的玩家作为对手,我们需要一个独立的匹配服来收集数据,然后进行房间分配。匹配服,也是跨服赛设计的基础。
典型的匹配服通信层我们可以采用http,也可以采用socket。本文将采用http作为游戏服与匹配服的通信层。选择http方式,我们可以搭个tomcat服务,非常方便。当然,如果不使用tomcat的话,我们也可以使用mina或者netty本身的http服务。
设计思路也非常简单,有点像游戏服的业务处理器。我们需要做到,对于不同的请求,我们都绑定一个方法与之对应。而对于数据的编解码,由于匹配服的通信数据一般都比较短,我们直接用json进行序列化即可。
下面,开始我们的编码。
★如有手游服务器租用可咨询宇众临风,QQ:2850293179 Tel:15999932452 订购网址:www.yuzhongidc.com
搭建mina的http服务
在前面游戏后台设计中,我们已经看到如何使用mina搭建http服务了。
-
/** -
* 匹配服http服务 -
* @author kingston -
*/ -
public class MatchServer { -
private Logger logger = LoggerFactory.getLogger(MatchServer.class); -
private IoAcceptor acceptor; -
//http端口 -
int port = 8899; -
public void start() throws Exception { -
acceptor = new NioSocketAcceptor(); -
acceptor.getFilterChain().addLast("codec", new HttpServerCodec()); -
acceptor.setHandler(new HttpServerHandle()); -
acceptor.bind(new InetSocketAddress(port)); -
logger.error("---------> http server start at port:{}", port); -
} -
public void shutdown() { -
if (acceptor != null) { -
acceptor.unbind(); -
acceptor.dispose(); -
} -
logger.error("---------> http server stop at port:{}", port); -
} -
} -
class HttpServerHandle extends IoHandlerAdapter { -
private static Logger logger = LoggerFactory.getLogger(MatchServer.class); -
@Override -
public void exceptionCaught(IoSession session, Throwable cause) -
throws Exception { -
} -
@Override -
public void messageReceived(IoSession session, Object urlParams) -
throws Exception { -
if (urlParams instanceof HttpRequest) { -
// 请求,解码器将请求转换成HttpRequest对象 -
HttpRequest request = (HttpRequest) urlParams; -
Message msg = parseHttpRequest(request); -
UrlDispatcher.getInstance().dispatch(session, msg); -
} -
} -
@SuppressWarnings("unchecked") -
private Message parseHttpRequest(HttpRequest httpReq) { -
String service = httpReq.getParameter("service"); -
if (StringUtils.isEmpty(service)) { -
return null; -
} -
Class<?> clazz = UrlDispatcher.getInstance().getMessageClazzBy(service); -
String paramJson = httpReq.getParameter("param"); -
if (StringUtils.isNotEmpty(paramJson)) { -
try{ -
return (Message)new Gson().fromJson(URLDecoder.decode(paramJson), clazz); -
}catch(Exception e) { -
e.printStackTrace(); -
} -
} -
return null; -
} -
}
消息通信
在游戏服,我们发出一条http请求。匹配服为了将请求分发到对应的处理器,我们需要为每一条消息作一个标记。最简单的,可以使用请求消息的类名。所以,我们必须把业务签名和参数都融合到url里面去。也就是说,一个有效的url可能是这样:
http://localhost:8899?service=MReqLadderApplyMessage¶m={"playerId":0,"score":0,"power":0}
为了能区别游戏服和匹配服的消息类型,我们匹配服的消息,都加一个M(Match)前缀,那么请求协议就MReq,响应协议就是MRes了。
对于游戏服来说,发出的请求属于Message的子类,返回的消息也是Message的子类。底层帮我们实现了消息的编解码。我们可以看下代码实现。
-
public class MatchHttpUtil { -
public static Message submit(Message request) throws IOException { -
String signature = request.getClass().getSimpleName(); -
String data = new Gson().toJson(request); -
String param = HttpUtil.buildUrlParam("service", signature, -
"param", data); -
String url = "http://localhost:8899" + "?" + param; -
System.err.println("发送url:" + url); -
String resultJson = HttpUtil.get(url); -
UrlResponse urlResponse = new Gson().fromJson(resultJson, UrlResponse.class); -
String respClazz = urlResponse.getAttachemt(); -
Class<?> msgClazz = MatchMessageFactory.getInstance().getMessageBy(respClazz); -
Message msgResponse = (Message)new Gson().fromJson(urlResponse.getMessage(), msgClazz); -
return msgResponse; -
} -
}
业务处理器
我们依然使用 @Controller注解来标识一个模块处理器,使用@RequestMapper注解来标记业务处理方法。不同的是,在游戏服我们每个消息的元信息都带有一个模块号和子类型号。在匹配服,我们就不这里处理了。因为匹配服的业务比较少。我们直接用消息类的名称作为业务签名即可。
在业务分发器,我们保存每一个方法签名,与对应的方法处理器。
-
public class UrlDispatcher { -
private Logger logger = LoggerFactory.getLogger(getClass()); -
private volatile static UrlDispatcher instance; -
/** [message signature, CmdExecutor] */ -
private static final Map<String, CmdExecutor> service2Handler = new HashMap<>(); -
private static final Map<String, Class<?>> signature2Message = new HashMap<>(); -
}
匹配服在收到一个http请求,通过参数解析得到对应的业务签名,同时通过json反序列化得到请求消息的参数。将消息分发到对应的业务处理器。代码如下:
-
public void dispatch(IoSession session, Message message) { -
String signature = buildSignature(message.getClass()); -
CmdExecutor cmdExecutor = service2Handler.get(signature); -
if (cmdExecutor == null) { -
logger.error("message executor missed, signature={}", signature); -
return; -
} -
Object[] params = convertToMethodParams(session, cmdExecutor.getParams(), message); -
Object controller = cmdExecutor.getHandler(); -
try { -
//通过反射 -
cmdExecutor.getMethod().invoke(controller, params); -
}catch(Exception e) { -
logger.error("", e); -
} -
}
一个完整的业务处理器,代码如下 (可以看出,跟游戏服是非常类似的):
-
@Controller -
public class LadderController { -
@RequestMapping -
public void apply(IoSession session, MReqLadderApplyMessage request) { -
HttpMessagePusher.push(session, new MResLadderApplySuccMessage()); -
} -
}
示例代码
启动匹配服服务器(MatchStartup.java)
再执行游戏服的单元测试
-
public class TestMatchHttp { -
@Test -
public void httpRquest() throws IOException { -
Message response = MatchHttpUtil.submit(new MReqLadderApplyMessage()); -
System.err.println("收到响应<<<<<<<<<" + response); -
} -
}
手游服务端开源框架系列完整的代码请移步github ->> jforgame


宇众网络商务售后官方QQ: 2116823012