Java 网络编程 —— 客户端协议处理框架
概述
Java 对客户程序的通信过程进行了抽象,提供了通用的协议处理框架,该框架封装了 Socket,主要包括以下类:
- URL 类:统一资源定位符,表示客户程序要访问的远程资源
- URLConnection 类:表示客户程序与远程服务器的连接,客户程序可以从 URLConnection 获得数据输入流和输出流
- URLStreamHandler 类:协议处理器,主要负责创建与协议相关的 URLConnection 对象
- ContentHandler 类:内容处理器,负责解析服务器发送的数据,把它转换为相应的 Java 对象
以上类都位于 java.net 包,除 URL 类为具体类,其余的都是抽象类,对于一种具体的协议,需要创建相应的具体子类。Oracle 公司为协议处理框架提供了基于 HTTP 的实现,它们都位于 JDK 类库的 sun.net.www 包或者其子包
URL 类的用法
下例的 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 实例的流程如下:
如果在 URL 缓存已经存在这样的
URLStreamHandler实例,则无须再创建,否则继续执行下一步如果程序通过 URL 类的静态
setURLStreamHandlerFactory()方法设置了URLStreamHandlerFactory接口的具体实现类,那么就通过这个工厂类的createURLStreamHandler()方法来构造URLStreamHandler实例,否则继续执行下一步根据系统属性
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类,如果以上操作都失败,那么继续执行下一步试图实例化位于
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()方法
URLConnection 类的用法
URLConnection 类表示客户程序与远程服务器的连接,URLConnection 有两个 boolean 类型的属性以及相应的 get 和 set 方法:
- dolnput:如果取值为 true,表示允许获得输入流,读取远程服务器发送的数据该属性的默认值为 true。程序可通过 getDolnput() 和 setDolnput() 方法来读取和设置该属性
- doOutput:如果取值为 true,表示允许获得输出流,向远程服务器发送数据该属性的默认值为 false。程序可通过 getDoOutput() 和 setDoOutput() 方法来读取和设置该属性
URLConnection 类提供了读取远程服务器的响应数据的一系列方法:
getHeaderField(String name):返回响应头中参数 name 指定的属性的值getContentType():返回响应正文的类型,如果无法获取响应正文的类型就返回 nullgetContentLength():返回响应正文的长度,如果无法获取响应正文的长度,就返回 -1getContentEncoding():返回响应正文的编码类型,如果无法获取响应正文的编码类型,就返回 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()));
}
}
实现协议处理框架
本节将为用户自定义的 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;
}
}
}
}
Java 网络编程 —— 客户端协议处理框架的更多相关文章
- java网络编程+通讯协议的理解
参考: http://blog.csdn.net/sunyc1990/article/details/50773014 网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很 ...
- Java网络编程客户端和服务器通信
在java网络编程中,客户端和服务器的通信例子: 先来服务器监听的代码 package com.server; import java.io.IOException; import java.io.O ...
- java网络编程客户端与服务端原理以及用URL解析HTTP协议
常见客户端与服务端 客户端: 浏览器:IE 服务端: 服务器:web服务器(Tomcat),存储服务器,数据库服务器. (注:会用到Tomact服务器,在webapps下有一个自己创建的目录myweb ...
- Java网络编程(TCP协议-服务端和客户端交互)
客户端: package WebProgramingDemo; import java.io.IOException; import java.io.InputStream; import java. ...
- java 网络编程 TCP协议 java 服务器和客户端 java socket编程
一个 HelloWord 级别的 Java Socket 通信的例子.通讯过程: 先启动 Server 端,进入一个死循环以便一直监听某端口是否有连接请求.然后运行 Client 端,客 ...
- Java网络编程-HTTP协议
HTTP协议的定义 这篇文章暂时不研究HTTP底层的TCP/IP的握手和挥手过程,只从表面的交互流程分析HTTP协议. HTTP英文全称是Hypertext Transfer Protpcol,也就是 ...
- Java网络编程(客户端和服务端原理)
运行下面的程序,浏览器端输入自己主机的IP地址+端口号(8888),会看到服务器返回的数据内容,Eclipse控制台会打印显示收到的信息, 通过我们自定义的服务器,可以看到浏览器端向服务器发送的请求信 ...
- Java网络编程(TCP协议-练习-上传文本文件)
客户端: package WebProgramingDemo; import java.io.BufferedReader; public class UploadTextClient { /** * ...
- Java网络编程(UDP协议-聊天程序)
接收端: package WebProgramingDemo; import java.net.DatagramPacket; import java.net.DatagramSocket; publ ...
- Java网络编程(UDP协议:接收端)
package WebProgramingDemo; import java.io.IOException; import java.net.DatagramPacket; import java.n ...
随机推荐
- Kafka 生产者写入数据
一.生产者发送消息的步骤
- 修改host文件
host文件路径 Windows 系统:C:\Windows\System32\drivers\etc\hosts Linux 系统:/etc/hosts Mac(苹果电脑)系统:/etc/hosts ...
- Delete ␍eslint(prettierprettier)错误
Delete ␍eslint(prettier/prettier)错误 今天在用HBuilder开发uniapp项目时,想换成vscode进行开发,但是用vscode打开之前的项目,eslint报错一 ...
- 微软博客上几篇 Semantic-kernel (SK)文章
自从最近微软开源Semantic-kernel 来帮助开发人员在其应用程序中使用AI大型语言模型(LLM)以来,Microsoft一直在忙于改进它,发布了有关如何使用它的新指南并发布了5篇文章介绍他 ...
- 六位一体Serverless化应用,帮你摆脱服务器的烦恼
随着互联网技术的飞速发展,越来越多的应用横空出世,是以不可避免带来了大量的服务器需求.大部分的开发者都选择购买或者租用服务器,然而这样也带来了诸多的烦恼. 1.硬件成本高昂 购买服务器费用昂贵,除 ...
- 电商AARRR模型分析(一)——R语言
在2010年,互联网创业者增长黑客之父肖恩·埃利斯(Sean Ellis)就创造了增长黑客(Growth hacker)这样一个概念.2015年,范冰撰写的一本新书<增长黑客>确立了Gro ...
- 搭建DHCP服务,实现自动分配地址
DHCP实现原理 DHCP定义 DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)是一个局域网的网络协议,使用UDP协议工作.它是一种流行的Clien ...
- [ElasticSearch]修改开源安全组件Search Guard-6 用户密码
ES有很多的安全组件可用,例如: X-pack,Sarch Guard.但目前开源免费的,仅Search Guard. 1 前置条件 Elastic Search 6 服务安装成功,且成功运行. ES ...
- MordernC++之 auto 和 decltype
在C++11标准中,auto作为关键字被引入,可以用来自动推导变量类型,auto可以用于定义变量,函数返回值,lambda表达式等,在定义变量时可以使用auto来代替具体类型,编译器根据变量初始化表达 ...
- Kubernetes客户端认证——基于CA证书的双向认证方式
1.Kubernetes 认证方式 Kubernetes集群的访问权限控制由API Server负责,API Server的访问权限控制由身份验证(Authentication).授权(Authori ...