JAVA SE 实战篇 C3 简单CSFramework(上) CS框架原理及通信层的搭建

P1 CSFramework简介

1 CSFramework功能概述

有了网络编程基础部分的内容,接下来要考虑到“一对多”的连接,即一个服务器对应多个客户端,CSFramework不是一个APP,它是为开发CS模式的APP软件所提供的一整套工具软件

这个工具要实现CS模式下APP要解决的有关网络通信,连接,信息传输,上下线,异常处理等普遍问题,使得基于CSFramework基础上编写的APP,无论是聊天室还是棋牌室,都不用关心底层的问题,可以将主要精力放在APP业务逻辑上

在这里插入图片描述

2 CSFramework的分层

对于一个基本功能完备的CS框架,一定要结构分明,条理清晰,对功能进行严格的分层,像MVC开发模式一样,这里做的简单CS框架分为四层:

通信层:负责发送信息和侦听信息(Communication)
会话层:负责完成通信协议(ServerConversation,ClientConversation)
API层:提供各种实用方法供APP调用(Server,Client)
APP层:调用API层的方法完成需求

3 CSFramework的工作原理

(1) 客户端如何连接到服务器

1,Server有着一个线程体用来持续不断地侦听是否有连接请求
2,某Client发起连接,先产生一个该Client的ClientConversation,ClientConversation继承于Communication,拥有发送信息和侦听对端信息的能力
3,Server的线程体侦听到了连接请求,为该Client产生一个ServerConversation,ServerConversation继承于Communication,拥有发送信息和侦听对端信息的能力

ServerConversation的对端就是ClientConversation,反之ClientConversation的对端就是ServerConversation

可以想象你是一个某商家管理客服的组长,每当有一个客户打电话过来,就指定一个工作人员来接听他的电话,你就是Server,这个工作人员就是ServerConversation,专门为客户Client服务,通信信道就是Communication

(2) 多个客户端连接到服务器

一个服务器连接多个客户端,每有一个客户端连接到服务器,在服务器一侧生成一个专门为该客户端服务的ServerConversation,在客户端一侧生成一个与服务器交流的ClientConversation:
在这里插入图片描述

(3) 一条私聊消息如何发送出去

对于CS框架,如果未来要作为聊天室的基础,如何将一条消息私聊出去,即:

扫描二维码关注公众号,回复: 12642687 查看本文章
客户端A 私聊 客户端B 信息message
假设API层私聊方法是toOne(),在会话层实现了toOne()方法

信息在框架中流动方式大致如下:

在这里插入图片描述

P2 网络中消息的存在形式

在开始实现底层前,先考虑一个最基本的问题,网络中信息的存在形式该是什么样子,怎么能让服务器正确分辨并处理一条信息

1 信息 = “信令” + “来源” + “目标” + “消息”

假如未来采用CSFramework制作一个聊天室,那么如果信息采用纯字符串在网络中传输的话,试想出现下面的问题:某客户端向服务器发送一条信息“我要下线了”,那么此时服务器应该怎么处理该信息,是将该用户下线,还是将这句话发给某一个客户端,还是发给所有客户端

为了解决这个问题,让服务器能分辨清信息的内容,就需要制定统一的协议,无论是服务器还是客户端所发出的信息,都必须遵守协议

这里采用简陋的“信令” + “来源” + “目标” + “消息”来作为通信中的信息,所谓信令,即已经规定好的一系列字符串,如上线,下线,私聊,群发等,来源即发送者,目标即接收者,消息即具体发送的内容,对于信息如:

私聊:客户端1:客户端2:我要下线了

这样处理信息清楚明了,就不会出现服务器不能辨别信息并正确处理的问题

2 信令 ENetCommand枚举

对于信令,这里采用枚举的方式来完成,即一系列有实际意义的字符串:

在这里插入图片描述

3 有效的网络信息 NetMessage类

有了信令,那么就可以规定传输的信息NetMessage,NetMessage类需要四个成员及其对应的set,get方法

command
source
target
message

在私聊过程中,对于发送者而言,对于一个私聊方法toOne(…),由于已知是私聊,那么信令确定,来源即该客户端也确定,那么toOne方法的参数只需要target和message,先创建一个NetMessage类,通过set方法更改其中的command,source,target,message就可以产生一个清晰的NetMessage再通过通信层发送给对端即可

package com.mec.csframework.core;

public class NetMessage {
    
    
	
	private ENetCommand command;
	private String source;
	private String target;
	private String message;
	
	
	/**
	 * NetMessage类中的所有方法都设置为包权限
	 * 因为NetMessage是框架中的一环,不想提供给APP层直接使用
	 */
	NetMessage() {
    
    
	}
	
	NetMessage(String netMessage) {
    
    
		int index = netMessage.indexOf(":");
		String strCommand = netMessage.substring(0, index);
		this.command = ENetCommand.valueOf(strCommand);
		netMessage = netMessage.substring(index + 1);
		
		index = netMessage.indexOf(":");
		this.source = netMessage.substring(0, index);
		netMessage = netMessage.substring(index + 1);
		
		index = netMessage.indexOf(":");
		this.target = netMessage.substring(0, index);
		netMessage = netMessage.substring(index + 1);
		
		this.message = netMessage;
	}

	
	/**
	 * 对于这四个成员的set方法全部返回NetMessage类对象
	 * 以便链式调用set方法,传递一个完整的NetMessage
	 */
	ENetCommand getCommand() {
    
    
		return command;
	}

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

	String getSource() {
    
    
		return source;
	}

	NetMessage setSource(String source) {
    
    
		this.source = source;
		return this;
	}

	String getTarget() {
    
    
		return target;
	}

	NetMessage setTarget(String target) {
    
    
		this.target = target;
		return this;
	}

	String getMessage() {
    
    
		return message;
	}

	NetMessage setMessage(String message) {
    
    
		this.message = message;
		return this;
	}

	@Override
	public String toString() {
    
    
		StringBuffer str = new StringBuffer();
		
		str.append(this.command).append(":")
			.append(this.source).append(":")
			.append(this.target).append(":")
			.append(this.message);
		
		return str.toString();	
	}	
	
}

P3 通信层的构建

1 需要通信层的原因

结合网络编程基础,可以发现,一个功能完备的聊天室起码要实现多轮的信息交互,而在one2one模式中的Server和Client,都基于网络通信,且Server的输入通信信道和客户端的输入通信信道,都必须要随时接收到对端发送的消息这个持续不断的侦听过程,必须不间断,直到通信结束

且对于dis.readUTF()方法的执行本身是阻塞的,像Scanner一样,如果采用顺序的编程方法,当readUTF()方法没有侦听到消息时,那么其后的代码永远无法执行

对于上述的两个问题,都需要用线程解决,因为服务器每和一个客户端连接后,之后都要进行信息的发送或接收,那么就可以将通信最基础的部分组合在一起组成Communication类

2 通信层的实现

(1) INetConfig接口

在网络编程中,需要频繁使用服务器的IP地址和双方确定好的端口号,为了不在每个需要的地方都写一遍,实现代码复用,这里创建一个简单的接口,包含连接的基本信息,并提供一个关闭连接的方法

在这里插入图片描述

(2) Communication抽象类

Communication类作为通信层,它需要完成至少建立连接,接收对端的信息,发送信息等功能

Communication类只负责建立连接,侦听信息,发送信息这些功能,对于将接收到的信息如何处理这种问题,应该留给更高层去完成,所以Communication类还必须作为抽象类,提供几个抽象方法,如处理信息,处理异常情况:

package com.mec.csframework.core;

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

public abstract class Communication implements Runnable, INetConfig{
    
    
	
	protected Socket socket;
	protected DataInputStream dis;
	protected DataOutputStream dos;
	
	//goon用来控制侦听对端线程的运行
	private volatile boolean goon;
	
	//根据一个连接socket,得到输入,输出信道,并启动侦听对端线程
	protected Communication(Socket socket) throws IOException {
    
    
		this.socket = socket;
		this.dis = new DataInputStream(socket.getInputStream());
		this.dos = new DataOutputStream(socket.getOutputStream());
		
		this.goon = true;
		new Thread(this).start();
	}
	
	//发送信息到对端,信息采用NetMessage类
	protected void send(NetMessage netMessage) {
    
    
		
		try {
    
    
			//将NetMessage转换成 信令:来源:目标:消息 发送到对端
			this.dos.writeUTF(netMessage.toString());
		} catch (IOException e) {
    
    
			e.printStackTrace();
		}		
	}
	
	/**
	 * 留给上层完成的抽象方法
	 */
	//对端异常掉线
	public abstract void peerAbnormalDrop();
	//具体处理消息
	public abstract void dealNetMessage(NetMessage netMessage);
	
	
	
	//侦听对端线程,持续不断的侦听对端发来的消息
	@Override
	public void run() {
    
    
		String message = null;
		
		while(this.goon) {
    
    
			try {
    
    
				message = this.dis.readUTF();
				dealNetMessage(new NetMessage(message));
			} catch (IOException e) {
    
    
				if(this.goon == true) {
    
    
					this.goon = false;
					peerAbnormalDrop();
				}
			}
		}
		//goon=false后,断开连接
		close(this.dis, this.dos, this.socket);
	}
	

}

猜你喜欢

转载自blog.csdn.net/weixin_43541094/article/details/110877951
今日推荐