出自:www.cn-java.com
http客户端程序已集成在java语言中,可以通过urlconnection类调用。遗憾的
是,由于sun没有公布http客户程序的源码,它实现的细节仍是一个谜。本文根据http
协议规范,用java.net.socket类实现一个http协议客户端程序。
1.socket类:
了解tcp/ip协议集通信的读者知道,协议间的通信是通过socket完成的。在
java.net包中,socket类就是对socket的具体实现。它通过连接到主机后,返回一个
i/o流,实现协议间的信息交换。
2 . http协议
http协议同其它tcp/ip协议集中的协议一样,是遵循客户/服务器模型工作的。客
户端发往服务端的信息格式如下:
------------------------------
请求方法 url http协议的版本号
提交的元信息
**空行**
实体
------------------------------
请求方法是对这次连接工作的说明,目前http协议已经发展到1.1版,它包括get、
head、post、delete、options、trace、put七种。元信息是关于当前请求的信息。通
过分析元信息,可以检查实体数据是否完整,接收过程是否出错,类型是否匹配等。元
信息的引入使http协议通信更加稳妥可靠。实体是请求的具体内容。
将上述报文发往web服务器,如果成功,应答格式如下:
--------------------------------
http协议的版本号 应答状态码 应答状态码说明
接收的元信息
**空行**
实体
--------------------------------
以上报文发向客户端,并且接收成功,彼此间关闭连接,完成一次握手。
下面用最常用的get方法,来说明具体的报文应用
----------------------------------
get http://www.youhost.com http/1.0
accept: www/source; text/html; image/gif; image/jpeg; */*
user_agent: myagent
**空行**
-----------------------------------
这个报文是向www.youhost.com主机请求一个缺省html文档。客户端http协议版本
号是1.0版,元信息包括可接收的文件格式,用户代理,每一段之间用回车换行符分
隔,最后以一个空行结束。发向服务器后,如果执行过程正常,服务器返回以下代码:
------------------------------------
http/1.1 200 ok
date: tue, 14 sep 1999 02:19:57 gmt
server: apache/1.2.6
connection: close
content-type: text/html
**空行**
......
------------------------------------
http/1.1表示这个http服务器是1.1版,200是服务器对客户请求的应答状态码,ok
是对应答状态码的解释,之后是这个文档的元信息和文档正文。(相关应答状态码和元
信息的解释请参阅.net.net标准草案:rfc2616)。
3. http客户端程序:
import java.net.*;
import java.io.*;
import java.util.properties;
import java.util.enumeration;
public class http {
protected socket client;
protected bufferedoutputstream sender;
protected bufferedinputstream receiver;
protected bytearrayinputstream bytestream;
protected url target;
private int responsecode=-1;
private string responsemessage="";
private string serverversion="";
private properties header = new properties();
public http() { }
public http(string url) {
get(url) ;
}
/* get方法根据url,会请求文件、数据库查询结果、程序运行结果等多种内容 */
public void get(string url) {
try {
checkhttp(url);
openserver(target.gethost(),target.getport() );
string cmd = "get "+ geturlformat(target) +" http/1.0\r\n"
+ getbaseheads()+"\r\n";
sendmessage(cmd);
receivemessage();
}catch(protocolexception p) {
p.printstacktrace();
return;
}catch(unknownhostexception e) {
e.printstacktrace();
return;
}catch(ioexception i)
i.printstacktrace();
return;
}
}
/*
* head方法只请求url的元信息,不包括url本身。若怀疑本机和服务器上的
* 文件相同,用这个方法检查最快捷有效。
*/
public void head(string url) {
try {
checkhttp(url);
openserver(target.gethost(),target.getport() );
string cmd = "head "+geturlformat(target)+" http/1.0\r\n"
+getbaseheads()+"\r\n";
sendmessage(cmd);
receivemessage();
}catch(protocolexception p) {
p.printstacktrace();
return;
}catch(unknownhostexception e) {
e.printstacktrace();
return;
}catch(ioexception i)
i.printstacktrace();
return;
}
}
/*
* post方法是向服务器传送数据,以便服务器做出相应的处理。例如网页上常用的
* 提交表格。
*/
public void post(string url,string content) {
try {
checkhttp(url);
openserver(target.gethost(),target.getport() );
string cmd = "post "+ geturlformat(target) +"
http/1.0\r\n"+getbaseheads();
cmd += "content-type: application/x-www-form-urlencoded\r\n";
cmd += "content-length: " + content.length() + "\r\n\r\n";
cmd += content+"\r\n";
sendmessage(cmd);
receivemessage();
}catch(protocolexception p) {
p.printstacktrace();
return;
}catch(unknownhostexception e) {
e.printstacktrace();
return;
}catch(ioexception i)
i.printstacktrace();
return;
}
}
protected void checkhttp(string url) throws protocolexception {
try {
url target = new url(url);
if(target==null !target.getprotocol().touppercase().equals("http") )
throw new protocolexception("这不是http协议");
this.target = target;
}catch(malformedurlexception m) {
throw new protocolexception("协议格式错误");
}
}
/*
* 与web服务器连接。若找不到web服务器,.netaddress会引发unknownhostexception
* 异常。若socket连接失败,会引发ioexception异常。
*/
protected void openserver(string host,int port) throws
unknownhostexception,ioexception {
header.clear();
responsemessage=""; responsecode=-1;
try {
if(client!=null) closeserver();
if(bytestream != null) {
bytestream.close(); bytestream=null;
}
.netaddress address = .netaddress.getbyname(host);
client = new socket(address,port==-1?80:port);
sender = new bufferedoutputstream(client.getoutputstream());
receiver = new bufferedinputstream(client.getinputstream());
}catch(unknownhostexception u) {
throw u;
}catch(ioexception i) {
throw i;
}
}
/* 关闭与web服务器的连接 */
protected void closeserver() throws ioexception {
if(client==null) return;
try {
client.close(); sender.close(); receiver.close();
}catch(ioexception i) {
throw i;
}
client=null; sender=null; receiver=null;
}
protected string geturlformat(url target) {
string spec = "http://"+target.gethost();
if(target.getport()!=-1)
spec+=":"+target.getport();
return spec+=target.getfile();
}
/* 向web服务器传送数据 */
protected void sendmessage(string data) throws ioexception{
sender.write(data.getbytes(),0,data.length());
sender.flush();
}
/* 接收来自web服务器的数据 */
protected void receivemessage() throws ioexception{
byte data[] = new byte[1024];
int count=0;
int word=-1;
// 解析第一行
while( (word=receiver.read())!=-1 ) {
if(word=='\r'word=='\n') {
word=receiver.read();
if(word=='\n') word=receiver.read();
break;
}
if(count == data.length) data = addcapacity(data);
data[count++]=(byte)word;
}
string message = new string(data,0,count);
int mark = message.indexof(32);
serverversion = message.substring(0,mark);
while( mark responsecode = integer.parseint(message.substring(mark+1,mark+=4));
responsemessage = message.substring(mark,message.length()).trim();
// 应答状态码和处理请读者添加
switch(responsecode) {
case 400:
throw new ioexception("错误请求");
case 404:
throw new filenotfoundexception( geturlformat(target) );
case 503:
throw new ioexception("服务器不可用" );
}
if(word==-1) throw new protocolexception("信息接收异常终止");
int symbol=-1;
count=0;
// 解析元信息
while( word!='\r' && word!='\n' && word>-1) {
if(word=='\t') word=32;
if(count==data.length) data = addcapacity(data);
data[count++] = (byte)word;
parseline: {
while( (symbol=receiver.read()) >-1 ) {
switch(symbol) {
case '\t':
symbol=32; break;
case '\r':
case '\n':
word = receiver.read();
if( symbol=='\r' && word=='\n') {
word=receiver.read();
if(word=='\r') word=receiver.read();
}
if( word=='\r' word=='\n' word>32) break parseline;
symbol=32; break;
}
if(count==data.length) data = addcapacity(data);
data[count++] = (byte)symbol;
}
word=-1;
}
message = new string(data,0,count);
mark = message.indexof(':');
string key = null;
if(mark>0) key = message.substring(0,mark);
mark++;
while( mark string value = message.substring(mark,message.length() );
header.put(key,value);
count=0;
}
// 获得正文数据
while( (word=receiver
if(count>0) bytestream = new bytearrayinputstream(data,0,count);
data=null;
closeserver();
}
public string getresponsemessage() {
return responsemessage;
}
public int getresponsecode() {
return responsecode;
}
public string getserverversion() {
return serverversion;
}
public inputstream getinputstream() {
return bytestream;
}
public synchronized string getheaderkey(int i) {
if(i>=header.size()) return null;
enumeration enum = header.propertynames();
string key = null;
for(int j=0; j<=i; j++)
key = (string)enum.nextelement();
return key;
}
public synchronized string getheadervalue(int i) {
if(i>=header.size()) return null;
return header.getproperty(getheaderkey(i));
}
public synchronized string getheadervalue(string key) {
return header.getproperty(key);
}
protected string getbaseheads() {
string inf = "user-agent: myselfhttp/1.0\r\n"+
"accept: www/source; text/html; image/gif; */*\r\n";
return inf;
}
private byte[] addcapacity(byte rece[]){
byte temp[] = new byte[rece.length+1024];
system.arraycopy(rece,0,temp,0,rece.length);
return temp;
}
}
注: 程序中只实现get、head、post三种方法。其他几种因不常使用,暂且忽略。
Java Asp PHP .Net XML C/C++ CGI VB Jsp J2ee J2se J2me EJB Servlet Tomcat Resin Struts Weblogic Eclipse ANT GUI JMS Web servise IDEA Webphere Hibernate Spring Jboss Applet Swing Socket Javamail Perl Ajax P2P 安全 模式 框架 测试 开源 游戏
Windows XP Windows 2000 Windows 2003 Windows Me Windows 9.x Linux UNIX 注册表 操作系统 服务器 应用服务器