本节主要和大家聊聊消息处理的细节。
1、java的字节序
这是一个不经常遇到的问题,写在这里以备不时之需。
字节序是指多字节数据在计算机
内存中存储或者网络传输时各字节的存储顺序。
Little endian:将低序字节存储在起始地址
Big endian:将高序字节存储在起始地址
所谓的JAVA字节序指的是在JAVA虚拟机中多字节类型数据的存放顺序,JAVA字节序是Big endian;而网络字节序是指数据在网络上传输时是大头还是小头的,在Internet的网络字节序也是Big endian。
如果通信的一方是JAVA程序、一方是C/C++程序时,则需要在C/C++一侧使用以上几个方法进行字节序的转换,而JAVA一侧,则不需要做任
何处理,因为JAVA字节序与网络字节序都是 Big endian,只要C/C++一侧能正确进行转换即可(发送前从主机序到网络序,接收时反变换)。如果通信的双方都是JAVA,则根本不用考虑字节序的问题了。
2、支持多消息打包处理
我们在项目中经常遇到一个页面上包含两个或者两个以上功能,而服务器端的
架构又是各模块间相互独立的,这时候就需要客户端在请求时将多个
协议封装在一起请求,而服务器端按照客户端请求的协议顺序依次处理,并将处理后的消息体打包发送给客户端。
在使用多协议封装模式时,协议处理顺序和处理过程中的
异常处理需要特别注意。这里对
异常处理进行着重说明。
当多协议封装模式下某一单独协议处理出现问题时,该单独协议将抛出异常。此时需要将异常提示信息单独发送给客户端,并将之前处理过的协议信息清除(如有必要的话还需要将处理过的协议信息回滚,当然如果需要回滚的协议不建议使用封装模式发送)。
如果是长连接状态下,我们可以直接调用netty的channel直接将提示信息发送给客户端,然后将channel关闭就ok了。如果是短连接状态下,需要在response中设置状态值,每次单个协议处理完时检测状态值,并作出相应处理。
以下是http模式下协议处理的代码片段
class="java">
boolean sendFlag = true;
StringBuilder responseMessage = new StringBuilder();
StringBuilder contentMessage = new StringBuilder();
if (httpRequest.getRequestType() == REQUEST_TYPE_USERDEFINE) {
responseMessage
.append("[")
.append(httpRequest.getUserDefinedCommandId())
.append(",");
}
long allBegin = System.currentTimeMillis();
IUser user = userContainer.getUser(httpRequest.getChannelId());
HttpRTGameResponse tempResponse = new HttpRTGameResponse(
httpRequest, user);
for (Command command : httpRequest.getCommandList()) {
int messageId = command.getCommandId();
HttpGameHandler handler = handlerMap.get(messageId);
if (handler != null) {
HttpGameResponse httpResponse = new HttpGameResponse(
httpRequest.getChannelId(), command, user);
try {
HttpGameContext.initGameContext();
HttpGameContext.setResponse(httpResponse);
handler.execute(httpRequest, httpResponse);
if (!httpResponse.isClose()) {
contentMessage.append(
httpResponse.getResponseData());
if(httpResponse.getCommandId()<1000){
break;
}else{
contentMessage.append(",");
}
} else {
sendFlag = false;
contentMessage=new StringBuilder();
contentMessage.append(
httpResponse.getResponseData());
}
tempResponse.setClose(httpResponse.isDelayClose());
if (!httpResponse.isDelayClose()) {
tempResponse.addDelayResp(httpResponse
.getDelaySend());
}
} catch (Exception e) {
sendFlag = false;
if (user != null) {
logger.error("[userId = " + user.getId()
+ "]\t"
+ httpResponse.getCommand().getData(),
e);
} else {
logger.error(httpResponse.getCommand()
.getData(), e);
}
} finally {
HttpGameContext.clear();
}
if (!sendFlag) {
break;
}
} else {
sendFlag = false;
if (user != null) {
logger.warn("[userId = {}] 指令 [{}]找不到",
user.getId(), messageId);
} else {
logger.warn("指令 [{}]找不到", messageId);
}
}
}
if (sendFlag) {
responseMessage.append(contentMessage);
if (httpRequest.getRequestType() == GameRequest.REQUEST_TYPE_USERDEFINE) {
responseMessage.setCharAt(responseMessage.length() - 1,
']');
} else {
responseMessage
.deleteCharAt(responseMessage.length() - 1);
}
tempResponse.setData(responseMessage.toString());
logger.info("response:"+responseMessage.toString());
HttpServerHandler.sendHttpResponse(httpRequest.getCtx(),
httpRequest.getReq(), tempResponse.getResp());
}else{
if(contentMessage.length()>0){
tempResponse.setData(contentMessage.toString());
logger.info("response:"+contentMessage.toString());
HttpServerHandler.sendHttpResponse(httpRequest.getCtx(),
httpRequest.getReq(), tempResponse.getResp());
}else{
HttpServerHandler.sendHttpResponse(httpRequest.getCtx(),
httpRequest.getReq(), new DefaultFullHttpResponse(HTTP_1_1,
FORBIDDEN));
}
}
if (!tempResponse.isClose()) {
for (HttpGameResponse response : tempResponse
.getDelaySend()) {
HttpServerHandler.sendHttpResponse(httpRequest.getCtx(),
httpRequest.getReq(), response.getResp());
logger.info("response:"+responseMessage.toString());
}
}
long allEnd = System.currentTimeMillis();
long diff = (allEnd - allBegin);
if (diff / httpRequest.getCommandList().size() >= 500) {
if (httpRequest.getRequestType() == REQUEST_TYPE_USERDEFINE) {
logger.warn("协议 {} 总耗时:{}ms",
httpRequest.getUserDefinedCommandId(),
(int) diff);
} else {
logger.warn("协议 {} 总耗时:{}ms", httpRequest
.getCommandList().get(0).getCommandId(),
(int) diff);
}
}
3、beforeExecute和afterExecute
我们在真正处理execute之前和之后,需要有一些公共的事物需要处理。
我们通常在beforeExecute中进行资源检测、权限检测、模块开放等级检测等;而在afterExecute中通常判断是否有需要推送的消息等。