C/S开发框架之会话层的实现
通信层实现了最基本的网络信息的收,发,对端异常掉线的发现,这为后续工作打下了基础。而会话层的主要任务就是向对端会话层发送,并处理来自对端的网络命令。服务器和客户端之间的操作存在密切的逻辑关系,而两者又有不同,因此需要把会话层分为两部分,服务器会话层(ServerConversation)和客户端会话层(ClientConversation)。
会话层基本功能实现
上篇说到通信层(Communication)抽象类,有两个抽象方法,即处理对端消息和处理对端异常掉线,这正是会话层需要完成的工作,因此服务器会话层和客户端会话层都将继承于通信层,也就必须完成两个抽象方法的具体实现。
我们从会话层的主要任务说起,向对端会话层发送,并处理来自对端的网络命令。
ServerConversation代码片段:
void outOfRoom() {
send(new NetMessage()
.setCommand(ENetCommand.SERVER_OUT_OF_ROOM));
close();
}
public void dealSendMessageToServer(NetMessage netMessage) {
String parameter = netMessage.getParameter();
server.publishMessage(parameter);
}
ClientConversation代码片段:
public void dealServerOutOfRoom(NetMessage netMessage) {
client.getClientAction().serverOutOfRoom();
close();
}
void sendMessageToServer(String message) {
send(new NetMessage()
.setCommand(ENetCommand.SEND_MESSAGE_TO_SERVER)
.setParameter(message));
}
从上面的代码片段可以看出,服务器会话层向客户端会话层发送网络命令SERVER_OUT_OF_ROOM,在客户端会话层得到相应处理,即dealServerOutOfRoom()方法;同样的,客户端会话层向服务器会话层发送网络命令SEND_MESSAGE_TO_SERVER,在服务器会话层得到相应处理,即dealSendMessageToServer()方法。其实会话层逻辑最复杂的,服务器和客户端联系最密切的就是这部分,信息的你来我往,包括比如说客户端上线下线,私聊群聊等等功能,当然,这都还要涉及更高一层Server和Client层,甚至还有应用层。
对端不同网络命令的自动执行
其实对于不同网络命令的处理,在服务器会话层和客户端会话层,我们可以用if-else或者switch-case的方式来处理,但是不免代码过于“臃肿”,
protected void dealPeerMessage(NetMessage message) {
ENetCommand command = message.getCommand();
String parameter = message.getParameter();
switch (command) {
case SERVER_OUT_OF_ROOM:
client.getClientAction().serverOutOfRoom();
close();
break;
case WHO_ARE_YOU:
String myInfo = client.getIp() + ":" + System.currentTimeMillis();
myInfo += "#" + myInfo.hashCode();
send(new NetMessage()
.setCommand(ENetCommand.I_AM)
.setParameter(myInfo));
break;
case ENSURE_ONLINE:
this.id = parameter;
client.getClientAction().afterConnectToServer();
break;
case TO_ONE:
InteractiveInfo interInfo = ArgumentMaker.gson.fromJson(parameter, InteractiveInfo.class);
client.getClientAction().dealToOne(interInfo.getSrouceId(), interInfo.getMessage());
break;
case TO_OTHER:
interInfo = ArgumentMaker.gson.fromJson(parameter, InteractiveInfo.class);
client.getClientAction().dealToOther(interInfo.getSrouceId(), interInfo.getMessage());
break;
case SERVER_FORCE_DOWN:
client.getClientAction().serverForcedown();
close();
break;
default:
break;
}
}
如果有更多的网络命令,按照上面这种写法将会使dealPeerMessage这个方法内容特别的长,所以代码看起来也有点不方便。为此,我们希望能够将每一种网络命令的处理都分立出去,形成一个个的方法,并且能够自动执行。
为了实现自动执行,统一方法的名称格式为deal + 网络命令,并且网络命令分隔线分开的每一个词的首字母大写,其余变成小写。下面用一个类来完成这些工作。
package com.mec.csframework.core;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class PeerMessageProcessor {
private Object object;
private Class<?> klass;
public PeerMessageProcessor() {
}
void setObject(Object object) {
this.object = object;
this.klass = object.getClass();
}
private String changeToLower(String str) {
String result = str.substring(0, 1);
result += str.substring(1).toLowerCase();
return result;
}
void dealNetMessage(NetMessage netMessage) {
ENetCommand command = netMessage.getCommand();
String commandName = command.name();
String[] commandNames = commandName.split("_");
String methodName = "deal";
for (String name : commandNames) {
methodName += changeToLower(name);
}
try {
Method method = klass.getDeclaredMethod(methodName, NetMessage.class);
method.invoke(object, netMessage);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
有了这个类,我们在服务器会话层和客户端会话层,只需调用即可。
@Override
protected void dealPeerMessage(NetMessage message) {
// 处理来自客户端的网络命令
peerMessageProcessor.dealNetMessage(message);
}
@Override
public void dealPeerMessage(NetMessage message) {
//处理来自服务器的网络命令
peerMessageProcessor.dealNetMessage(message);
}
ArgumentMaker类—参数构造器
客户端向服务器发REQUEST时,需要传递参数,在执行action对应的方法时,需要用户提供的具体的参数值,都要对参数进行解析,传递给服务器,为此,我们专门准备一个类,将帮助客户端生成相关参数的json字符串,并且以变量名为键,json字符串为值,构成一个Map,将这个Map的对象也转换成json字符串,发送给服务器。
package com.mec.csframewok.action;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
public class ArgumentMaker {
public static final Gson gson = new GsonBuilder().create();
public static final Type type = new TypeToken<Map<String, String>>(){}.getType();
private Map<String, String> argPool;
public ArgumentMaker() {
argPool = new HashMap<String, String>();
}
public ArgumentMaker(String json) {
argPool = gson.fromJson(json, type);
}
/**
* 获取参数的值
* @param name
* @param type
* @return
*/
public Object getValue(String name, Class<?> type) {
String valueJson = argPool.get(name);
return gson.fromJson(valueJson, type);
}
/**
* 获取参数的值,方法的重载
* @param name
* @param type
* @return
*/
public Object getValue(String name, Type type) {
String valueJson = argPool.get(name);
return gson.fromJson(valueJson, type);
}
/**
* 增加一个参数
* @param name
* @param value
* @return 为了方便链式调用,返回对象本身
*/
public ArgumentMaker add(String name, Object value) {
argPool.put(name, gson.toJson(value));
return this;
}
/**
* 将argPool转换成json字符串
*/
@Override
public String toString() {
return gson.toJson(argPool);
}
}
Gson类与参数值的传递
如果客户端发送的是八大基本类型或者String类型的参数数据,在服务器端进行解析倒也方便,但是,如果客户端传输的是一个类的对象,或者是带泛型的数据,这在服务器的解析工作将变得艰难。不过,有一个工具类Gson,可以帮助我们很好的处理。(需要导入jar包)
我们通过一个简单测试类来看一下Gson的主要功能:
package com.mec.csframewok.test;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.mec.complex.core.Complex;
public class TestForGson {
public static void main(String[] args) {
//创建一个gson对象
Gson gson = new GsonBuilder().create();
//创建一个Complex类对象
Complex c1 = new Complex(1.0, 2.3);
//通过gson,将Complex类对象c1转化成json格式的字符串
String str = gson.toJson(c1);
System.out.println(str);
//json格式的字符串转化为Complex类对象
Complex c2 = gson.fromJson(str, Complex.class);
System.out.println(c2);
}
}
下面是运行结果:
{"real":1.0,"vir":2.3}
(1.0,2.3)
从运行结果可以看出,所谓的json格式,就是“键”:“值”形式。
ServerConversation完整代码:
package com.mec.csframework.core;
import java.io.IOException;
import java.net.Socket;
import com.mec.csframework.action.IActionProcessor;
import com.mec.util.ArgumentMaker;
public class ServerConversation extends Communication {
private static final int OK = 1;
private static final int ILLEGAL_USER = 2;
private String ip;
private String id;
private Server server;
private IActionProcessor actionProcessor;
private PeerMessageProcessor peerMessageProcessor;
protected ServerConversation(Socket socket, Server server) throws IOException {
super(socket);
this.server = server;
this.peerMessageProcessor = new PeerMessageProcessor();
this.peerMessageProcessor.setObject(this);
}
IActionProcessor getActionProcessor() {
return actionProcessor;
}
void setActionProcessor(IActionProcessor actionProcessor) {
this.actionProcessor = actionProcessor;
}
String getId() {
return id;
}
void forcedown() {
send(new NetMessage()
.setCommand(ENetCommand.SERVER_FORCE_DOWN));
close();
}
void outOfRoom() {
send(new NetMessage()
.setCommand(ENetCommand.SERVER_OUT_OF_ROOM));
close();
}
void whoAreYou() {
send(new NetMessage()
.setCommand(ENetCommand.WHO_ARE_YOU));
}
void toOne(InteractiveInfo interactiveInfo) {
send(new NetMessage()
.setCommand(ENetCommand.TO_ONE)
.setParameter(ArgumentMaker.gson.toJson(interactiveInfo)));
}
void toOther(InteractiveInfo interactiveInfo) {
send(new NetMessage()
.setCommand(ENetCommand.TO_OTHER)
.setParameter(ArgumentMaker.gson.toJson(interactiveInfo)));
}
public void dealIAm(NetMessage netMessage) {
String parameter = netMessage.getParameter();
server.getTemporaryConversationPool().removeTempConversation(this);
int result = checkClient(parameter);
if (result == OK) {
// 生成客户端id,并将其从temp中移动到clientPool中,
// 并告知“XXX客户端登录”,同时告知客户端相关id。
ensureClientOnline();
} else if (result == ILLEGAL_USER) {
// 告知客户端,你是非法用户!并停止侦听,从temp中删除!
close();
}
}
public void dealOffline(NetMessage netMessage) {
server.getClientPool().removeClient(this);
server.publishMessage("客户端[" + this.id + "]下线!");
close();
}
public void dealToOne(NetMessage netMessage) {
String parameter = netMessage.getParameter();
InteractiveInfo interactiveInfo = ArgumentMaker.gson.fromJson(parameter, InteractiveInfo.class);
server.toOne(interactiveInfo);
}
public void dealToOther(NetMessage netMessage) {
String parameter = netMessage.getParameter();
InteractiveInfo interactiveInfo = ArgumentMaker.gson.fromJson(parameter, InteractiveInfo.class);
server.toOther(interactiveInfo);
}
public void dealRequest(NetMessage netMessage) {
String action = netMessage.getAction();
int index = action.indexOf('#');
String request = action.substring(0, index);
String response = action.substring(index + 1);
String parameter = netMessage.getParameter();
try {
String responseResult = actionProcessor.dealRequest(request, parameter);
send(new NetMessage()
.setCommand(ENetCommand.RESPONSE)
.setAction(response)
.setParameter(responseResult));
} catch (Exception e) {
e.printStackTrace();
server.publishMessage(e.getMessage());
}
}
public void dealSendMessageToServer(NetMessage netMessage) {
String parameter = netMessage.getParameter();
server.publishMessage(parameter);
}
@Override
protected void dealPeerMessage(NetMessage message) {
// 处理来自客户端的网络命令
peerMessageProcessor.dealNetMessage(message);
}
private void ensureClientOnline() {
this.id = this.ip + ":" + System.currentTimeMillis();
server.getClientPool().addClient(this);
server.publishMessage("客户端[" + this.id + "]上线!");
send(new NetMessage()
.setCommand(ENetCommand.ENSURE_ONLINE)
.setParameter(this.id));
}
private int checkClient(String message) {
int index = message.indexOf("#");
String clientInfo = message.substring(0, index);
String password = message.substring(index + 1);
index = clientInfo.indexOf(":");
this.ip = clientInfo.substring(0, index);
if (clientInfo.hashCode() == Integer.valueOf(password)) {
return OK;
}
return ILLEGAL_USER;
}
@Override
protected void peerAbnormalDrop() {
server.getTemporaryConversationPool().removeTempConversation(this);
server.getClientPool().removeClient(this);
server.publishMessage("客户端[" + id + "]异常掉线!");
}
}
ClientConversation完整代码:
package com.mec.csframework.core;
import java.io.IOException;
import java.net.Socket;
import com.mec.util.ArgumentMaker;
public class ClientConversation extends Communication {
private Client client;
private String id;
private PeerMessageProcessor peerMessageProcessor;
ClientConversation(Client client, Socket socket) throws IOException {
super(socket);
this.client = client;
this.peerMessageProcessor = new PeerMessageProcessor();
this.peerMessageProcessor.setObject(this);
}
void offline() {
send(new NetMessage()
.setCommand(ENetCommand.OFFLINE));
close();
}
void sendRequest(String request, String response, String parameter) {
send(new NetMessage()
.setCommand(ENetCommand.REQUEST)
.setAction(request + "#" + response)
.setParameter(parameter));
}
void sendMessageToServer(String message) {
send(new NetMessage()
.setCommand(ENetCommand.SEND_MESSAGE_TO_SERVER)
.setParameter(message));
}
public void dealServerOutOfRoom(NetMessage netMessage) {
client.getClientAction().serverOutOfRoom();
close();
}
public void dealWhoAreYou(NetMessage netMessage) {
NetNode me = client.getMe();
String myInfo = me.getIp() + ":" + System.currentTimeMillis();
myInfo += "#" + myInfo.hashCode();
send(new NetMessage()
.setCommand(ENetCommand.I_AM)
.setParameter(myInfo));
}
public void dealEnsureOnline(NetMessage netMessage) {
String parameter = netMessage.getParameter();
this.id = parameter;
client.getClientAction().afterConnectToServer();
}
public void dealToOne(NetMessage netMessage) {
String parameter = netMessage.getParameter();
InteractiveInfo interactiveInfo = ArgumentMaker.gson.fromJson(parameter, InteractiveInfo.class);
client.getClientAction().dealToOne(interactiveInfo.getSourceId(), interactiveInfo.getMessage());
}
public void dealToOther(NetMessage netMessage) {
String parameter = netMessage.getParameter();
InteractiveInfo interactiveInfo = ArgumentMaker.gson.fromJson(parameter, InteractiveInfo.class);
client.getClientAction().dealToOther(interactiveInfo.getSourceId(), interactiveInfo.getMessage());
}
public void dealServerForceDown(NetMessage netMessage) {
client.getClientAction().serverForcedown();
close();
}
public void dealResponse(NetMessage netMessage) {
String action = netMessage.getAction();
String parameter = netMessage.getParameter();
try {
client.getActionProcesser().dealResponse(action, parameter);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void dealPeerMessage(NetMessage message) {
peerMessageProcessor.dealNetMessage(message);
}
String getId() {
return id;
}
void toOne(String targetId, String message) {
InteractiveInfo interactiveInfo = new InteractiveInfo(this.id, targetId, message);
send(new NetMessage()
.setCommand(ENetCommand.TO_ONE)
.setParameter(ArgumentMaker.gson.toJson(interactiveInfo)));
}
void toOther(String message) {
InteractiveInfo interactiveInfo = new InteractiveInfo(this.id, null, message);
send(new NetMessage()
.setCommand(ENetCommand.TO_OTHER)
.setParameter(ArgumentMaker.gson.toJson(interactiveInfo)));
}
@Override
protected void peerAbnormalDrop() {
client.getClientAction().serverAbnormalDrop();
}
}
至此,关于CSFramework的会话层以及参数构造器工具简述结束。
下一篇将继续CSFramework的Server, Client层的实现。
更多推荐
CSFramework---会话层(Conversation)的实现
发布评论