客户端与服务器端通信

关系图


客户端逻辑

【发送】逻辑管理器 (处理逻辑发送指令)→指令解析管理器(根据协议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();
		}
	}


服务器端逻辑类似

逻辑跟客户端类似,这里直说哈扩散思路, 用户请求公会人员时,会从公会管理器的缓存内取,没有的话就会查询数据库然后缓存起,下次取得时候就不用查询数据库了,同时公会的删人,加人也要一起维护这个缓存

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值