JAVA SE 实战篇 C4 简单CSFramework(中) 会话层的搭建

P4 服务器会话层

1 会话池 ServerConversationPool类

package com.mec.csframework.core;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ServerConversationPool {
	
	private Map<String, ServerConversation> conversationPool;
	
	
	/**
	 * 所有方法采用包权限,作为内部工具
	 */
	ServerConversationPool() {
		this.conversationPool = new HashMap<>();
	}
	
	//将每个连接到服务器的客户端的服务器会话层加入会话池
	void addClient(ServerConversation client) {
		this.conversationPool.put(client.getId(), client);
	}
	
	//根据id将其从会话池中删除
	void removeClient(String id) {
		this.conversationPool.remove(id);
	}
	
	//根据id获取客户端的服务器会话层
	ServerConversation getClient(String id) {
		return this.conversationPool.get(id);
	}
	
	//获取会话池中连接数量
	int getClientCount() {
		return this.conversationPool.size();
	}
	
	//查询会话池是否为空
	boolean isEmpty() {
		return this.conversationPool.isEmpty();
	}
	
	//得到除了指定客户端外的所有客户端的服务器会话层
	List<ServerConversation> getClientExcept(String id) {
		
		List<ServerConversation> clientList = new ArrayList<>();
		
		for(String orgId : this.conversationPool.keySet()) {
			if(orgId.equals(id)) {
				continue;
			}
			clientList.add(this.conversationPool.get(orgId));
		}	
		return clientList;
	}
	
	
	//得到所有客户端的服务器会话层
	List<ServerConversation> getClient() {
		return getClientExcept(null);
	}
	
	
	
}

2 服务器会话层 ServerConversation类

package com.mec.csframework.core;

import java.io.IOException;
import java.net.Socket;

public class ServerConversation extends Communication {
	
	//未来要生成唯一的Id所需要的客户端的ip地址
	private String ip;
	
	//每个服务器会话端都需要唯一的ID
	private String id;
	
	//把Server当作成员,以便调用其中的方法
	private Server server;
	
	String getId() {
		return this.id;
	}
	
	String getIp() {
		return this.ip;
	}
	
	void setId(String id) {
		this.id = id;
	}

	protected ServerConversation(Socket socket, Server server) throws IOException {
		super(socket);
		this.server = server;
		//根据连接socket得到客户端的ip地址
		this.ip = socket.getInetAddress().getHostAddress();
	}
	
	/**
	 * 为每个ServerConversation产生一个唯一的id
	 * 生成ID后还要发给对应的ClientConversation
	 */
	void createClientId() {
		//每连接上一个客户端,为其的ServeerConversation生成一个唯一的ID
		//(ip地址 +当前时间毫秒)的hashCode是很难被破解的
		this.id = String.valueOf((this.ip + System.currentTimeMillis()).hashCode());
		
		send(new NetMessage()
				.setCommand(ENetCommand.ID)
				.setSource(Server.SERVER)
				.setTarget(id));
	}
	
	/**
	 * 告知用户已经满员,拒绝连接
	 */
	void OutOfRoom() {
		send(new NetMessage()
				.setCommand(ENetCommand.OUT_OF_ROOM)
				.setSource(Server.SERVER));
		//发送给ClientConversation后断开ServerConversation的连接
		close(this.dis, this.dos, this.socket);
	}
	
	/**
	 * 处理强制宕机
	 * 即给所有在线的客户端发出提示
	 * 并关闭连接
	 */
	void forceDown() {
		send(new NetMessage()
				.setCommand(ENetCommand.FORCEDOWN));
		
		close(this.dis, this.dos, this.socket);
	}
	
	
	
	
	/**
	 * 这里的toOne是从ServerConversation发给ClientConversation的内部工具
	 */
	void toOne(String sourceId, String message) {
		send(new NetMessage()
				.setCommand(ENetCommand.TO_ONE)
				.setSource(sourceId)
				.setTarget(id)
				.setMessage(message));
	}
	
	/**
	 * 这里的toOther是由ServerConversation发给ClientConversation的内部工具
	 * 即从点到点的发送,但这个方法将被Server内的toOther调用,完成群发功能
	 */
	void toOther(String sourceId, String message) {
		send(new NetMessage()
				.setCommand(ENetCommand.TO_OTHER)
				.setSource(sourceId)
				.setTarget(id)
				.setMessage(message));
	}

	
	/**
	 * 处理客户端异常掉线 
	 */
	@Override
	public void peerAbnormalDrop() {
		this.server.clientOffLine(id);
		this.server.speakOut("客户端"+ ip + "," + id +"异常掉线");
	}
	
	
	/**
	 * 服务器主动断开与某个客户端的连接
	 */
	void killClient(String reason) {
		send(new NetMessage()
				.setCommand(ENetCommand.KILL_CLIENT)
				.setMessage(reason));
		this.server.clientOffLine(id);
		close(this.dis, this.dos, this.socket);
		this.server.speakOut("客户端" + id + ip + reason + "被服务器下线");
	}
	
	
	
	/**
	 * 处理来自客户端的网络信息
	 * 这些信息都是由ClientConversation传来的
	 */
	@Override
	public void dealNetMessage(NetMessage netMessage) {
		ENetCommand command = netMessage.getCommand();
		//对于从客户端接收到的信息,调用应用层的API来处理
		//所以要将Server当作ServerConversation的一个成员
		//以便调用其中的方法
		switch(command) {
		//服务器会话层接收到客户端会话层传来的To_One命令,则调用Server中的toOne方法处理该消息
		//应用层找到ToOne的目标target,即目标的ServerConversation
		//再调用目标ServerConversation内的toOne方法,将信息传递给目标ClientConversation
		case TO_ONE:
			this.server.toOne(netMessage.getSource(), netMessage.getTarget(), netMessage.getMessage());
			break;
		//服务器会话层收到来自客户端会话层的OffLine命令,调用Server中的clientOffLine()方法
		//直接从会话池移除这个客户端的ServerConversation
		//并关闭本次连接
		case OFFLINE:
			this.server.clientOffLine(id);
			this.server.speakOut("客户端" + ip + "," + id +"下线");
			close(this.dis, this.dos, this.socket);
			break;
		//群发一个消息,需要serverConversation把消息上交给掌握了
		//会话池的Server来处理,id就是发送者,这里就是发送源
		case TO_OTHER:
			this.server.toOther(id, netMessage.getMessage());
			break;
		case MESSAGE_TO_SERVER:
		//这个信息是客户端发给服务器的,应该像服务器接收消息抛给APP层一样
		//这里也需要交给服务器APP层来完成,创建接口即可
			this.server.messageFromClient(id, netMessage.getMessage());
			break;
		default:
			break;
		}

	}

}

3 广播与侦听 ISpeaker接口,IListener接口

广播接口:
在这里插入图片描述

听众接口:
在这里插入图片描述

P5 客户端会话层

1 客户端会话层 ClientConversation类

package com.mec.csframework.core;

import java.io.IOException;
import java.net.Socket;

public class ClientConversation extends Communication {
	
	//将Client作为成员,以便调用其中处理特殊情况的方法
	private Client client;
	
	//这里的ID应该是当客户端发起连接时
	//服务器侦听到并确认本次连接合法时,
	//先为该客户端的ServerConversation生成一个ID
	//而ClientConversation的ID应该与它对应的ServerConversation的ID是一样的
	private String id;
	
	String getId() {
		return this.id;
	}
	
	
	public ClientConversation(Socket socket, Client client) throws IOException {
		super(socket);
		this.client = client;
	}
	
	/**
	 * 服务器异常掉线处理
	 */
	@Override
	public void peerAbnormalDrop() {
		this.client.getClientAction().serverAbnormalDrop();
	}
	
	/**
	 * 这里的toOne是将信息从客户端发给服务器的工具
	 */
	void toOne(String targetId, String message) {
		send(new NetMessage()
				.setCommand(ENetCommand.TO_ONE)
				.setSource(id)
				.setTarget(targetId)
				.setMessage(message));
	}
	
	
	/**
	 * 这里的toOther是APP要调用的API的真正实现工具
	 */
	void toOther(String message) {
		send(new NetMessage()
				.setCommand(ENetCommand.TO_OTHER)
				.setSource(id)
				.setTarget(Server.SERVER)
				.setMessage(message));
	}
	
	/**
	 * 实现由客户端向服务器发送信息的方法
	 */
	void messageToServer(String message) {
		send(new NetMessage()
				.setCommand(ENetCommand.MESSAGE_TO_SERVER)
				.setSource(id)
				.setTarget(Server.SERVER)
				.setMessage(message));
	}
	
	
	
	/**
	 * 这里ClientConversation的offLine下线方法被Client调用
	 * clientConversation发送下线消息给为它服务的ServerConversation
	 * 这个ServerConversation收到OFF_LINE消息后将自己从会话池中移除
	 */
	void offLine() {
		send(new NetMessage()
				.setCommand(ENetCommand.OFFLINE));
		close(this.dis, this.dos, this.socket);
	}

	
	
	/**
	 * 处理来自服务器的网络信息
	 * 根据传来的信息,分解信息中的信令
	 * 来做出各种信息的应对
	 * 这些消息全部是ServerConversation传来的
	 */
	@Override
	public void dealNetMessage(NetMessage netMessage) {
		ENetCommand command = netMessage.getCommand();
		switch(command) {
		case OUT_OF_ROOM:
			//对于从服务器接收到的Out_Of_Room信息应该由APP层处理
			//所以要将Client当作ClientConversation的一个成员
			//以便调用其中的方法
			this.client.getClientAction().serverOutOfRoom();
			close(this.dis, this.dos, this.socket);
			break;
		case ID:
			//接收生成的ID并同步设置为自己的ID
			this.id = netMessage.getTarget();
			break;
		case TO_ONE:
			//接收到ServerConversation传来的toOne信息
			//这个信息的处理应该由APP层处理
			this.client.getClientAction().toOne(netMessage.getSource(), netMessage.getMessage());
			break;
		case TO_OTHER:
			//接收到ServerConversation传来的toOther信息
			//这个信息的处理交给APP层
			this.client.getClientAction().toOne(netMessage.getSource(), netMessage.getMessage());
			break;
		case FORCEDOWN:
			//接收到ServerConversation传来的ForceDown信息
			//这个信息交给APP层处理
			this.client.getClientAction().serverForceDown();
			close(this.dis, this.dos, this.socket);
			break;
		case KILL_CLIENT:
			//接收到ServerCoversation传来的KillClient信息
			//这个信息交给APP层处理
			this.client.getClientAction().killByServer(netMessage.getMessage());
			close(this.dis, this.dos, this.socket);
			break;
		default:
			break;	
		}

	}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值