文章目录
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;
}
}
}