客户端协议处理框架
本文为《Java网络编程精解》客户端协议处理框架这一章的一些学习笔记。
Java对客户程序的通讯过程进行了抽象,提供了通用的协议处理框架,这个框架的优点在与能够封装Socket与服务器通信的细节,在客户程序中一般只需要访问URL和URLConnection类,就能够完成与服务器的通信,框架的具体实现对客户程序是透明的。
作为协议处理框架的实现程序,通常要实现基于特定协议的URLConenction、URLStreamHandler和ContentHandler类的具体子类,此外还要创建URLStreamHandler和ContentHandler类的工厂类,他们负责创建URLStreamHandler和ContentHandler类的具体子类的实力
框架图:
实现协议处理框架
- EchoURLConnection类:继承自URLConnection类
- EchoURLStreamHandler类:继承自URLStreamHandler类
- EchoURLStreamHandlerFactory类:实现了URLStreamHandlerFactory接口
- EchoContentHandler:继承自ContentHandler类
- EchoContentHandlerFactory:实现ContentHandlerFactory接口
创建EchoURLConnection类
EchoURLConnection类实现了connect(),getContentType(),getInputStream()和getOutputStream()方法。EchoURLConnection类封装了一个Socket,在connect()方法中创建与远程服务器连接的Socket对象
package urlFramwork;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
/**
* URLConnection子类,理解为某协议与服务器之间的连接
* @author xusy
*
*/
public class EchoURLConnection extends URLConnection {
private Socket connection;
public static final int DEFAULT_PORT = 8000;
protected EchoURLConnection(URL url) {
super(url);
}
/*
* 连接服务器
*/
@Override
public void connect() throws IOException {
//TODO 创建与服务器之间的连接
if (!connected) {
int port = url.getPort();
if (port < 0 || port > 65535)
port = DEFAULT_PORT;
connection = new Socket(url.getHost(), port);
this.connected = true;
}
}
/**
* 断开服务器
*/
public void disconnect() throws IOException{
if(connected){
connection.close();
this.connected = false;
}
}
/*
* 当与服务器建立起连接后,获得InputStream
*/
public synchronized InputStream getInputStream() throws IOException {
if (!connected)
connect();
return connection.getInputStream();
}
/*
* * 当与服务器建立起连接后,获得OutputStream
*/
public synchronized OutputStream getOutputStream() throws IOException {
if (!connected)
connect();
return connection.getOutputStream();
}
public String getContentType(){
return "text/plain";
}
}
创建EchoURLStreamHandler 类和工厂
EchoURLStreamHandler 协议处理器,主要负责创建与协议相关的URLConnection对象,EchoURLStreamHandler类的openConnection()方法负责创建一个EchoURLConenction对象。URL类的openConnection()方法会调用EchoURLStreamHandler类的openConnection()方法。
package urlFramwork;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
/**
* 负责创建EchoURLConnection对象,URL类的openConnection()方法会调用EchoURLStreamHandler类的openConnection()方法。
* @author xusy
*
*/
public class EchoURLStreamHandler extends URLStreamHandler {
@Override
protected URLConnection openConnection(URL u) throws IOException {
return new EchoURLConnection(u);
}
}
EchoURLStreamHandler的工厂类
package urlFramwork;
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
/**
*负责创建 EchoURLStreamHandler的工厂类
* @author xusy
*
*/
public class EchoURLStreamHandlerFactory implements URLStreamHandlerFactory {
@Override
public URLStreamHandler createURLStreamHandler(String protocol) {
if (protocol.equals("echo"))
return new EchoURLStreamHandler();
return null;
}
}
创建EchoContentHandler类和工厂类
对于服务器发送的数据,客户程序可通过URLConnection类的getInputStream()方法获得输入流,然后读取数据,此外URLConnection类还提供了getContent()方法,
它有两种重载形式:
1)public Object getContent()
2) public Object getContent(Class[] classes)
第一个getContent()方法能把服务器发送的数据转换成一个Java对象。第二个getContent()方法的classes参数制定了转换而成的Java对象的类型,getContent()方法把服务器发送的数据优先转换成classes数组中
第一个元素制定的类型,如果转换失败,则尝试转换为classes数组中第二个元素的类型,依次类推。URLConnection类的getContent()方法会调用ContentHandler类的相应的getContent()方法。
在客户端程序中,可以通过一下方式处理服务器发送的数据
URL url = new URL("www.googel.com");
URLConnection connection = url.openConnection();
Class[] classes = {String.class,InputStream.class};
Object o = connection.getContent(classes);
if(o instanceof String){
System.out.prinltn(0)
}else if(o instanceof InputStream){
InputStream in=(InputStream)o;
//处理输入流...
}else{
System.out.println("无法识别服务器发送的数据");
}
package urlFramwork;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ContentHandler;
import java.net.URLConnection;
/**
* @author xusy
*
*/
/**
* @author xusy
*
*/
public class EchoContentHandler extends ContentHandler {
/*
* 读取服务器发送的一行数据,把它转换为字符串对象
*/
@Override
public Object getContent(URLConnection connection) throws IOException {
InputStream in = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
return reader.readLine();
}
/*
* 根据参数classes,将服务器传输来的数据转换为对象并返回
* @see java.net.ContentHandler#getContent(java.net.URLConnection, java.lang.Class[])
*/
public Object getContent(URLConnection connection, Class[] classes)
throws IOException {
InputStream inputStream = connection.getInputStream();
for (int i = 0; i < classes.length; i++) {
if (classes[i] == InputStream.class)
return inputStream;
else if (classes[i] == String.class)
return getContent(connection);
}
return null;
}
}
在客户端程序中,可通过URLConnection类的静态setContetHandlerFactory()方法来设置EchoContentHandlerFactory:
//设置ContentHandlerFactory
URLConnection.setContentHandlerFactory(new EchoContentHandlerFactory());
URL url = new URL("echo://localhost:8000");
EchoURLConnection connection = (EchoURLConenction)url.openConnection();
...
//读取服务器返回的数据,它被包装为一个字符串对象
String echoMsg = (String)connection.getContent();
package urlFramwork;
import java.net.ContentHandler;
import java.net.ContentHandlerFactory;
public class EchoContentHandlerFactory implements ContentHandlerFactory {
@Override
public ContentHandler createContentHandler(String mimetype) {
if(mimetype.equals("text/plain"))
return new EchoContentHandler();
else
return null;
}
}