关系图
客户端逻辑
【发送】逻辑管理器 (处理逻辑发送指令)→指令解析管理器(根据协议xml解析指令成二进制数据)→把二进制数据传给服务器
【接收】服务器发送二进制数据到客户端→指令解析管理器(根据协议xml解析二进制数据)→根据解析数据映射到逻辑类来处理
第一步:跟服务器端约定好协议规则,如100指令对应什么逻辑,101指令对应什么逻辑,就需要1个逻辑协议约定xml,如下:
<?xml version="1.0" encoding="UTF-8"?>
<!-- 参数类型0:数组,1:整型,2:字符串 -->
<!-- ---------------连接区域-------0到20区域段-------------------------------->
<!-- 描述:服务器向客户端发送第二次握手 -->
<cmd>
2 <!-- 指令名-->
<param>
1 <!-- 服务器id-->
</param>
</cmd>
<!-- 描述:客户端向服务器申请第三次握手 -->
<cmd>
3 <!-- 指令名-->
</cmd>
<!---------------登陆区域-------20到100区域段------------------------------>
<!-- 描述:客户端用户申请登陆 -->
<cmd>
20 <!-- 指令名-->
<param>
2,2 <!-- 用户名,密码-->
</param>
</cmd>
<!-- 描述:返回用户登陆结果 -->
<cmd>
21 <!-- 指令名-->
<param>
1,1,2 <!-- 用户id,是否登陆成功,失败原因-->
</param>
</cmd>
指令名和指令名对应的返回参数类型
第二步:客户端获取到指令后,通过指令要找到对应的逻辑处理类,就需要1个映射文件(xml),如下
cmdLib=new HashMap<Integer, String>();
//---------------连接区域-------0到20区域段------------------------------
//---------------连接区域-------0到20区域段------------------------------
//---------------连接区域-------0到20区域段------------------------------
//客户端接收请求
cmdLib.put(2, "net.socket.client.cmd.ClientCommandManager@receiveShakeHand2"); //服务器向客户端发送第二次握手
//客户端发送
cmdLib.put(3, "net.socket.server.cmd.ServerCommandManager@receiveShakeHand3"); //客户端向服务器申请第三次握手
//---------------登陆区域-------20到100区域段------------------------------
//---------------登陆区域-------20到100区域段------------------------------
//---------------登陆区域-------20到100区域段------------------------------
//客户端发送
cmdLib.put(20, "net.socket.server.cmd.ServerCommandManager@receiveUserLogin"); //申请用户登陆
//客户端接收请求
cmdLib.put(21, "net.socket.client.cmd.ClientCommandManager@receiveLoginRelt"); //返回用户登陆结果
//---------------好友区域-------100到200区域段------------------------------
//---------------好友区域-------100到200区域段------------------------------
//---------------好友区域-------100到200区域段------------------------------
//客户端发送
cmdLib.put(101, "net.socket.server.cmd.ServerCommandManager@receiveApplyFriendsList"); //申请好友列表
//客户端接收请求
cmdLib.put(102, "net.socket.client.cmd.ClientCommandManager@receiveFriendsList"); //接收到好友列表
cmdLib.put(103, "net.socket.client.cmd.ClientCommandManager@receiveFriendLogin"); //接收到好友登陆
cmdLib.put(104, "net.socket.client.cmd.ClientCommandManager@receiveFriendLoginOut"); //接收到好友下线
//---------------设备区域--------200到300区域段-----------------------------
//---------------设备区域--------200到300区域段-----------------------------
cmdLib.put(201, "web.dev.cmd.DevCommandManager@applyDevUpdate"); //申请设备升级
cmdLib.put(202, "web.dev.cmd.DevCommandManager@receiveDevUpdate"); //获取到有设备升级
cmdLib.put(203, "web.dev.cmd.DevCommandManager@receiveDevUpdateResult"); //返回设备升级结果
cmdLib.put(204, "web.dev.cmd.DevCommandManager@applyDevList"); //申请设备列表
cmdLib.put(205, "web.dev.cmd.DevCommandManager@receiveDevList"); //获取设备列表
如上201就是指令名,web.dev.cmd.DevCommandManager@applyDevUpdate就是处理逻辑
第三步,逻辑类调用逻辑方法,发送指令,通过协议规则解析成二进制数据后到服务器
逻辑类:
/**
* 申请登陆
* @param name 用户名
* @param passward 密码
*/
public void applyLogin(String name,String passward){
int cmdId=20;
CommandParseManager.writeParseCmd(new Object[]{cmdId,name,passward});
}
指令解析类:
/**
* 发送指令
* @param cmdId 协议id
* @param objects 参数数组
* @param socket 客户端socket
*/
public static void writeParseCmd(Object[] objects) {
DataOutputStream data_out=SocketClient.socket.getData_out();
try {
//获取指令对应协议的参数串
Integer cmdId=Integer.parseInt(objects[0].toString());
String paramTypes=CmdConfig.getCmdParamClassName(cmdId);
String[] paramList=paramTypes.split(",");
int len=objects.length;
for(int i=0;i<len;i++)
{
if("1".equals(paramList[i]) )
{
data_out.writeInt(Integer.parseInt(objects[i].toString()));
}else if("2".equals(paramList[i]) )
{
data_out.writeUTF(objects[i].toString());
}else if("0".equals(paramList[i]))
{
Object[][] arrayParam=(Object[][])objects[i];
++i;
//数组总长度
data_out.writeInt(arrayParam.length);
++i;
//数组对象长度
data_out.writeInt(arrayParam[0].length);
//把数组对象的参数类型保存到1个数组
String[] arrayParamType=new String[arrayParam[0].length];
for(int n=0;n<arrayParam[0].length;n++)
{
++i;
arrayParamType[n]=paramList[i];
}
for(int x=0;x<arrayParam.length;x++)
{
for(int y=0;y<arrayParam[x].length;y++)
{
if("1".equals(arrayParamType[y]))
{
data_out.writeInt(Integer.parseInt(arrayParam[x][y].toString()));
//System.out.println(Integer.parseInt(objects[i].toString()));
}else if("2".equals(arrayParamType[y]))
{
data_out.writeUTF(arrayParam[x][y].toString());
}
}
}
}
}
SocketClient.socket.sendMessage();//发送数据到服务器
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
发送数据到服务器
/**
* 发送二进制消息
*/
public void sendMessage() {
try {
data_out.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
第四步,客户端获取到指令后,通过调用指令对应方法和传入指令对应方法参数,来做客户端的逻辑处理
接收到服务器传来二进制数据
// 创建好服务器连接后,与服务器端同步服务的线程
class ClientThread extends Thread {
// 接收消息线程的构造方法
public ClientThread() {
}
public void run() {
while (true) {
try {
CommandParseManager.readParseCmd(socket);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
解析二进制数据, 调用指令对应方法和传入指令对应方法参数,来做客户端的逻辑处理
/**
* 读取指令
* @param objects
* @param socket
* @throws IOException
*/
public static void readParseCmd(ClientSocket socket) throws IOException {
DataInputStream data_in=socket.getData_in();
//读取服务器跟客户端约定的密钥(略)
//获取协议指令
int cmdId=data_in.readInt();
String classStr=CmdConfig.getCmdClassStr(cmdId);
String[] classArray=classStr.split("@");
String className=classArray[0]; //获取逻辑类名
String classFun=classArray[1]; //获取逻辑方法名
//获取指令对应协议的参数串
String paramTypes=CmdConfig.getCmdParamClassName(cmdId);
String[] paramList=paramTypes.split(",");
int len=paramList.length;
//逻辑类调用的参数
ArrayList<Object> params=new ArrayList<Object>();
for(int i=1;i<len;i++)
{
if("1".equals(paramList[i]))
{
params.add(data_in.readInt());
}else if("2".equals(paramList[i]))
{
params.add(data_in.readUTF());
}else if("0".equals(paramList[i]))//如果是数组
{
++i;
//数组总长度
int arrayParamLen1=data_in.readInt();
++i;
//数组对象长度
int arrayParamLen2=data_in.readInt();
//把数组对象的参数类型保存到1个数组
String[] arrayParamType=new String[arrayParamLen2];
for(int n=0;n<arrayParamLen2;n++)
{
++i;
arrayParamType[n]=paramList[i];
}
//遍历数组
Object[][] arrayParam=new Object[arrayParamLen1][arrayParamLen2];
for(int x=0;x<arrayParam.length;x++)
{
for(int y=0;y<arrayParam[x].length;y++)
{
if("1".equals(arrayParamType[y]))
{
arrayParam[x][y]=data_in.readInt();
}else if("2".equals(arrayParamType[y]))
{
arrayParam[x][y]=data_in.readUTF();
}
}
}
params.add(arrayParam);
}
}
//params.add(socket);
//反射调用方法
ReflectUtil.invokeMethodWithObjHasParame(className,ReflectUtil.getSingleObjByClassName(className),classFun,params.toArray());
}
客户端逻辑
/**
* 服务器返回登陆结果
* @param userId 用户id
* @param isSuc 是否登陆成功
* @param failmsg 失败原因
*/
public void receiveLoginRelt(Integer userId,Integer isSuc,String failmsg){
if(isSuc==1)
{
System.out.println("登陆成功");
applyFriendsList();
}
}
服务器端逻辑类似
逻辑跟客户端类似,这里直说哈扩散思路,
用户请求公会人员时,会从公会管理器的缓存内取,没有的话就会查询数据库然后缓存起,下次取得时候就不用查询数据库了,同时公会的删人,加人也要一起维护这个缓存