專題推薦網絡編程基礎到進階教程
在討論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 *;
import javaio*;
public class HttpSimulator
{
private Socket socket;
private int port = ;
private String host = localhost;
private String request = ; // HTTP請求消息
private boolean isPost isHead;
public void run() throws Exception
{
BufferedReader reader = new BufferedReader(new InputStreamReader(
Systemin));
while (true) // 開始大循環
{
try
{
if (!readHostAndPort(reader))
break;
readHttpRequest(reader);
sendHttpRequest();
readHttpResponse(reader);
}
catch (Exception e)
{
Systemoutprintln(err: + egetMessage());
}
}
}
public static void main(String[] args) throws Exception
{
new HttpSimulator()run();
}
}
從上面的代碼可以看出第和分別調用了上述的四個方法這些方法的具體實現將在後面討論上面的代碼除了調用這四個核心方法外還做了一些准備工作在至行定義了一些以後要用到的變量在和行使用控制台的輸入流建立了BufferedReader對象通過這個對象可以直接從控制台讀取字符串而不是一個個地字節
二readHostAndPort(……)方法的實現
這個方法的主要功能是從控制台讀取域名和端口域名和端口通過隔開和域名以及端口之間不能有空格當從控制台讀取一個q時這個函數返回false表示程序可以退出了否則返回true表示輸入的域名和端口是正確的這個方法的實現代碼如下
private boolean readHostAndPort(BufferedReader consoleReader)
throws Exception
{
Systemoutprint(host:port>);
String[] ss = null;
String s = consoleReaderreadLine();
if (sequals(q))
return false;
else
{
ss = ssplit([:]);
if (!ss[]equals())
host = ss[];
if (sslength > )
port = IntegerparseInt(ss[]);
Systemoutprintln(host + : + StringvalueOf(port));
return true;
}
}
第行這個方法有一個BufferedReader類型的參數這個參數的值就是在HttpSimulatorjava中的第和行根據控制台輸入流建立的BufferedReader對象
第 行這輸出HTTP模擬器的控制符就象Windows的控制台的C>一樣
第 行從控制台讀取一行字符串
第 行通過字符串的split方法和響應的正則表示式([])將域名和端口分開域名的默認值是localhost端口的默認值是
三readHttpRequest(……)方法的實現
這個方法的主要功能是從控制台讀取HTTP請求消息如果輸入一個空行表示請求消息頭已經輸完如果使用的是POST方法還要輸入POST請求的實體內容這個方法的實現代碼如下
private void readHttpRequest(BufferedReader consoleReader)
throws Exception
{
Systemoutprintln(請輸入HTTP請求:);
String s = consoleReaderreadLine();
request = s + \r\n;
boolean isPost = ssubstring( )equals(POST);
boolean isHead = 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請求消息發送到服務器下面是這個方法的實現代碼
private void sendHttpRequest() throws Exception
{
socket = new Socket();
socketsetSoTimeout( * );
Systemoutprintln(正在連接服務器);
nnect(new InetSocketAddress(host port) * );
Systemoutprintln(服務器連接成功!);
OutputStream out = socketgetOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(out);
writerwrite(request);
writerflush();
}
第行設置讀取數據超時為秒
第行連接服務器並設置連接超時為秒
五readHttpResponse(……)方法的實現
這個方法的主要功能是從服務器讀取返回的響應消息首先讀取了響應消息頭然後要求用戶輸入Y或N以確定是否顯示響應消息的實體內容這個程序之所以這樣做主要有兩個原因
() 為了研究HTTP協議
() 由於本程序是以字符串形式顯示響應消息的因此如果用戶請求了一個二進制Web資源如一個rar文件那麼實體內容將會顯示亂碼所以在顯示完響應消息頭後由用戶決定是否顯示實體內容
這個方法的實現代碼如下
private void readHttpResponse(BufferedReader consoleReader)
{
String s = ;
try
{
InputStream in = socketgetInputStream();
InputStreamReader inReader = new InputStreamReader(in);
BufferedReader socketReader = new BufferedReader(inReader);
Systemoutprintln(HTTP頭);
boolean b = true; // true: 未讀取消息頭 false: 已經讀取消息頭
while ((s = socketReaderreadLine()) != null)
{
if (sequals() && b == true && !isHead)
{
Systemoutprintln();
b = false;
Systemoutprint(是否顯示HTTP的內容(Y/N):);
String choice = consoleReaderreadLine();
if (choiceequals(Y) || choiceequals(y))
{
Systemoutprintln(HTTP內容);
continue;
}
else
break;
}
else
Systemoutprintln(s);
}
}
catch (Exception e)
{
Systemoutprintln(err: + egetMessage());
}
finally
{
try
{
socketclose();
}
catch (Exception e)
{
}
}
Systemoutprintln();
}
在上面的代碼中行是最值得注意的其中sequals()表示讀入一個空行(表明消息頭已經結束)由於在實體內容中也可以存在空行因此b == true來標記消息頭是否已經被讀過當讀完消息頭後將b設為false如果以後再遇到空行就不會當成消息頭來處理了當HTTP請求使用HEAD方法時服務器只返回響應消息頭因此使用!isHead來保證使用HEAD發送請求時不顯示響應消息的內容實體
現在我們已經實現了這個HTTP模擬器下面讓我們來運行並測試它
運行
運行如下的命令
java httpHttpSimulator
運行以上的命令後將顯示如圖所示的界面
圖
測試
在HTTP模擬器中輸入如下的域名
在HTTP模擬器中輸入如下的HTTP請求消息
GET / HTTP/
Host
運行的結果如圖所示
本文實現的Http模擬器在後面的文章中會經常使用讀者可以從本文的開始部分下載Http模擬器的源代碼和class文件
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26047.html