Java 网络编程 —— 客户端协议处理框架

Java 对客户程序的通信过程进行了抽象,提供了通用的协议处理框架,该框架封装了 Socket,主要包括以下类:

  • URL 类:统一资源定位符,表示客户程序要访问的远程资源
  • URLConnection 类:表示客户程序与远程服务器的连接,客户程序可以从 URLConnection 获得数据输入流和输出流
  • URLStreamHandler 类:协议处理器,主要负责创建与协议相关的 URLConnection 对象
  • ContentHandler 类:内容处理器,负责解析服务器发送的数据,把它转换为相应的 Java 对象

以上类都位于 java.net 包,除 URL 类为具体类,其余的都是抽象类,对于一种具体的协议,需要创建相应的具体子类。Oracle 公司为协议处理框架提供了基于 HTTP 的实现,它们都位于 JDK 类库的 sun.net.www 包或者其子包

2|0URL 类的用法

下例的 HtpClient 类利用 URL 类创建了一个简单的 HTTP 客户程序,先创建了一个 URL 对象,然后通过它的 openStream() 方法获得一个输入流,接下来就从这个输入流中读取服务器发送的响应结果

 

public class HttpClient { public static void main(String args[]) throws IOException { //http是协议符号 URI url = new URL("http://www.javathinker.net/hello.htm"); //接收响应结果 InputStream in = url.openStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); bytel] buff = new byte[1024]; int len = -l; while((len = in.read(buff)) != -1) { buffer.write(buff, 0, len); } //把字节数组转换为字符串 System.out.println(new String(buffer.toByteArray())); } }

URL 类的构造方法创建 URLStreamHandler 实例的流程如下:

  1. 如果在 URL 缓存已经存在这样的 URLStreamHandler 实例,则无须再创建,否则继续执行下一步

  2. 如果程序通过 URL 类的静态 setURLStreamHandlerFactory() 方法设置了 URLStreamHandlerFactory 接口的具体实现类,那么就通过这个工厂类的 createURLStreamHandler() 方法来构造 URLStreamHandler 实例,否则继续执行下一步

  3. 根据系统属性 java.prolocol.handler.pkgs 来决定 URLStreamHandler 具体子类的名字,然后对其实例化,假定运行 HttpClient 的命令为:

     

    java -Djava.protocol.handler.pkgs=com.abc.net.www | net.javathinker.protocols HttpClient

    以上命令中的 -D 选项设定系统属性,会先查找并试图实例化 com.abc.net.www.http.Handler 类,如果失败,再试图实例化 net.javathinkerprotocols.http.Handler 类,如果以上操作都失败,那么继续执行下一步

  4. 试图实例化位于 sun.net.www.prolocol 包的 sun.netwww.protocol.协议名.Handler 类,如果失败,URL 构造方法就会抛出 MalforedURLException。在本例协议名是 http,会试图实例化 sun.net.www.protocol.http.Handler 类

URL 类具有以下方法:

  • openConnection():创建并返回一个 URLConnection 对象,这个 openConnection() 方法实际上是通过调用 URLStreamHandler 类的 openConnection() 方法,来创建 URLConnection 对象
  • openStream():返回用于读取服务器发送数据的输入流,该方法实际上通过调用 URLConnection 类的 getInputStream() 方法来获得输入流
  • getContent():返回包装了服务器发送数据的 Java 对象,该方法实际上调用 URLConnection 类的 getContent) 方法,而 URLConnection 类的 getContent() 方法又调用了 ContentHandler 类的 getContent() 方法

3|0URLConnection 类的用法

URLConnection 类表示客户程序与远程服务器的连接,URLConnection 有两个 boolean 类型的属性以及相应的 get 和 set 方法:

  • dolnput:如果取值为 true,表示允许获得输入流,读取远程服务器发送的数据该属性的默认值为 true。程序可通过 getDolnput() 和 setDolnput() 方法来读取和设置该属性
  • doOutput:如果取值为 true,表示允许获得输出流,向远程服务器发送数据该属性的默认值为 false。程序可通过 getDoOutput() 和 setDoOutput() 方法来读取和设置该属性

URLConnection 类提供了读取远程服务器的响应数据的一系列方法:

  • getHeaderField(String name):返回响应头中参数 name 指定的属性的值
  • getContentType():返回响应正文的类型,如果无法获取响应正文的类型就返回 null
  • getContentLength():返回响应正文的长度,如果无法获取响应正文的长度,就返回 -1
  • getContentEncoding():返回响应正文的编码类型,如果无法获取响应正文的编码类型,就返回 null

下例的 HtpClient 类利用 URLConnection 类来读取服务器的响应结果

 

public class HttpClient { public static void main(String args[]) throws IOException { URL url = new URL("http://www,javathinkernet/hello.htm"); URLConnection connection = url.openConnection(); //接收响应结果 System.out.printIn("正文类型:" + connection.getContentType()); System.out.printIn("正文长度:" + connection.getContentLength()); //读取响应正文 InputStream in = connection.getInputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); byte[] buff = new byte[1024]; int len = -l; while((len = in.read(buff)) != -1) { buffer.write(buff, 0, len); } //把字节数组转换为字符串 System.out.println(new String(buffer.toByteArray())); } }

4|0实现协议处理框架

本节将为用户自定义的 ECHO 协议实现处理框架,共创建了以下类:

  • EchoURLConnection 类:继承自 URLConnection 类
  • EchoURLStreamHandler 类:继承自 URLStreamHandler 类
  • EchoURLStreamHandlerFactory 类:实现 URLStreamHandlerFactory 接口
  • EchoContentHandler 类:继承自 ContentHandler 类
  • EchoContentHandlerFactory 类:实现 ContentHandlerFactory 接口

1. 创建 EchoURLConnection 类

EchoURLConnection 类封装了一个 Socket,在 connect() 方法中创建与远程服务器连接的 Socket 对象

 

public class EchoURLConnection extends URLConnection { private Socket connection = null; public final static int DEFAULT PORT = 8000; public EchoURLConnection(URL url) { super(url); } public synchronized InputStream getInputStream() throws IOException { if(!connected) connect(); return connection.getInputStream(); } public synchronized OutputStream getOutputStream() throws IOException { if(!connected) connect(); return connection.getOutputStream(); } public String getContentType() { return "text/plain"; } public synchronized void connect() throws IOException { if(!connected) { int port = url.getPort(); if(port < 0 || port > 65535) port = DEFAULT_PORT; this.connection = new Socket(url.getHost(), port); this.connected = true; } } public synchronized void disconnect() throws IOException { if(connected) { //断开连接 this.connection.close(); this.connected = false; } } }

2. 创建 EchoURLStreamHandler 及工厂类

EchoURLStreamHandler 类的 openConnection() 方法负责创建一个 EchoURLConnection 对象

 

public class EchoURLStreamHandler extends URLStreamHandler { public int getDefaultPort() { return 8000; } protected URLConnection openConnection(URL url) throws IOException { return new EchoURLConnection(url); } }

EchoURLStreamHandlerFactory 类的 createURLStreamHandle() 方法负责构造 EchoURLStreamHandler 实例

 

public class EchoURLStreamHandlerFactory implements URLStreamhandlerFactory { public URLStreamHandler createURLStreamHandler(String protocol) { if(protocol.equals("echo")) return new EchoURLStreamHandler(); else return null; } }

在客户程序中,可以通过以下方式设置 EchoURLStreamHandlerFactory

 

URL.setURLStreamHandlerFactory(new EchoURLStreamHandlerFactory()); URL url=new URL("echo://localhost:8000");

3. 创建 EchoContentHandler 类及工厂类

URLConnection 类还提供了 getContent() 方法,它有两种重载形式:

 

public Object getContent(); public Object getContent(Class[] classes);

第二个 getContent() 方法把服务器发送的数据优先转换为 classes 数组第一个元素指定的类型,如果转换失败,再尝试转换第二个元素指定的类型,以此类推

下例 HttpClient 演示处理服务器发送的数据

 

public class HttpClient { public static void main(String args[]) throws IOException { URL url = new URL("http://www,javathinker.net/hello.htm"); URlConnection connection = url.openConnection(); //接收响应结果 InputStream in = connection.getInputStream(); Class[] types = {String.class, InputStream.class}; Object obj = connection.getContent(types); if(obj instanceof String) { System.out.println(obj); } else if(obj instanceof InputStream) { in = (InputStream) obj; FileOutputStream file = new FileOutputStream("data"); byte[] buff = new byte[1024]; int len = -l; while((len = in.read(buff)) != -1) { file.write(buff, 0 ,len); } System.out.println("正文保存完毕"); } else { System.out.println("未知的响应正文类型"); } } }

EchoContentHandler 类负责处理 EchoServer 服务器发送的数据

 

public class EchoContentHandler extends ContentHandler { /** 读取服务器发送的一行数据,把它转换为字符串对象 */ public Object getContent(URLConnection connection) throws IOException { InputStream in = connection.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(in)); return br.readLine(); } public Object getContent(URLConnection connection, Class[] classes) throws IOException { InputStream in = connection.getInputStream(); for(int i = 0; i < classes.length; i++) { if(classes[i] == InputStream.class) { return in; } else if(classes[i] == String.class) { return getContent(connection); } } return null; } }

第二个 getContent() 方法依次遍历 classes 参数中的元素,判断元素是否为 InputSuream 类型或 String 类型,如果是,就返回相应类型的对象,它包含了服务器发送的数据。如果 classes 参数中的元素都不是 InputStream 类型或 String 类型,就返回 null

EchoContentHandlerFactory 类的 createContentHandler() 方法负责创建一个EchoContentHandler 对象

 

public class EchoContentHandlerFactory implements ContentHandlerFactory { public ContentHandler createContentHandler(String mimetype) { if(mimetype.equals("text/plain")) { return new EchoContentHandler(); } else { return null; } } }

在客户程序中,可以通过以下方式设置 EchoContentHandlerFactory

 

URLConnection.setContentHandlerFactory(new EchoContentHandlerFactory()); URL url = new URL("echo://localhost:8000"); EchoURLConnection connection = (EchoURLConnection)url.openConnection(); ... //读取服务器返回的数据,它被包装为一个字符串对象 String echoMsg = (String)connection.getContent();

4. 在 EchoClient 类运用 ECHO 协议处理框架

 

public class EchoClient { public static void main(String args[]) throws IOException { //设置URLStreamHandlerFactory URL.setURLStreamHandlerFactory(new EchoURLStreamHandlerFactory()); //设置ContentHandlerFactory URLConnection.setContentHandlerFactory(new EchoContentHandlerFactory()); URL url = new URL("echo://localhost:8000"); EchoURLConnection connection = (EchoURlConnection) url.openConnection(); //允许获得输出流 connection.setDoOutput(true); //获得输出流 PrintWriter pw = new PrintWriter(connection.getOutputStream(), true); while(true) { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String msg = br.readLine(); //向服务器发送消息 pw.println(msg); //读取服务器返回的消息 String echoMsg = (String) connection.getContent(); System.out.println(echoMsg); if(echoMsg.equals("echo:bye")) { //断开连接 connection.disconnect(); break; } } } }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野生的狒狒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值