這是我的第一篇文章。和眾多程序員一樣,非常希望去分享自己的知識(shí)。但是萬(wàn)事開(kāi)頭難,也可能是我自己水平的問(wèn)題,真是想不到要從什么內(nèi)容開(kāi)始。于是趁著有時(shí)間,就自己搗鼓了一下HTTP服務(wù)器。后來(lái)想想干脆就以自己手寫(xiě)一個(gè)簡(jiǎn)單的HTTP服務(wù)器的教程來(lái)開(kāi)始我自己的寫(xiě)作吧。本文中不足和錯(cuò)誤的地方請(qǐng)各位看客多多指教。下面就開(kāi)始吧。
HTTP簡(jiǎn)介
當(dāng)用戶在瀏覽器中輸入一個(gè)指向特定網(wǎng)頁(yè)的URL地址時(shí),瀏覽器就會(huì)生成一個(gè)HTTP請(qǐng)求,因?yàn)闉g覽器需要將HTTP請(qǐng)求發(fā)送的服務(wù)器,所以這時(shí)瀏覽器會(huì)與服務(wù)器建立TCP連接,當(dāng)TCP可靠連接建立之后,瀏覽器會(huì)將生成的HTTP請(qǐng)求發(fā)送到服務(wù)器端。這時(shí)服務(wù)器程序接收到了信息將要去識(shí)別這個(gè)信息的內(nèi)容。服務(wù)器程序識(shí)別之后是一個(gè)HTTP的請(qǐng)求。就調(diào)用相應(yīng)的服務(wù)程序,經(jīng)過(guò)服務(wù)程序的分析和處理之后服務(wù)器端知道需要返回什么內(nèi)容給瀏覽器了。當(dāng)服務(wù)器返回了內(nèi)容給瀏覽器后,這時(shí)瀏覽器與服務(wù)器之間的數(shù)據(jù)交換完畢,這時(shí)TCP可靠連接就會(huì)斷開(kāi)。如果用戶希望訪問(wèn)新的網(wǎng)頁(yè),瀏覽器就必須再次建立與服務(wù)器的連接了。

開(kāi)始之前我們準(zhǔn)備好工具
- java環(huán)境(JDK1.7)
- eclipse
- chrome瀏覽器
開(kāi)始
使用eclipse建立項(xiàng)目結(jié)構(gòu)如圖
HTTPServer.java為程序代碼
abc.html為需要請(qǐng)求的html頁(yè)面

HTTPServer.java 代碼
package server;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class HTTPServer {
public static void main(String[] args) {
int port;
ServerSocket serverSocket;
try{
port=Integer.parseInt(args[0]);
}catch(Exception e){
System.out.println("port=8080(默認(rèn))");
port=8080;
}
try {
serverSocket=new ServerSocket(port);
System.out.println("服務(wù)器正在監(jiān)聽(tīng)端口:"+serverSocket.getLocalPort());
while(true){
Socket socket=serverSocket.accept(); //沒(méi)有時(shí)會(huì)阻塞 程序停在此處
System.out.println("建立了與客戶的一個(gè)新的TCP連接,該客戶的地址為:"+socket.getInetAddress()+":"+socket.getPort());
try {
service(socket);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void service(Socket socket) throws Exception{
InputStream socketIn=socket.getInputStream();
Thread.sleep(500);
int size=socketIn.available();
byte[] buffer=new byte[size];
socketIn.read(buffer);
String request=new String(buffer);
System.out.println(request);
//獲取HTTP請(qǐng)求的第一行
String firstLineOfRequest=request.substring(0,request.indexOf("\r\n"));
System.out.println("http請(qǐng)求的第一行:"+firstLineOfRequest);
//解析HTTP請(qǐng)求的第一行
String[] parts=firstLineOfRequest.split(" ");
//獲取HTTP請(qǐng)求中的URI
String uri=parts[1];
/*決定HTTP響應(yīng)正文的類型*/
String contentType;
if(uri.indexOf("html")!=-1||uri.indexOf("htm")!=-1)
contentType="text/html";
else if(uri.indexOf("jpg")!=-1||uri.indexOf("jpeg")!=-1)
contentType="image/jpeg";
else if(uri.indexOf("gif")!=-1)
contentType="image/gif";
else
contentType="application/octet-stream"; //字節(jié)流類型
/*創(chuàng)建HTTP響應(yīng)結(jié)果*/
//HTTP響應(yīng)的第一行
String responseFirstLine="HTTP/1.1 200 OK\r\n";
String responseHeader="Content-type:"+contentType+"\r\n\r\n";
InputStream in=HTTPServer.class.getResourceAsStream("/server"+uri);
/*發(fā)送HTTP響應(yīng)結(jié)果*/
OutputStream socketOut=socket.getOutputStream();
socketOut.write(responseFirstLine.getBytes());
socketOut.write(responseHeader.getBytes());
//發(fā)送正文
int len=0;
buffer=new byte[128];
while((len=in.read(buffer))!=-1)
socketOut.write(buffer,0,len);
Thread.sleep(1000);
socket.close();
}
}
abc.html
this Html
程序默認(rèn)監(jiān)聽(tīng)端口8080
在一般情況下accept()函數(shù)是阻塞式的,當(dāng)沒(méi)有socket連接的時(shí)候本線程是停在此處的。
當(dāng)有一個(gè)TCP請(qǐng)求建立連接時(shí)這時(shí)服務(wù)器程序的accept()返回一個(gè)Socket對(duì)象。這個(gè)Socket對(duì)象就是在本程序中充當(dāng)客戶端的角色對(duì)象??梢詮拇藗€(gè)對(duì)象中獲取輸入流讀取信息進(jìn)來(lái),也可以通過(guò)輸出流返回信息給客戶端。
這里我們通過(guò)瀏覽器進(jìn)行測(cè)試
將這個(gè)程序跑起來(lái)
我們使用瀏覽器進(jìn)行請(qǐng)求http://localhost:8080/abc.html 請(qǐng)求自己服務(wù)器的abc.html

查看控制臺(tái)打印出來(lái)的內(nèi)容
port=8080(默認(rèn))
服務(wù)器正在監(jiān)聽(tīng)端口:8080
建立了與客戶的一個(gè)新的TCP連接,該客戶的地址為:/0:0:0:0:0:0:0:1:52085
GET /abc.html HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
http請(qǐng)求的第一行:GET /abc.html HTTP/1.1
從控制臺(tái)打印結(jié)果中我們可以看到HTTP協(xié)議的大致結(jié)構(gòu)
- 請(qǐng)求行
- 消息報(bào)頭
- 請(qǐng)求正文
請(qǐng)求行中包括了本次HTTP協(xié)議是什么方式,以及uri定位地址在哪里 和一些關(guān)于HTTP規(guī)范版本的信息。
結(jié)語(yǔ)
文章內(nèi)容比較簡(jiǎn)單基礎(chǔ),但這是關(guān)于底層的基礎(chǔ)內(nèi)容。我想這些機(jī)理還是很重要的,需要牢記于心,以便于掌控。這樣才能在碰到問(wèn)題的時(shí)候迅速解決問(wèn)題。
哈哈
謝謝大家閱讀!