CSFramework---会话层(Conversation)的实现

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层的实现。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值