该类并没有做过多处理,只是简单地调用了Exception父类的构造方法。代码如下:
import java.io.*;
/**
*自定义异常类
*/
public class TunnelException extends Exception{
public TunnelException(String message){
super(message);
}
}
第六步:编写商业对象代理类BusObjectProxy.java,实际也是网络数据流的解调制类(客户端)。
该类的构造函数就是初始化:包括接收客户端的用户名、密码登录信息,然后与服务器建立连接,并向服务器发送9999报头和用户名、密码,请求服务器通过验证。其他的方法就是实现接口BusInterface中定义的所有业务逻辑。当然这个实现只是象征性的。实现的代码如下:
import java.io.*;
import java.util.Vector;
/**
*
*服务器主类的客户端代理
*/
public class BusObjectProxy extends TunnelClient implements BusInterface{
/**
*调用TunnelClient的初始化函数,建立连接,并向服务器发送9999报头
*提供客户端用户名和密码进行登录请求。
*/
public BusObjectProxy(String name,String pass)
throws TunnelException,IOException{
setUserName(name);
setUserPass(pass);
initialize();
}
/**
*实现接口中定义的方法
*代理负责将入口参数打包发送到服务器
*并返回从服务器获得的结果
*方法1-- 功能:获取指定目录的所有文件名和目录名
* 编号:1
* 入口参数:路径名,例如"d:\\s"
* 返回值:对象FileAndDirectory
*/
public FileAndDirectory getAllFileName(String path){
FileAndDirectory fd=null;
try{
writeHeader(1);
//向字节流中写入入口参数
writeObject(new String(path));
//读报头
readHeader();
//从输入流中获取执行的结果
fd=(FileAndDirectory)readObject();
}
catch(Exception ex){
ex.printStackTrace();
fd=null;
}
return fd;
}
}
第七步:编写服务器端负责调制类的网络层封装部分TunnelServer.java(服务器)。
负责服务器端口监听的创建,信息在网络上的分发、接收、服务端界面创建、客户连接SESSION管理等,这段代码比较复杂,使用的Java程序设计技巧也多,比如抽象类定义、内隐类定义等。实现的代码如下:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.*;
import java.io.*;
import java.util.*;
/**
*服务器端负责处理网络数据报的调制类
*
*参考类: Talk ClientSession
*/
public abstract class TunnelServer extends JFrame implements Runnable{
private static ServerSocket serverSocket; //服务器端监听socket
private static Socket clientSocket; //与客户连接的句柄
private static ClientSession cs; //客户的SESSION
private static Hashtable ht=new Hashtable(); //存放所有客户连接(“连接顺号-客户SESSION”)
private DataInputStream in;
private DataOutputStream out;
private int port=7484;
int count=0; //连接总数
public static JTextArea ta=new JTextArea(12,34);
JButton btn=new JButton("启动");
public void startServer(int port) throws Exception{
try{
//建立服务器监听Socket
serverSocket=new ServerSocket(port);
ta.append("Server Listener Created at port "+port+"......\n");
}catch(IOException ex){}
//启动客户连接侦听器
Thread thread=new Thread(this);
thread.start();
}
public void run(){
try{
//每隔500毫秒检测一次
Thread.sleep(500);
//实现客户端循环监听
while(true){
//获取到一个客户连接
clientSocket=serverSocket.accept();
//指定数据流封装方式
in=new DataInputStream(clientSocket.getInputStream());
out=new DataOutputStream(clientSocket.getOutputStream());
//将该客户连接保存到哈希表中
// 主要有:商业业务逻辑对象,与客户相关的Socket,编号
synchronized (ht){
count++;
Object serverBusObject=getRealObject();
cs=new ClientSession(count,clientSocket,serverBusObject);
ht.put(Integer.toString(count),cs);
}
ta.append("Connection from "+
clientSocket.getInetAddress().getHostAddress()+"\n");
String lineSep=System.getProperty("line.separator"); //取得回车换行符
InetAddress addr=serverSocket.getInetAddress().getLocalHost();
String outData="编号为"+count+
"的访问者:欢迎您登陆EAC服务器。 服务器地址:"+
addr+
" 当前服务的端口号为:"+serverSocket.getLocalPort();
//根据收发协议的规定,首先进行身份验证
//初始化的第一个报头一定是9999,所以没有保存该报头的信息。
int flag=readHeader();
ta.append("::> request header:"+flag+"\n");
//从客户端获取用户名和密码
String userName=(String)readObject();
String userPass=(String)readObject();
ta.append("::> received from client"+count+" name="+userName);
ta.append(" and password="+userPass+"\n");
//系统初始化,接收到9999报头,模拟系统身份验证
if(correctLogin(userName,userPass)){
ta.append("::> access is legal,client "+count+
",you are welcome!"+"\n");
writeHeader(9999);
writeObject(outData);
//管理每一个客户的完整交互过程,
//注意:客户的每个完整交互过程是在一个新线程中完成的。
new Talk(count,ht,ta){
public void executeMethod(Object busObject,
int flag,DataInputStream in,DataOutputStream out)
throws Exception{
executeMethods(busObject,flag,in,out);
}
}.start();
}
else{
ta.append("::> guest logging in declined\n");
writeHeader(-1);
out.writeUTF("你无权登录网络,请联系系统管理员");
}
}
}catch(Exception ex){}
}
/**
* 将对象写入字节流,先写入一个整型数,表示对象的长度,然后再写对象本身
* 调用该函数前应该先创建DataOutputStream对象out
*/
public void writeObject(Object obj) throws Exception{
try{
//首先将对象转为字节流
ByteArrayOutputStream buf=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(buf);
oos.writeObject(obj);
byte[] bytes=buf.toByteArray();
int l=bytes.length;
//写入对象长度
out.writeInt(l);
//写入对象字节流
out.write(buf.toByteArray());
out.flush();
}
catch(Exception ex){
ex.printStackTrace();
}
}
/**
* 从输入流中读取一个对象,先读出该对象占用字节数,然后读对象本身
* 调用该函数前应该存在DataInputStream对象in
*/
public Object readObject() throws Exception{
try{
//读入对象的长度
int l=in.readInt();
//构造对象字节流数组
byte[] bytes=new byte[l];
in.readFully(bytes);
//返回对象本身
ByteArrayInputStream buf=new ByteArrayInputStream(bytes);
ObjectInputStream ois=new ObjectInputStream(buf);
return ois.readObject();
}
catch(Exception ex){
ex.printStackTrace();
return null;
}
}
//向输出流写入报头
public void writeHeader(int flag) throws TunnelException{
try{
out.writeInt(flag);
out.flush();
}catch(IOException ex){
ex.printStackTrace();
throw new TunnelException(ex.getMessage());
}
}
//读取报头
public int readHeader() throws TunnelException{
int f=-1;
try{
//获取从服务器返回的信息
//先读报头信息
f=in.readInt();
}catch(IOException ex){
f=-1;
ex.printStackTrace();
throw new TunnelException(ex.getMessage());
}
return f;
}
//模拟系统登录过程,在这里可以使用数据库
private boolean correctLogin(String name,String pass){
if(name.equals("qixiaorui") && pass.equals("20000203")){
return true;
}
else{
return false;
}
}
public int getPort(){
return port;
}
public void setPort(int port){
this.port=port;
}
/**
*获取商业对象实例
*/
public abstract Object getRealObject() throws TunnelException;
/**
* 执行flag指定的远程方法,详细内容在收发协议里有规定
*/
public abstract void executeMethods(Object busObject,
int flag,DataInputStream in,DataOutputStream out)
throws Exception;
}
/**
*用户交互监听处理类
*负责处理用户的完整交互过程
*/
abstract class Talk extends Thread implements Runnable{
private int count;
private Hashtable ht;
private JTextArea ta;
public Talk(int count,Hashtable ht,JTextArea ta){
this.count=count;
this.ht=ht;
this.ta=ta;
}
public void run(){
try{
while(true){
sleep(1000);
String response="";
Socket clientSock=
((ClientSession)ht.get(Integer.toString(count))).getClientSock()
Object serverBusObject=
((ClientSession)ht.get(Integer.toString(count))).getBusObject();
DataInputStream in=
new DataInputStream(clientSock.getInputStream());
DataOutputStream out=
new DataOutputStream(clientSock.getOutputStream());
//读取报头
int flag=in.readInt();
ta.append("client "+count+
":> execute remote method header="+flag+"\n");
//执行报头协议里定义的本地方法
executeMethod(serverBusObject,flag,in,out);
}
}catch(Exception ex){}
}
public abstract void executeMethod(Object busObject,int flag,
DataInputStream in,DataOutputStream out) throws Exception;
}
/**
* 用户SESSION类,记录用户的交互信息
* 包含:服务器为其分配的编号、该客户与服务器连接的句柄
* 和为该客户连接的商业对象
*/
|