一個簡單的用 Java Socket 編寫的 HTTP 伺服器應用, 示範了請求和應答的協定通信内容以及給用戶端傳回 HTML 文本和二進制資料檔案(一個圖檔), 同時展示了 404, 200 等狀态碼.
首先運作這個程式,然後打開Web浏覽器,鍵入http://localhost,則這個程式能夠顯示出浏覽器發送了那些資訊并且向浏覽器傳回一個網頁和一副圖檔, 并測試同浏覽器對話. 當浏覽器看到 HTML 中帶有圖檔位址時, 則會發出第二次連接配接來請求圖檔等資源. 這個例子可以幫您了解 Java 的 HTTP 伺服器軟體是基于 J2SE 的 Socket 等軟體編寫的概念, 并熟悉HTTP 協定. 相反的用 Telnet 連接配接到已有的伺服器則可以幫忙了解浏覽器的運作過程和伺服器端的傳回内容.
當使用者在Web浏覽器位址欄中輸入一個帶有http://字首的URL并按下Enter後,或者在Web頁面中某個以http://開頭的超連結上單擊滑鼠,HTTP事務處理的第一個階段--建立連接配接階段就開始了.HTTP的預設端口是80. 随着連接配接的建立,HTTP就進入了客戶向伺服器發送請求的階段.客戶向伺服器發送的請求是一個有特定格式的ASCII消息,其文法規則為:
< Method > < URL > < HTTP Version > <\r\n>
{ <Header>:<Value> <\r\n>}
<\r\n>
{ Entity Body }
請求消息的頂端是請求行,用于指定方法,URL和HTTP協定的版本,請求行的最後是回車換行.方法GET,POST,HEAD,PUT,DELETE等. 在請求行之後是若幹個報頭(Header)行.每個報頭行都是由一個報頭和一個取值構成的二進制對,報頭和取值之間以":"分隔;報頭行的最後是回車換行.常見的報頭有Accept(指定MIME媒體類型),Accept_Charset(響應消息的編碼方式),Accept_Encoding(響應消息的字元集),User_Agent(使用者的浏覽器資訊)等. 在請求消息的報頭行之後是一個回車換行,表明請求消息的報頭部分結束.在這個之後是請求消息的消息實體(Entity Body).具體的例子參看httpRequest.txt.
Web伺服器在收到客戶請求并作出處理之後,要向客戶發送應答消息.與請求消息一樣,應答消息的文法規則為:
< HTTP Version> <Status Code> [<Message>]<\r\n>
{ <Header>:<Value> <\r\n> }
<\r\n>
{ Entity Body }
應答消息的第一行為狀态行,其中包括了HTTP版本号,狀态碼和對狀态碼進行簡短解釋的消息;狀态行的最後是回車換行.狀态碼由3位數字組成,有5類:
參看:HTTP應答碼及其意義
1XX 保留
2XX 表示成功
3XX 表示URL已經被移走
4XX 表示客戶錯誤
5XX 表示伺服器錯誤
例如:415,表示不支援改媒體類型;503,表示伺服器不能通路.最常見的是200,表示成功.常見的報頭有:Last_Modified(最後修改時間),Content_Type(消息内容的MIME類型),Content_Length(内容長度)等.
在報頭行之後也是一個回車換行,用以表示應答消息的報頭部分的結束,以及應答消息實體的開始.
下面是一個應答消息的例子:
HTTP/1.0 200 OK
Date: Moday,07-Apr-97 21:13:02 GMT
Server:NCSA/1.1
MIME_Version:1.0
Content_Type:text/html
Last_Modified:Thu Dec 5 09:28:01 1996
Coentent_Length:3107
<HTML><HEAD><TITLE></HTML>
在用Java語言實作HTTP伺服器時,首先啟動一個java.net.ServerSocket在提供服務的端口上監聽連接配接.向客戶傳回文本時,可以用 PrintWriter,但是如果傳回二進制資料,則必須使用OutputStream.write(byte[])方法,傳回的應答消息字元串可以使用 String.getBytes()方法轉換為位元組數組傳回,或者使用PrintStream的print()方法寫入文本,用 write(byte[])方法寫入二進制資料.
源程式來自http://blog.csdn.net/myeclipse_java,本程式是在原有基礎上進行了一些調整,源程式在POST請求時會阻塞,本程式解決了此問題,另外,本程式在原有的基礎上增加了檔案上傳與下載下傳模拟功能:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.StringTokenizer;
/**
*
* @author 劉長炯
* modi by jzj
*
*/
public class SimpleHttpServer implements Runnable {
ServerSocket serverSocket;//伺服器Socket
public static int PORT = 80;//标準HTTP端口
public String encoding = "GBK";
public SimpleHttpServer() {
try {
serverSocket = new ServerSocket(PORT);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
new Thread(this).start();
System.out.println("HTTP伺服器正在運作,端口:" + PORT);
}
public void run() {
while (true) {
try {
Socket client = serverSocket.accept();//客戶機(這裡是 IE 等浏覽器)已經連接配接到目前伺服器
if (client != null) {
System.out.println("連接配接到伺服器的使用者:" + client);
try {
// 第一階段: 打開輸入流
InputStream is = client.getInputStream();
System.out.println("用戶端發送的請求資訊: >>>>>>>>>>>>>>>>>>>>>>>>>");
// 讀取第一行, 請求位址
String line = readLine(is, 0);
//列印請求行
System.out.print(line);
// < Method > < URL > < HTTP Version > <\r\n> 取的是URL部分
String resource = line.substring(line.indexOf('/'), line
.lastIndexOf('/') - 5);
//獲得請求的資源的位址
resource = URLDecoder.decode(resource, encoding);//反編碼 URL 位址
String method = new StringTokenizer(line).nextElement()
.toString();// 擷取請求方法, GET 或者 POST
int contentLength = 0;//如果為POST方法,則會有消息體長度
// 讀取所有浏覽器發送過來的請求參數頭部資訊
do {
line = readLine(is, 0);
//如果有Content-Length消息頭時取出
if (line.startsWith("Content-Length")) {
contentLength = Integer.parseInt(line.split(":")[1]
.trim());
}
//列印請求部資訊
System.out.print(line);
//如果遇到了一個單獨的回車換行,則表示請求頭結束
} while (!line.equals("\r\n"));
//如果是POST請求,則有請求體
if ("POST".equalsIgnoreCase(method)) {
//注,這裡隻是簡單的處理表單送出的參數,而對于上傳檔案這裡是不能這樣處理的,
//因為上傳的檔案時消息體不隻是一行,會有多行消息體
System.out.print(readLine(is, contentLength));
System.out.println();
}
System.out.println("用戶端發送的請求資訊結束 <<<<<<<<<<<<<<<<<<<<<<<<<<");
System.out.println("使用者請求的資源是:" + resource);
System.out.println("請求的類型是: " + method);
System.out.println();
//如果是下載下傳檔案
if (resource.startsWith("/download")) {
fileDownload("test.txt", client);
closeSocket(client);
continue;
}
// GIF 圖檔就讀取一個真實的圖檔資料并傳回給用戶端
if (resource.endsWith(".gif")) {
imgDownload("test.gif", client);
closeSocket(client);
continue;
}
// 請求 JPG 格式就報錯 404
if (resource.endsWith(".jpg")) {
PrintWriter out = new PrintWriter(client.getOutputStream(),
true);
out.println("HTTP/1.0 404 Not found");//傳回應答消息,并結束應答
out.println();// 根據 HTTP 協定, 空行将結束頭資訊
out.close();
closeSocket(client);
continue;
} else {
// 用 writer 對用戶端 socket 輸出一段 HTML 代碼
PrintWriter out = new PrintWriter(client.getOutputStream(),
true);
out.println("HTTP/1.0 200 OK");//傳回應答消息,并結束應答
out.println("Content-Type:text/html;charset=" + encoding);
out.println();// 根據 HTTP 協定, 空行将結束頭資訊
out.println("<h1> Hello Http Server</h1>");
out.println("你好, 這是一個 Java HTTP 伺服器 demo 應用.<br>");
out.println("您請求的路徑是: " + resource + "<br>");
out.println("你請求的頁面含有圖檔:<img src='test.gif'><br>"
+ "<a href='test.gif'>手動點選打開test.gif圖檔檔案.</a>");
out.println("<br>伺服器不支援jpg格式圖檔,是以顯示XX:"
+ "<img src='test.jpg'><br><a href='test.jpg'>"
+ "手動點選打開test.jpg,會跳轉另一頁面,并且服務傳回為404錯誤</a><br>");
out
.println("<form method=post action='/path?qryParm=POST URL查詢參數' > POST 表單 "
+ "<input name=username value='使用者'> "
+ "<input name=submit type=submit value=submit></form>");
out
.println("<form method=get action='/path?qryParm=GET URL查詢參數' > GET 表單 "
+ "<input name=username value='使用者'> "
+ "<input name=submit type=submit value=submit></form>");
out
.println("<form method=post action='/path?qryParm=POST URL查詢參數'"
+ " enctype='multipart/form-data' >"
+ "檔案上傳 <input type='file' name=file1 ><br>"
+ " "
+ "<input type='file' name=file2 ><br>"
+ " "
+ "<input name=username value='使用者'> "
+ "<input name=submit type=submit value=submit></form>");
out.println("<a href='/download'>點選此處模拟檔案下載下傳</a>");
out.close();
closeSocket(client);
}
} catch (Exception e) {
System.out.println("HTTP伺服器錯誤:" + e.getLocalizedMessage());
}
}
//System.out.println(client+"連接配接到HTTP伺服器");//如果加入這一句,伺服器響應速度會很慢
} catch (Exception e) {
System.out.println("HTTP伺服器錯誤:" + e.getLocalizedMessage());
}
}
}
/*
* 這裡我們自己模拟讀取一行,因為如果使用API中的BufferedReader時,它是讀取到一個回車換行後
* 才傳回,否則如果沒有讀取,則一直阻塞,這就導緻如果為POST請求時,表單中的元素會以消息體傳送,
* 這時,消息體最末按标準是沒有回車換行的,如果此時還使用BufferedReader來讀時,則POST送出
* 時會阻塞。如果是POST送出時我們按照消息體的長度Content-Length來截取消息體,這樣就不會阻塞
*/
private String readLine(InputStream is, int contentLe) throws IOException {
ArrayList lineByteList = new ArrayList();
byte readByte;
int total = 0;
if (contentLe != 0) {
do {
readByte = (byte) is.read();
lineByteList.add(Byte.valueOf(readByte));
total++;
} while (total < contentLe);//消息體讀還未讀完
} else {
do {
readByte = (byte) is.read();
lineByteList.add(Byte.valueOf(readByte));
} while (readByte != 10);
}
byte[] tmpByteArr = new byte[lineByteList.size()];
for (int i = 0; i < lineByteList.size(); i++) {
tmpByteArr[i] = ((Byte) lineByteList.get(i)).byteValue();
}
lineByteList.clear();
String tmpStr = new String(tmpByteArr, encoding);
/* http請求的header中有一個Referer屬性,這個屬性的意思就是如果目前請求是從别的頁面連結過
* 來的,那個屬性就是那個頁面的url,如果請求的url是直接從浏覽器位址欄輸入的就沒有這個值。得
* 到這個值可以實作很多有用的功能,例如防盜鍊,記錄通路來源以及記住剛才通路的連結等。另外,浏
* 覽器發送這個Referer連結時好像固定用UTF-8編碼的,是以在GBK下出現亂碼,我們在這裡糾正一下
*/
if (tmpStr.startsWith("Referer")) {//如果有Referer頭時,使用UTF-8編碼
tmpStr = new String(tmpByteArr, "UTF-8");
}
return tmpStr;
}
/**
* 關閉用戶端 socket 并列印一條調試資訊.
* @param socket 用戶端 socket.
*/
void closeSocket(Socket socket) {
try {
socket.close();
} catch (IOException ex) {
ex.printStackTrace();
}
System.out.println(socket + "離開了HTTP伺服器");
}
/**
* 讀取一個圖像檔案的内容并傳回給浏覽器端.
* @param fileName 檔案名
* @param socket 用戶端 socket.
*/
void imgDownload(String fileName, Socket socket) {
try {
PrintStream out = new PrintStream(socket.getOutputStream(), true);
File fileToSend = new File(fileName);
if (fileToSend.exists() && !fileToSend.isDirectory()) {
out.println("HTTP/1.0 200 OK");//傳回應答消息,并結束應答
out.println("Content-Type: application/octet-stream");
out.println("Content-Length: " + fileToSend.length());// 傳回内容位元組數
out.println();// 根據 HTTP 協定, 空行将結束頭資訊
FileInputStream fis = new FileInputStream(fileToSend);
byte data[] = new byte[fis.available()];
fis.read(data);
out.write(data);
//檔案下載下傳完後關閉socket流,但socket還沒有關閉
out.close();
fis.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 讀取一個檔案的内容并傳回給浏覽器端.
* @param fileName 檔案名
* @param socket 用戶端 socket.
*/
void fileDownload(String fileName, Socket socket) {
try {
PrintStream out = new PrintStream(socket.getOutputStream(), true);
File fileToSend = new File(fileName);
if (fileToSend.exists() && !fileToSend.isDirectory()) {
out.println("HTTP/1.0 200 OK");//傳回應答消息,并結束應答
out.println("Content-Type: application/octet-stream;charset=" + encoding);
/* Content-Disposition不是标準參數,檢視一下HTTP/1.1的規範文檔,對于這個參數的解釋大意如下:
* Content-Disposition參數本來是為了在用戶端另存檔案時提供一個建議的檔案名,但是考慮到安全的原因,
* 就從規範中去掉了這個參數。但是由于很多浏覽器已經能夠支援這個參數,是以隻是在規範文檔中列出,但是要
* 注意這個不是HTTP/1.1的标準參數。其值為“attachment”,那麼無論這個檔案是何類型,浏覽器都會提示我
* 們下載下傳此檔案,因為此時它認為後面的消息體是一個“附件”,不需要由浏覽器來處理了。
*/
out.println("Content-Disposition: attachment;filename=測試下載下傳檔案.txt");
// out.println("Accept-Ranges: bytes");
out.println("Content-Length: " + fileToSend.length());// 傳回内容位元組數
out.println();// 根據 HTTP 協定, 空行将結束頭資訊
FileInputStream fis = new FileInputStream(fileToSend);
byte[] tmpByteArr = new byte[10];//這裡為了測試看下載下傳進度條,是以設定小點
while (fis.available() > 0) {
int readCount = fis.read(tmpByteArr);
out.write(tmpByteArr, 0, readCount);
}
//檔案下載下傳完後關閉socket流
out.close();
fis.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
PORT = 8080;
new SimpleHttpServer();
}
}
下面是我測試的過程:
在位址欄輸入http://localhost:8080/後顯示如下頁面:
點選“手動點選打開test.gif圖檔檔案. ”連接配接顯示以下圖檔:
再點選“手動點選打開test.jpg,會跳轉另一頁面,并且服務傳回為404錯誤”顯示以下錯誤頁面:
再點選“點選此處模拟檔案下載下傳”顯示檔案下載下傳框:
最後是伺服器運作日志,僅供參考:
HTTP伺服器正在運作,端口:8080 連接配接到伺服器的使用者:Socket[addr=/0:0:0:0:0:0:0:1,port=56155,localport=8080] 用戶端發送的請求資訊: >>>>>>>>>>>>>>>>>>>>>>>>> GET / HTTP/1.1 Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, ** Referer: http://localhost:8080/ Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 用戶端發送的請求資訊結束 <<<<<<<<<<<<<<<<<<<<<<<<<< 使用者請求的資源是:/test.gif 請求的類型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56156,localport=8080]離開了HTTP伺服器 連接配接到伺服器的使用者:Socket[addr=/0:0:0:0:0:0:0:1,port=56157,localport=8080] 用戶端發送的請求資訊: >>>>>>>>>>>>>>>>>>>>>>>>> GET /test.jpg HTTP/1.1 Accept: ** Referer: http://localhost:8080/ Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 用戶端發送的請求資訊結束 <<<<<<<<<<<<<<<<<<<<<<<<<< 使用者請求的資源是:/test.gif 請求的類型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56158,localport=8080]離開了HTTP伺服器 連接配接到伺服器的使用者:Socket[addr=/0:0:0:0:0:0:0:1,port=56159,localport=8080] 用戶端發送的請求資訊: >>>>>>>>>>>>>>>>>>>>>>>>> GET /test.jpg HTTP/1.1 Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, ** Referer: http://localhost:8080/ Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Content-Type: application/x-www-form-urlencoded Accept-Encoding: gzip, deflate Host: localhost:8080 Content-Length: 35 Connection: Keep-Alive Cache-Control: no-cache username=%D3%C3%BB%A7&submit=submit 用戶端發送的請求資訊結束 <<<<<<<<<<<<<<<<<<<<<<<<<< 使用者請求的資源是:/path?qryParm=POST URL查詢參數 請求的類型是: POST Socket[addr=/0:0:0:0:0:0:0:1,port=56160,localport=8080]離開了HTTP伺服器 連接配接到伺服器的使用者:Socket[addr=/0:0:0:0:0:0:0:1,port=56161,localport=8080] 用戶端發送的請求資訊: >>>>>>>>>>>>>>>>>>>>>>>>> GET /test.gif HTTP/1.1 Accept: ** Referer: http://localhost:8080/path?qryParm=POST URL查詢參數 Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 用戶端發送的請求資訊結束 <<<<<<<<<<<<<<<<<<<<<<<<<< 使用者請求的資源是:/test.jpg 請求的類型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56162,localport=8080]離開了HTTP伺服器 連接配接到伺服器的使用者:Socket[addr=/0:0:0:0:0:0:0:1,port=56163,localport=8080] 用戶端發送的請求資訊: >>>>>>>>>>>>>>>>>>>>>>>>> GET /path?username=%D3%C3%BB%A7&submit=submit HTTP/1.1 Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, ** Referer: http://localhost:8080/path?username=%D3%C3%BB%A7&submit=submit Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 用戶端發送的請求資訊結束 <<<<<<<<<<<<<<<<<<<<<<<<<< 使用者請求的資源是:/test.jpg 請求的類型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56165,localport=8080]離開了HTTP伺服器 連接配接到伺服器的使用者:Socket[addr=/0:0:0:0:0:0:0:1,port=56164,localport=8080] 用戶端發送的請求資訊: >>>>>>>>>>>>>>>>>>>>>>>>> GET /test.gif HTTP/1.1 Accept: ** Referer: http://localhost:8080/path?username=%D3%C3%BB%A7&submit=submit Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Content-Type: multipart/form-data; boundary=---------------------------7d91c380444 Accept-Encoding: gzip, deflate Host: localhost:8080 Content-Length: 591 Connection: Keep-Alive Cache-Control: no-cache -----------------------------7d91c380444 Content-Disposition: form-data; name="file1"; filename="file1.txt" Content-Type: text/plain 123 -----------------------------7d91c380444 Content-Disposition: form-data; name="file2"; filename="file2.txt" Content-Type: text/plain 這是第二個測試檔案的内容: 中a [email protected]#$%^&*()_+{}|:\" <>?`-=[]\\;',./ -----------------------------7d91c380444 Content-Disposition: form-data; name="username" 使用者 -----------------------------7d91c380444 Content-Disposition: form-data; name="submit" submit -----------------------------7d91c380444-- 用戶端發送的請求資訊結束 <<<<<<<<<<<<<<<<<<<<<<<<<< 使用者請求的資源是:/path?qryParm=POST URL查詢參數 請求的類型是: POST Socket[addr=/0:0:0:0:0:0:0:1,port=56168,localport=8080]離開了HTTP伺服器 連接配接到伺服器的使用者:Socket[addr=/0:0:0:0:0:0:0:1,port=56169,localport=8080] 用戶端發送的請求資訊: >>>>>>>>>>>>>>>>>>>>>>>>> GET /test.gif HTTP/1.1 Accept: ** Referer: http://localhost:8080/path?qryParm=POST URL查詢參數 Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 用戶端發送的請求資訊結束 <<<<<<<<<<<<<<<<<<<<<<<<<< 使用者請求的資源是:/test.jpg 請求的類型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56170,localport=8080]離開了HTTP伺服器 連接配接到伺服器的使用者:Socket[addr=/0:0:0:0:0:0:0:1,port=56171,localport=8080] 用戶端發送的請求資訊: >>>>>>>>>>>>>>>>>>>>>>>>> GET /download HTTP/1.1 Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Referer: http://localhost:8080/path?qryParm=POST URL查詢參數 Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive 用戶端發送的請求資訊結束 <<<<<<<<<<<<<<<<<<<<<<<<<< 使用者請求的資源是:/download 請求的類型是: GET Socket[addr=/0:0:0:0:0:0:0:1,port=56171,localport=8080]離開了HTTP伺服器 |