专题推荐网络编程基础到进阶教程
在讨论HTTP协议的具体请求和响应头字段之前让我们先来利用以前所学的知识来实现一个HTTP模拟器所谓HTTP模拟器就是可以在用户输入HTTP的请求消息后由这个模拟器将HTTP请求发送给相应的服务器再接收服务器的响应消息这个HTTP模拟器有几下特点
可以手工输入HTTP请求并向服务器发送
接收服务器的响应消息
消息头和实体内容分段显示也就是说并不是象Telnet等客户端一样将HTTP响
应消息全部显示而是先显示消息头然后由用户决定是否显示实体内容
集中发送请求这个HTTP模拟器和Telnet不同的是并不是一开始就连接服务器
而是将域名端口以及HTTP请求消息都输完后才连接服务器并将这些请求发送给服务器这样做的可以预防服务器提前关闭网络连接的现象
可以循环做上述的操作
从以上的描述看要实现这个HTTP模拟器需要以下五步
建立一个大循环在循环内部是一个请求/响应对这样就可以向服务器发送多次请求/响应以了下面的四步都是被包括在循环内部的
从控制台读取域名和端口这个功能可以由readHostAndPort(……)来完成
从控制台读取HTTP请求消息这个功能由readHttpRequest(……)来完成
向服务器发送HTTP请求消息这个功能由sendHttpRequest()来完成
读取服务器回送的HTTP响应消息这个功能由readHttpResponse(……)来完成
下面我们就来逐步实现这五步
一建立一个大循环
在建立这个循环之前先建立一个中叫HttpSimulator的类并在这个类中定义一个run方法用来运行这个程序实现代码如下
package http;
import*;
importjavaio*;
publicclassHttpSimulator
{
privateSocketsocket;
privateintport=;
privateStringhost=localhost;
privateStringrequest=;//HTTP请求消息
privatebooleanisPostisHead;
publicvoidrun()throwsException
{
BufferedReaderreader=newBufferedReader(newInputStreamReader(
Systemin));
while(true)//开始大循环
{
try
{
if(!readHostAndPort(reader))
break;
readHttpRequest(reader);
sendHttpRequest();
readHttpResponse(reader);
}
catch(Exceptione)
{
Systemoutprintln(err:+egetMessage());
}
}
}
publicstaticvoidmain(String[]args)throwsException
{
newHttpSimulator()run();
}
}
从上面的代码可以看出第和分别调用了上述的四个方法这些方法的具体实现将在后面讨论上面的代码除了调用这四个核心方法外还做了一些准备工作在至行定义了一些以后要用到的变量在和行使用控制台的输入流建立了BufferedReader对象通过这个对象可以直接从控制台读取字符串而不是一个个地字节
二readHostAndPort(……)方法的实现
这个方法的主要功能是从控制台读取域名和端口域名和端口通过隔开和域名以及端口之间不能有空格当从控制台读取一个q时这个函数返回false表示程序可以退出了否则返回true表示输入的域名和端口是正确的这个方法的实现代码如下
privatebooleanreadHostAndPort(BufferedReaderconsoleReader)
throwsException
{
Systemoutprint(host:port>);
String[]ss=null;
Strings=consoleReaderreadLine();
if(sequals(q))
returnfalse;
else
{
ss=ssplit([:]);
if(!ss[]equals())
host=ss[];
if(sslength>)
port=IntegerparseInt(ss[]);
Systemoutprintln(host+:+StringvalueOf(port));
returntrue;
}
}
第行这个方法有一个BufferedReader类型的参数这个参数的值就是在HttpSimulatorjava中的第和行根据控制台输入流建立的BufferedReader对象
第 行这输出HTTP模拟器的控制符就象Windows的控制台的C>一样
第 行从控制台读取一行字符串
第 行通过字符串的split方法和响应的正则表示式([])将域名和端口分开域名的默认值是localhost端口的默认值是
三readHttpRequest(……)方法的实现
这个方法的主要功能是从控制台读取HTTP请求消息如果输入一个空行表示请求消息头已经输完如果使用的是POST方法还要输入POST请求的实体内容这个方法的实现代码如下
privatevoidreadHttpRequest(BufferedReaderconsoleReader)
throwsException
{
Systemoutprintln(请输入HTTP请求:);
Strings=consoleReaderreadLine();
request=s+\r\n;
booleanisPost=ssubstring()equals(POST);
booleanisHead=ssubstring()equals(HEAD);
while(!(s=consoleReaderreadLine())equals())
request=request+s+\r\n;
request=request+\r\n;
if(isPost)
{
Systemoutprintln(请输入POST方法的内容:);
s=consoleReaderreadLine();
request=request+s;
}
}
第 行读入HTTP请求消息的第一行
第 行确定所输入的请求方法是不是POST和HEAD
第 行读入HTTP请求消息的其余行
第 行如果HTTP请求使用的是POST方法要求用户继续输入HTTP请求的实体内容
四sendHttpRequest()方法的实现
这个方法的功能是将request变量中的HTTP请求消息发送到服务器下面是这个方法的实现代码
privatevoidsendHttpRequest()throwsException
{
socket=newSocket();
socketsetSoTimeout(*);
Systemoutprintln(正在连接服务器);
nnect(newInetSocketAddress(hostport)*);
Systemoutprintln(服务器连接成功!);
OutputStreamout=socketgetOutputStream();
OutputStreamWriterwriter=newOutputStreamWriter(out);
writerwrite(request);
writerflush();
}
第行设置读取数据超时为秒
第行连接服务器并设置连接超时为秒
五readHttpResponse(……)方法的实现
这个方法的主要功能是从服务器读取返回的响应消息首先读取了响应消息头然后要求用户输入Y或N以确定是否显示响应消息的实体内容这个程序之所以这样做主要有两个原因
() 为了研究HTTP协议
() 由于本程序是以字符串形式显示响应消息的因此如果用户请求了一个二进制Web资源如一个rar文件那么实体内容将会显示乱码所以在显示完响应消息头后由用户决定是否显示实体内容
这个方法的实现代码如下
privatevoidreadHttpResponse(BufferedReaderconsoleReader)
{
Strings=;
try
{
InputStreamin=socketgetInputStream();
InputStreamReaderinReader=newInputStreamReader(in);
BufferedReadersocketReader=newBufferedReader(inReader);
Systemoutprintln(HTTP头);
booleanb=true;//true:未读取消息头false:已经读取消息头
while((s=socketReaderreadLine())!=null)
{
if(sequals()&&b==true&&!isHead)
{
Systemoutprintln();
b=false;
Systemoutprint(是否显示HTTP的内容(Y/N):);
Stringchoice=consoleReaderreadLine();
if(choiceequals(Y)||choiceequals(y))
{
Systemoutprintln(HTTP内容);
continue;
}
else
break;
}
else
Systemoutprintln(s);
}
}
catch(Exceptione)
{
Systemoutprintln(err:+egetMessage());
}
finally
{
try
{
socketclose();
}
catch(Exceptione)
{
}
}
Systemoutprintln();
}
在上面的代码中行是最值得注意的其中sequals()表示读入一个空行(表明消息头已经结束)由于在实体内容中也可以存在空行因此b == true来标记消息头是否已经被读过当读完消息头后将b设为false如果以后再遇到空行就不会当成消息头来处理了当HTTP请求使用HEAD方法时服务器只返回响应消息头因此使用!isHead来保证使用HEAD发送请求时不显示响应消息的内容实体
现在我们已经实现了这个HTTP模拟器下面让我们来运行并测试它
运行
运行如下的命令
javahttpHttpSimulator
运行以上的命令后将显示如图所示的界面
图
测试
在HTTP模拟器中输入如下的域名
在HTTP模拟器中输入如下的HTTP请求消息
GET / HTTP/
Host
运行的结果如图所示
本文实现的Http模拟器在后面的文章中会经常使用读者可以从本文的开始部分下载Http模拟器的源代码和class文件