Http客户端程序已集成在Java语言中,可以通过URLCo ection类调用。遗憾的
是,由于SUN没有公布Http客户程序的源码,它实现的细节仍是一个谜。本文根据HTTP
协议规范,用Java.net.Socket类实现一个HTTP协议客户端程序。
1.Socket类:
了解TCP/IP协议集通信的读者知道,协议间的通信是通过Socket完成的。在
Java.net包中,Socket类就是对Socket的具体实现。它通过连接到主机后,返回一个
I/O流,实现协议间的信息交换。
2.HTTP协议
HTTP协议同其它TCP/IP协议集中的协议一样,是遵循客户/服务器模型工作的。客
户端发往服务端的信息格式如下:
------------------------------
请求方法URLHTTP协议的版本号
提交的元信息
**空行**
实体
------------------------------
请求方法是对这次连接工作的说明,目前HTTP协议已经发展到1.1版,它包括GET、
HEAD、POST、DELETE、OPTIO 、TRACE、PUT七种。元信息是关于上常用的
*提交表格。
*/
publicvoidPOST(Stringurl,Stringcontent){
try{
checkHTTP(url);
ope erver(target.getHost(),target.getPort());
Stringcmd="POST"+getURLFormat(target)+"
HTTP/1.0\r\n"+getBaseHeads();
cmd+="Content-type:a lication/x-www-form-urlencoded\r\n";
cmd+="Content-length:"+content.length()+"\r\n\r\n";
cmd+=content+"\r\n";
sendMe age(cmd);
receiveMe age();
}catch(ProtocolExceptio ){
p.printStackTrace();
retur
}catch(UnknownHostExceptione){
e.printStackTrace();
retur
}catch(IOExceptioni)
i.printStackTrace();
retur
}
}
protectedvoidcheckHTTP(Stringurl)throw rotocolException{
try{
URLtarget=newURL(url);
if(target==null||!target.getProtocol().toU erCase().equals("HTTP"))
thrownewProtocolException("这不是HTTP协议");
this.target=target;
}catch(MalformedURLExceptionm){
thrownewProtocolException("协议格式错误");
}
}
/*
*与Web服务器连接。若找不到Web服务器,InetAddre 会引发UnknownHostException
*异常。若Socket连接失败,会引发IOException异常。
*/
protectedvoidope erver(Stringhost,intport)throws
UnknownHostException,IOException{
header.clear();
re o eMe age="";re o eCode=-1;
try{
if(client!=null)closeServer();
if(byteStream!=null){
byteStream.close() yteStream=null;
}
InetAddre addre =InetAddre .getByName(host);
client=newSocket(addre ,port==-1?80:port);
sender=newBufferedOutputStream(client.getOutputStream());
receiver=newBufferedI utStream(client.getI utStream());
}catch(UnknownHostExceptionu){
throwu;
}catch(IOExceptioni){
throwi;
}
}
/*关闭与Web服务器的连接*/
protectedvoidcloseServer()throwsIOException{
if(client==null)retur
try{
client.close() ender.close();receiver.close();
}catch(IOExceptioni){
throwi;
}
client=null ender=null;receiver=null;
}
protectedStringgetURLFormat(URLtarget){
String ec="http://"+target.getHost();
if(target.getPort()!=-1)
ec+=":"+target.getPort();
retur ec+=target.getFile();
}
/*向Web服务器传送数据*/
protectedvoidsendMe age(Stringdata)throwsIOException{
sender.write(data.getBytes(),0,data.length());
sender.flush();
}
/*接收来自Web服务器的数据*/
protectedvoidreceiveMe age()throwsIOException{
bytedata[]=newbyte[1024];
intcount=0;
intword=-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;
}
Stringme age=newString(data,0,count);
intmark=me age.indexOf(32);
serverVersion=me age.su tring(0,mark);
while(markre o eCode=Integer.parseInt(me age.su tring(mark+1,mark+=4));
re o eMe age=me age.su tring(mark,me age.length()).trim();
//应答状态码和处理请读者添加
switch(re o eCode){
case400:
thrownewIOException("错误请求");
case404:
thrownewFileNotFoundException(getURLFormat(target));
case503:
thrownewIOException("服务器不可用");
}
if(word==-1)thrownewProtocolException("信息接收异常终止");
intsymbol=-1;
count=0;
//解析元信息
while(word!='\r'&am am word!='\n'&am am 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 reak;
case'\r':
case'\n':
word=receiver.read();
if(symbol=='\r'&am am word=='\n'){
word=receiver.read();
if(word=='\r')word=receiver.read();
}
if(word=='\r'||word=='\n'||word>32)breakparseLine;
symbol=32 reak;
}
if(count==data.length)data=addCapacity(data);
data[count++]=(byte)symbol;
}
word=-1;
}
me age=newString(data,0,count);
mark=me age.indexOf(':');
Stringkey=null;
if(mark>0)key=me age.su tring(0,mark);
mark++;
while(markStringvalue=me age.su tring(mark,me age.length());
header.put(key,value);
count=0;
}
//获得正文数据
while((word=receiver
if(count>0)byteStream=newByteArrayI utStream(data,0,count);
data=null;
closeServer();
}
publicStringgetRe o eMe age(){
returnre o eMe age;
}
publicintgetRe o eCode(){
returnre o eCode;
}
publicStringgetServerVersion(){
retur erverVersio
}
publicI utStreamgetI utStream(){
retur yteStream;
}
publicsynchronizedStringgetHeaderKey(inti){
if(i>=header.size())retur ull;
Enumerationenum=header.propertyNames();
Stringkey=null;
for(intj=0;j<=i;j++)
key=(String)enum.nextElement();
returnkey;
}
publicsynchronizedStringgetHeaderValue(inti){
if(i>=header.size())retur ull;
returnheader.getProperty(getHeaderKey(i));
}
publicsynchronizedStringgetHeaderValue(Stringkey){
returnheader.getProperty(key);
}
protectedStringgetBaseHeads(){
Stringinf="User-Agent:myselfHttp/1.0\r\n"+
"Accept:www/source;text/html;image/gif;*/*\r\n";
returninf;
}
privatebyte[]addCapacity(byterece[]){
bytetemp[]=newbyte[rece.length+1024];
System.arraycopy(rece,0,temp,0,rece.length);
returntem
}
}
注:程序中只实现GET、HEAD、POST三种方法。其他几种因不常使用,暂且忽略。


