HTTP是基于TCP協議的。TCP負責數據傳輸,而HTTP只是規范了TCP傳輸的數據的格式,而這個具體的格式,請見后面給出的資料。
HTTP服務的底層實現就是socket編程。
下面基于socket編寫一個簡單的HTTP server。
~~~
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
class SocketHandler implements Runnable {
final static String CRLF = "\r\n"; // 1
private Socket clientSocket;
public SocketHandler(Socket clientSocket) {
this.clientSocket = clientSocket;
}
public void handleSocket(Socket clientSocket) throws IOException {
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream())
);
PrintWriter out = new PrintWriter(
new BufferedWriter( new OutputStreamWriter(clientSocket.getOutputStream())),
true
);
String requestHeader = "";
String s;
while ((s = in.readLine()) != null) {
s += CRLF; // 2 很重要,默認情況下in.readLine的結果中`\r\n`被去掉了
requestHeader = requestHeader + s;
if (s.equals(CRLF)){ // 3 此處HTTP請求頭我們都得到了;如果從請求頭中判斷有請求正文,則還需要繼續獲取數據
break;
}
}
System.out.println("客戶端請求頭:");
System.out.println(requestHeader);
String responseBody = "客戶端的請求頭是:\n"+requestHeader;
String responseHeader = "HTTP/1.0 200 OK\r\n" +
"Content-Type: text/plain; charset=UTF-8\r\n" +
"Content-Length: "+responseBody.getBytes().length+"\r\n" +
"\r\n";
// 4 問題來了:1、瀏覽器如何探測編碼 2、瀏覽器受到content-length后會按照什么方式判斷?漢字的個數?字節數?
System.out.println("響應頭:");
System.out.println(responseHeader);
out.write(responseHeader);
out.write(responseBody);
out.flush();
out.close();
in.close();
clientSocket.close();
}
@Override
public void run() {
try {
handleSocket(clientSocket);
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
public class MyHTTPServer {
public static void main(String[] args) throws Exception {
int port = 8000;
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("啟動服務,綁定端口: " + port);
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(30); // 5
while (true) { // 6
Socket clientSocket = serverSocket.accept();
System.out.println("新的連接"
+ clientSocket.getInetAddress() + ":" + clientSocket.getPort());
try {
fixedThreadPool.execute(new SocketHandler(clientSocket));
} catch (Exception e) {
System.out.println(e);
}
}
}
}
~~~
這是一個實現HTTP 1.0的服務器,對于所有的HTTP請求,會把HTTP請求頭響應回去。 這個程序說明了web服務器處理請求的基本流程,JSP、Servlet、Spring MVC等只是在 這個基礎上嫁了許多方法,以讓我們更方面的編寫web應用。web服務器不僅可以基于多線程, 也可以基于多進程、Reactor模型等。
**測試程序:**
運行上面的程序。我們使用curl訪問`http://127.0.0.1`(也可以使用瀏覽器):
~~~
$ curl -i http://127.0.0.1:8000
HTTP/1.0 200 OK
Content-Type: text/plain; charset=UTF-8
Content-Length: 106
客戶端的請求頭是:
GET / HTTP/1.1
User-Agent: curl/7.35.0
Host: 127.0.0.1:8000
Accept: */*
~~~
Java程序輸出:
~~~
啟動服務,綁定端口: 8000
新的連接/127.0.0.1:36463
新的連接/127.0.0.1:36463客戶端請求頭:
GET / HTTP/1.1
User-Agent: curl/7.35.0
Host: 127.0.0.1:8000
Accept: */*
響應頭:
HTTP/1.0 200 OK
Content-Type: text/plain; charset=UTF-8
Content-Length: 106
~~~
**程序解析:**
`// 1`:定義了HTTP頭的換行符。
`// 2`:in.readLine()的結果默認不帶換行符,這里把它加上。(這不是強制的,主要看你的程序邏輯需不需要, 這個程序的目標是把HTTP請求頭響應回去)。
`// 3`:此時s是一個空行,根據HTTP協議,整個請求頭都得到了。
`// 4`:Content-Length的值是字節的數量。
`// 5`:線程池。
`// 6`:這個循環不停監聽socket連接,使用SocketHandler處理連入的socket,而這個處理是放在線程池中的。
**HTTP 1.1:**
HTTP 1.1也是在這個思路的基礎上實現的,即多個HTTP請求都在一個TCP連接中傳輸。對于HTTP 1.1,如何區分出每個HTTP請求很重要, 比較簡單的可以是用過`Content-Length`判斷一條請求是否結束。如果一個HTTP請求數據較多,往往采用Chunked方式, 可以參考[Chunked transfer encoding](https://en.wikipedia.org/wiki/Chunked_transfer_encoding)。
## [](https://github.com/someus/another-tutorial-about-java-web/blob/master/00-02.md#http相關資料)HTTP相關資料
**網絡教程:**
[菜鳥教程-HTTP教程](http://www.runoob.com/http/http-tutorial.html)
[List of HTTP header fields](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields)
[14 Header Field Definitions](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html)
[HTTP header should use what character encoding?](http://stackoverflow.com/questions/4400678/http-header-should-use-what-character-encoding)
**書籍:**
[HTTP權威指南](https://github.com/someus/another-tutorial-about-java-web/blob/master)?[計算機網絡](https://github.com/someus/another-tutorial-about-java-web/blob/master)?謝希仁HTTP是基于TCP協議的。TCP負責數據傳輸,而HTTP只是規范了TCP傳輸的數據的格式,而這個具體的格式,請見后面給出的資料。
HTTP服務的底層實現就是socket編程。
下面基于socket編寫一個簡單的HTTP server。
~~~
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
class SocketHandler implements Runnable {
final static String CRLF = "\r\n"; // 1
private Socket clientSocket;
public SocketHandler(Socket clientSocket) {
this.clientSocket = clientSocket;
}
public void handleSocket(Socket clientSocket) throws IOException {
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream())
);
PrintWriter out = new PrintWriter(
new BufferedWriter( new OutputStreamWriter(clientSocket.getOutputStream())),
true
);
String requestHeader = "";
String s;
while ((s = in.readLine()) != null) {
s += CRLF; // 2 很重要,默認情況下in.readLine的結果中`\r\n`被去掉了
requestHeader = requestHeader + s;
if (s.equals(CRLF)){ // 3 此處HTTP請求頭我們都得到了;如果從請求頭中判斷有請求正文,則還需要繼續獲取數據
break;
}
}
System.out.println("客戶端請求頭:");
System.out.println(requestHeader);
String responseBody = "客戶端的請求頭是:\n"+requestHeader;
String responseHeader = "HTTP/1.0 200 OK\r\n" +
"Content-Type: text/plain; charset=UTF-8\r\n" +
"Content-Length: "+responseBody.getBytes().length+"\r\n" +
"\r\n";
// 4 問題來了:1、瀏覽器如何探測編碼 2、瀏覽器受到content-length后會按照什么方式判斷?漢字的個數?字節數?
System.out.println("響應頭:");
System.out.println(responseHeader);
out.write(responseHeader);
out.write(responseBody);
out.flush();
out.close();
in.close();
clientSocket.close();
}
@Override
public void run() {
try {
handleSocket(clientSocket);
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
public class MyHTTPServer {
public static void main(String[] args) throws Exception {
int port = 8000;
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("啟動服務,綁定端口: " + port);
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(30); // 5
while (true) { // 6
Socket clientSocket = serverSocket.accept();
System.out.println("新的連接"
+ clientSocket.getInetAddress() + ":" + clientSocket.getPort());
try {
fixedThreadPool.execute(new SocketHandler(clientSocket));
} catch (Exception e) {
System.out.println(e);
}
}
}
}
~~~
這是一個實現HTTP 1.0的服務器,對于所有的HTTP請求,會把HTTP請求頭響應回去。 這個程序說明了web服務器處理請求的基本流程,JSP、Servlet、Spring MVC等只是在 這個基礎上嫁了許多方法,以讓我們更方面的編寫web應用。web服務器不僅可以基于多線程, 也可以基于多進程、Reactor模型等。
**測試程序:**
運行上面的程序。我們使用curl訪問`http://127.0.0.1`(也可以使用瀏覽器):
~~~
$ curl -i http://127.0.0.1:8000
HTTP/1.0 200 OK
Content-Type: text/plain; charset=UTF-8
Content-Length: 106
客戶端的請求頭是:
GET / HTTP/1.1
User-Agent: curl/7.35.0
Host: 127.0.0.1:8000
Accept: */*
~~~
Java程序輸出:
~~~
啟動服務,綁定端口: 8000
新的連接/127.0.0.1:36463
新的連接/127.0.0.1:36463客戶端請求頭:
GET / HTTP/1.1
User-Agent: curl/7.35.0
Host: 127.0.0.1:8000
Accept: */*
響應頭:
HTTP/1.0 200 OK
Content-Type: text/plain; charset=UTF-8
Content-Length: 106
~~~
**程序解析:**
`// 1`:定義了HTTP頭的換行符。
`// 2`:in.readLine()的結果默認不帶換行符,這里把它加上。(這不是強制的,主要看你的程序邏輯需不需要, 這個程序的目標是把HTTP請求頭響應回去)。
`// 3`:此時s是一個空行,根據HTTP協議,整個請求頭都得到了。
`// 4`:Content-Length的值是字節的數量。
`// 5`:線程池。
`// 6`:這個循環不停監聽socket連接,使用SocketHandler處理連入的socket,而這個處理是放在線程池中的。
**HTTP 1.1:**
HTTP 1.1也是在這個思路的基礎上實現的,即多個HTTP請求都在一個TCP連接中傳輸。對于HTTP 1.1,如何區分出每個HTTP請求很重要, 比較簡單的可以是用過`Content-Length`判斷一條請求是否結束。如果一個HTTP請求數據較多,往往采用Chunked方式, 可以參考[Chunked transfer encoding](https://en.wikipedia.org/wiki/Chunked_transfer_encoding)。
## [](https://github.com/someus/another-tutorial-about-java-web/blob/master/00-02.md#http相關資料)HTTP相關資料
**網絡教程:**
[菜鳥教程-HTTP教程](http://www.runoob.com/http/http-tutorial.html)
[List of HTTP header fields](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields)
[14 Header Field Definitions](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html)
[HTTP header should use what character encoding?](http://stackoverflow.com/questions/4400678/http-header-should-use-what-character-encoding)
**書籍:**
[HTTP權威指南](https://github.com/someus/another-tutorial-about-java-web/blob/master)?[計算機網絡](https://github.com/someus/another-tutorial-about-java-web/blob/master)?謝希仁
- JSP & Servlet
- 00-00、序
- 00-01、相關軟件的安裝
- 00-02、理解HTTP
- 00-03、從JSP開始
- 00-04、理解Servlet
- 00-05、過濾器與監聽器
- 00-06、使用velocity模板引擎
- 00-07、使用數據庫連接池
- 00-08、Tomcat的運行機制
- Spring MVC
- 01-00、Spring與依賴注入
- 01-01、Spring與面向切面編程
- 01-02、使用Spring MVC構建Hello World
- 01-03、JdbcTemplate
- 01-04、基于注解的URL映射
- 01-05、JSON
- 01-06、校驗器
- 01-07、國際化
- 01-08、攔截器
- 01-09、文件上傳
- 01-10、轉換器與格式化
- Book
- Online Tutorial
- Q & A
- Learn More
- Supplement