CS-Framework框架action的处理——分发机制

CS-Framework框架action的处理

首先简单介绍一个概念
分发器:在服务端的通道栈之上,则是一个分发器(Dispatcher,或者说是调度器),它会首先对消息进行检查,然后选择一个客户端要调用的操作。

@在CS-Framework框架中,通过客户端Client向服务器端Server发送请求,服务器端响应请求并处理,然后将处理结果返回给发出请求的客户端。所以二者之间必然涉及到客户端以及服务端的消息通信及事件处理。

这里呢,我将消息构造成一个网络消息NetMessage类(成员包含ENetCommand类(枚举类型–消息类别)、action(请求动作)、para(参数),并且让客户端以及服务端之间的消息通信遵守一定的规范。

NetMessage类具体设计如下

package com.mec.cs_framework.core;

public class NetMessage {
	private ENetCommand command;
	private String action;
	private String para;
	
	NetMessage() {
	}

	NetMessage(String message) {
		int dotIndex;
		
		dotIndex = message.indexOf('.');
		if (dotIndex < 0) {
			return;
		}
		String str = message.substring(0, dotIndex);
		this.command = ENetCommand.valueOf(str);
		
		message = message.substring(dotIndex + 1);
		dotIndex = message.indexOf('.');
		if (dotIndex < 0) {
			this.command = null;
			return;
		}
		str = message.substring(0, dotIndex); 
		this.action = str.equals(" ") ? null : str;
		
		message = message.substring(dotIndex + 1);
		this.para = message;
	}
	
	ENetCommand getCommand() {
		return command;
	}

	NetMessage setCommand(ENetCommand command) {
		this.command = command;
		return this;
	}

	String getAction() {
		return action;
	}

	NetMessage setAction(String action) {
		this.action = action;
		return this;
	}

	String getPara() {
		return para;
	}

	NetMessage setPara(String para) {
		this.para = para;
		return this;
	}

	@Override
	public String toString() {
		StringBuffer result = new StringBuffer(command.name());
		result.append('.');
		result.append(action == null ? " " : action).append('.');
		result.append(para);
		
		return result.toString();
	}
	
}

ENetCommand类具体如下

package com.mec.cs_framework.core;

public enum ENetCommand {
	OUT_OF_ROOM,
	ID,
	OFFLINE,
	FORCE_DOWN,
	
	TO_ONE,
	TO_OTHER,
	GONE,
	
	REQUEST,
	RESPONSE,
}

消息通信已经设计好,那么接下来考虑事件处理。对于不同的场景,客户端会调用不同的方法,如以下例子所示。

public void sendRequest(String action, String para) {
		conversation.sendRequest(action + ":" + action, para);
	}

public void sendRequest(String request, String response, String para) {
		conversation.sendRequest(request + ":" + response, para);
}
	
public void toOne(String targetId, String message) {
		// 通过调用conversation层所提供的相关功能实现
		conversation.toOne(targetId, message);
}
	
public void toOther(String message) {
		// 通过调用conversation层所提供的相关功能实现
		conversation.toOther(message);
}

实际上客户端调用的方法里面,消息的发送都是由客户端会话层来实现的,因此会发送不同的NetMessage,如下图所示。

void sendRequest(String action, String para) {// 向服务器发送“请求”	
		send(new NetMessage()
				.setCommand(ENetCommand.REQUEST)
				.setAction(action)
				.setPara(para));
	}

	void toOne(String targetId, String message) {	//私聊
		send(new NetMessage()
				.setCommand(ENetCommand.TO_ONE)
				.setAction(targetId)
				.setPara(message));
	}
	
	void toOther(String message) {//群聊
		send(new NetMessage()
				.setCommand(ENetCommand.TO_OTHER)
				.setPara(message));
	}

服务器会话层ServerConversation接收到客户端会话层发送过来的NetMessage实例以后,就会处理NetMessage实例,一般的做法是通过switch语句来处理。如下图所示。

@Override
	protected void dealNetMessage(NetMessage message) {
		String para = message.getPara();
		String action = message.getAction();
		String resourceId = this.id;
		String targetId = action;
		switch (message.getCommand()) {
		case OFFLINE:
			server.removeConversation(id);
			server.speakOut("客户端[" + id + "]下线!");
			close();
			break;
		case TO_OTHER:
			server.toOther(resourceId, para);
			break;
		case TO_ONE:
			server.toOne(resourceId, targetId, para);
			break;
		case REQUEST:
			
			break;
		default:
			break;
		}
	}

正如你所见的,通过switch的一般用法使功能及代码上有些混杂,而且我们每次新增处理方式都会造成函数的改动,很有可能造成不必要的麻烦;我们更希望,对NetMessage处理用对应的函数来代替,使得功能独立并且代码简洁。
比如当服务器会话层ServerConversation层接收到的NetMessage中的ENetCommand为OFFLINE时,服务器会话层ServerConversation层就会调用`dealOffline方法处理。

public void dealOffline(NetMessage message) {
		server.removeConversation(id);
		server.speakOut("客户端[" + id + "]下线!");
		close();
	}

这种方法的效果是显而易见的,所以我们这里对调用的方法有个规范,对应着不同的NetMessage中的ENetCommand,命名相应的处理方法。
这里我构造了一个DealNetMessage类来实现,该类中包含了根据NetMessage中的ENetCommand,命名相应的处理方法的方法,也包含对该方法的反射执行。该类具体设计如下。

package com.mec.cs_framework.core;

import java.lang.reflect.Method;

public class DealNetMessage {
	
	public DealNetMessage() {
	}
	
	private static String getMethodName(String command) {
		StringBuffer result = new StringBuffer("deal");//StringBuffer处理字符串连接比String高效很多,并且线程安全。
		
		String[] words = command.split("_");//以下划线为分隔符将字符串分为几个字符串数组
		int wordCount = words.length;
		for (int i = 0; i < wordCount; i++) {
			result.append(words[i].substring(0, 0+1).toUpperCase());//deal后面首字母大写
			result.append(words[i].substring(1).toLowerCase());
		}
		
		return result.toString();
	}
	
	public static void dealCommand(Object object, NetMessage message) {
		Class<?> klass = object.getClass();
		String methodName = getMethodName(message.getCommand().name());
		Method method;
		try {
			method = klass.getMethod(methodName, NetMessage.class);//获取构造的相应方法名
			method.invoke(object, message);//反射机制调用执行
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
}

客户端会话层还是服务器端会话层都是继承通信层Communication而来的,在通信层中对NetMessage的处理是一个抽象方法,因此,无论是客户端会话层还是服务器端会话层在接收到NetMessage后都会实现该方法,具体怎样实现对应不同的需求具体设计。这里我对二者的实现方式都是一样的。
具体实现方法如以下dealNetMessage(NetMessage message),里面调用了DealNetMessage.dealCommand(this, message),反射执行服务端会话层或者客户端会话层的方法。

public void dealToOther(NetMessage message) {
		server.toOther(this.id, message.getPara());
	}
	
	public void dealToOne(NetMessage message) {
		server.toOne(this.id, message.getAction(), message.getPara());
	}
	
	public void dealRequest(NetMessage message) {
		String action = message.getAction();
		int index = action.indexOf(":");
		String requestAction = action.substring(0, index);
		String responseAction = action.substring(index + 1);
		String para = message.getPara();
		String result = null;
		try {
			result = actionExecuter.doRequest(requestAction, para);
		} catch (Exception e) {
			e.printStackTrace();
		}
		send(new NetMessage()
				.setCommand(ENetCommand.RESPONSE)
				.setAction(responseAction)
				.setPara(result));
	}
	
	@Override
	protected void dealNetMessage(NetMessage message) {
		DealNetMessage.dealCommand(this, message);
	}

自从通过消息类别来命名方法,再通过反射机制来执行方法后,再也不用写switch…case…了。

也许有人会问,通过这样的方式来执行NetMessage的处理方法会不会在时间和性能上造成损失?的确,由于反射机制自身的原因,会造成必要的性能损失,但这种方法带来的好处远比损失大的多。

总结

1.建立客户端与服务端的消息通信规范
2.建立通过消息类别来命名方法的规范,再通过反射机制来执行方法。
尚有不足之处,请多多指教!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值