大力:“Java网络编程需要通过套接字去进行客户程序与服务器程序的交互,这种底层的通信实现起来比较麻烦,有没有现成的API呢?”
卫琴:“JDK为基于HTTP协议的网络通信提供了协议处理框架API,Java客户程序通过它来访问HTTP服务器,会很方便。”
本文以HTTP客户程序为例,介绍URL类和URLConnection类的用法。
一、URL类的用法
以下例程的HttpClient1类利用URL类和URLConnection类创建了一个简单的HTTP客户程序。
/* HttpClient1.java (使用URL类的openStream()方法)*/
import java.net.*;
import java.io.*;
public class HttpClient1 {
public static void main(String args[])throws IOException{
//“http”是协议符号
URL url=new URL("http://www.javathinker.net/hello.htm");
//接收响应结果
InputStream in=url.openStream();
ByteArrayOutputStream buffer=new ByteArrayOutputStream();
byte[] buff=new byte[1024];
int len=-1;
while((len=in.read(buff))!=-1){
buffer.write(buff,0,len);
}
//把字节数组转换为字符串
System.out.println(new String(buffer.toByteArray()));
}
}
以上程序先创建了一个URL对象,然后通过它的openStream()方法获得一个输入流,接下来就从这个输入流中读取服务器发送的响应结果。HttpClient1最后会打印如下内容:
helloapp
hello
在通过URL类的构造方法URL(String url)创建一个URL对象时,构造方法会根据参数url中的协议符号,来创建一个与协议匹配的URLStreamHandler实例。在本例中,协议符号为“http”。URL类的构造方法创建URLStreamHandler实例的流程如下:
(1)如果在URL缓存中已经存在这样的URLStreamHandler实例,则无需再创建新的URLStreamHandler实例。否则继续执行下一步。
(2)如果程序已经通过URL类的静态setURLStreamHandlerFactory()方法设置了URLStreamHandlerFactory接口的具体实现类,那么就通过这个工厂类的createURLStreamHandler()方法来构造一个URLStreamHandler实例。否则继续执行下一步。
(3)根据系统属性java.protocol.handler.pkgs来决定URLStreamHandler具体子类的名字,然后对其实例化。假定运行HttpClient1的命令为:
java -Djava.protocol.handler.pkgs=com.abc.net.www |
net.javathinker.protocols
HttpClient1
以上命令中的“-D”选项设定系统属性,URL构造方法会先查找并试图实例化com.abc.net.www.http.Handler类,如果失败,再试图实例化net.javathinker.protocols.http.Handler类。在设置java.protocol.handler.pkgs属性时,多个包名以“|”隔开。对于一个基于FTP协议的客户程序,那么URL构造方法会先后查找并试图实例化 com.abc.net.www.ftp.Handler 类和net.javathinker.protocols.ftp.Handler类。
如果以上操作都失败,那么继续执行下一步。
(4)试图实例化位于 sun.net.www.protocol 包中的sun.net.www.protocol. 协议名.Handler类,如果失败,URL构造方法就会抛出MalformedURLException。对于一个基于HTTP协议的客户程序,那么URL构造方法会试图实例化 sun.net.www.protocol.http.Handler 类。对于一个基于FTP协议的客户程序,那么URL构造方法会试图实例化 sun.net.www.protocol.ftp.Handler 类。
从上述流程可以推断出,如果运行命令“java HttpClient1”,那么URL构造方法实际上会实例化 sun.net.www.protocol.http.Handler 类,它是URLStreamHandler类的一个具体子类。
URL类具有以下方法:
- openConnection()方法:创建并返回一个URLConnection对象,这个openConnection()方法实际上是通过调用URLStreamHandler类的openConnection()方法,来创建URLConnection对象的。
- openStream()方法:返回用于读取服务器发送数据的输入流,该方法实际上通过调用URLConnection类的getInputStream()方法来获得输入流。
- getContent()方法:返回包装了服务器发送数据的Java对象,该方法实际上调用URLConnection类的getContent()方法,而URLConnection类的getContent()方法又调用了ContentHandler类的getContent()方法。
二、 URLConnection类的用法
URLConnection类表示客户程序与远程服务器的连接。URLConnection有两个boolean类型的属性以及相应的get和set方法:
doInput属性:如果取值为true,表示允许获得输入流,读取远程服务器发送的数据。该属性的默认值为true。程序可通过getDoInput()和setDoInput()方法来读取和设置该属性。
doOutput属性:如果取值为true,表示允许获得输出流,向远程服务器发送数据。该属性的默认值为false。程序可通过getDoOutput()和setDoOutput()方法来读取和设置该属性。
URLConnection类提供了读取远程服务器的响应数据的一系列方法:
- getHeaderField(String name):返回响应头中参数name指定的属性的值。
- getContentType():返回响应正文的类型。如果无法获取响应正文的类型,就返回null。对于HTTP响应结果,在响应头中可能会包含响应正文的类型信息。
- getContentLength():返回响应正文的长度。如果无法获取响应正文的长度,就返回-1。对于HTTP响应结果,在响应头中可能会包含响应正文的长度信息。
- getContentEncoding():返回响应正文的编码类型。如果无法获取响应正文的编码类型,就返回null。对于HTTP响应结果,在响应头中可能会包含响应正文的编码类型信息。
以下例程的HttpClient2类利用URLConnection类来读取服务器的响应结果。
/* HttpClient2.java (使用URLConnection类)*/
import java.net.*;
import java.io.*;
public class HttpClient2{
public static void main(String args[])throws IOException{
URL url=new URL("http://www.javathinker.net/hello.htm");
URLConnection connection=url.openConnection();
//接收响应结果
System.out.println("正文类型:"+connection.getContentType());
System.out.println("正文长度:"+connection.getContentLength());
InputStream in=connection.getInputStream(); //读取响应正文
ByteArrayOutputStream buffer=new ByteArrayOutputStream();
byte[] buff=new byte[1024];
int len=-1;
while((len=in.read(buff))!=-1){
buffer.write(buff,0,len);
}
//把字节数组转换为字服务器托管网符串
System.out.println(new String(buffer.toByteArray()));
}
}
运行“java HttpClient2”命令,将得到如下打印结果:
正文类型:text/html
正文长度:97
helloapp
hello
HTTP响应结果包括HTTP响应代码、响应头和响应正文这三部分。而Oracle公司在对HTTP协议的框架实现中,HttpURLConnection类作为URLConnection的具体子类,它的getInputStream()方法仅仅返回响应正文部分的输入流。所以本范例的HttpClient2实际上仅仅读取了HTTP响应结果中的正文部分内容。
客户程序调用URLConnection的getInputStream()方法得到输入流后,就能读取服务器发送的响应正文。响应正文有可能是图片文件,也有可能是文本文件,还有可能是压缩文件或可运行文件等。客户程序可以采用以下几种方式来判断响应正文的类型:
(1)调用URLConnection类的getContentType()方法。
(2)调用URLConnection类的静态guessContentTypeFromName(String fname)方法,参数fname表示URL地址中的文件名部分。该方法根据文件的扩展名来猜测响应正文的类型。表6-1列出了文件扩展名与响应正文类型的对应关系。从该表可以推断出,执行URLConnection.guessContentTypeFromName(“hello.htm”)方法,其返回结果应该是“text/html”。
正文类型也称为MIME(Multipurpose Internet Mail Extensions)类型,它规定了在Internet网上传送的常见数据类型的格式。RFC2045文档详细描述了MIME规范,网址为:https://www.ietf.org/rfc/rfc2045.txt
(3)调用URLConnection类的静态guessContentTypeFromStream (InputStream in)方法,参数in表示输入流。该方法根据输入流中的前几个字节来猜测响应正文的类型。以下表列出了输入流中的前几个字节与响应正文类型的对应关系。
客户程序只要先调用了URLConnection类的setDoOutput(true)方法,就能通过URLConnection类的getOutputStream()方法获得输出流,然后向服务器发送数据。对于HTTP客户程序,在POST方式下可以发送大量表单数据。以下例程的HttpClient3类访问的URL地址为:http://www.javathinker.net/aboutBook.jsp
图 HttpClient3向服务器查询特定书的信息
/* 例程 HttpClient3.java */
import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class HttpClient3{
public static void main(String[] args){
JFrame frame = new PostTestFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class PostTestFrame extends JFrame{
/** 负责发送HTTP请求正文,以及接收HTTP响应正文 */
public static String doPost(String urlString, Map
nameValuePairs)
throws IOException{
URL url = new URL(urlString);
URLConnection connection = url.openConnection();
connection.setDoOutput(true); //允许输出数据
//发送HTTP请求正文
PrintWriter out = new PrintWriter(connection.getOutputStream());
boolean first = true;
for (Map.Entry pair : nameValuePairs.entrySet()){
if (first) first = false;
else out.print('&');
String name = pair.getKey();
String value = 服务器托管网pair.getValue();
out.print(name);
out.print('=');
//请求正文采用GB2312编码
out.print(URLEncoder.encode(value, "GB2312"));
}
out.close();
//接收HTTP响应正文
InputStream in=connection.getInputStream(); //读取响应正文
ByteArrayOutputStream buffer=new ByteArrayOutputStream();
byte[] buff=new byte[1024];
int len=-1;
while((len=in.read(buff))!=-1){
buffer.write(buff,0,len);
}
in.close();
return new String(buffer.toByteArray()); //把字节数组转换为字符串
}
public PostTestFrame(){
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
setTitle("卫琴书籍系列");
JPanel northPanel = new JPanel();
add(northPanel, BorderLayout.NORTH);
final JComboBox combo = new JcomboBox();
for (int i = 0; i post =
new HashMap();
post.put("title",
books[combo.getSelectedIndex()]);
try{
result.setText(doPost(SERVER_URL, post));
}catch (IOException e){
result.setText("" + e);
}
}
}).start();
}
});
}
private static String[] books = {"Java面向对象编程",
"Tomcat与JavaWeb开发技术详解",
"精通Struts:基于MVC的JavaWeb设计与开发",
"精通JPA与Hibernate:Java对象持久化技术详解",
"Java2认证考试指南与试题解析"};
public static final int DEFAULT_WIDTH = 400;
public static final int DEFAULT_HEIGHT = 300;
}
上文参考孙卫琴的经典Java书籍《Java面向对象编程》
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
相关推荐: Spring的创建与使用
1.创建 Spring 项⽬ 2.将 Bean (对象) 存储到 Spring (容器) 中3.ApplicationContext与BeanFactory目录 1.创建 Spring 项⽬ 1.1 创建一个 maven 项目 1.2 添加 spring 框架支持(spring-context/spring-beans) 2.将 Bean (对象) 存储到 Spring (容器) 中 2.1 在resour…