這是我的第一篇文章殴穴。和眾多程序員一樣,非常希望去分享自己的知識。但是萬事開頭難采幌,也可能是我自己水平的問題劲够,真是想不到要從什么內容開始。于是趁著有時間休傍,就自己搗鼓了一下HTTP服務器征绎。后來想想干脆就以自己手寫一個簡單的HTTP服務器的教程來開始我自己的寫作吧。本文中不足和錯誤的地方請各位看客多多指教磨取。下面就開始吧人柿。
HTTP簡介
當用戶在瀏覽器中輸入一個指向特定網頁的URL地址時,瀏覽器就會生成一個HTTP請求忙厌,因為瀏覽器需要將HTTP請求發(fā)送的服務器凫岖,所以這時瀏覽器會與服務器建立TCP連接,當TCP可靠連接建立之后逢净,瀏覽器會將生成的HTTP請求發(fā)送到服務器端哥放。這時服務器程序接收到了信息將要去識別這個信息的內容。服務器程序識別之后是一個HTTP的請求汹胃。就調用相應的服務程序婶芭,經過服務程序的分析和處理之后服務器端知道需要返回什么內容給瀏覽器了。當服務器返回了內容給瀏覽器后着饥,這時瀏覽器與服務器之間的數(shù)據(jù)交換完畢,這時TCP可靠連接就會斷開惰赋。如果用戶希望訪問新的網頁宰掉,瀏覽器就必須再次建立與服務器的連接了。
開始之前我們準備好工具
- java環(huán)境(JDK1.7)
- eclipse
- chrome瀏覽器
開始
使用eclipse建立項目結構如圖
HTTPServer.java為程序代碼
abc.html為需要請求的html頁面
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(默認)");
port=8080;
}
try {
serverSocket=new ServerSocket(port);
System.out.println("服務器正在監(jiān)聽端口:"+serverSocket.getLocalPort());
while(true){
Socket socket=serverSocket.accept(); //沒有時會阻塞 程序停在此處
System.out.println("建立了與客戶的一個新的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請求的第一行
String firstLineOfRequest=request.substring(0,request.indexOf("\r\n"));
System.out.println("http請求的第一行:"+firstLineOfRequest);
//解析HTTP請求的第一行
String[] parts=firstLineOfRequest.split(" ");
//獲取HTTP請求中的URI
String uri=parts[1];
/*決定HTTP響應正文的類型*/
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響應結果*/
//HTTP響應的第一行
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響應結果*/
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
程序默認監(jiān)聽端口8080
在一般情況下accept()函數(shù)是阻塞式的轨奄,當沒有socket連接的時候本線程是停在此處的。
當有一個TCP請求建立連接時這時服務器程序的accept()返回一個Socket對象拒炎。這個Socket對象就是在本程序中充當客戶端的角色對象挪拟。可以從此個對象中獲取輸入流讀取信息進來击你,也可以通過輸出流返回信息給客戶端玉组。
這里我們通過瀏覽器進行測試
將這個程序跑起來
我們使用瀏覽器進行請求http://localhost:8080/abc.html 請求自己服務器的abc.html
查看控制臺打印出來的內容
port=8080(默認)
服務器正在監(jiān)聽端口:8080
建立了與客戶的一個新的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請求的第一行:GET /abc.html HTTP/1.1
從控制臺打印結果中我們可以看到HTTP協(xié)議的大致結構
- 請求行
- 消息報頭
- 請求正文
請求行中包括了本次HTTP協(xié)議是什么方式丁侄,以及uri定位地址在哪里 和一些關于HTTP規(guī)范版本的信息惯雳。
結語
文章內容比較簡單基礎,但這是關于底層的基礎內容鸿摇。我想這些機理還是很重要的石景,需要牢記于心,以便于掌控。這樣才能在碰到問題的時候迅速解決問題潮孽。
哈哈
謝謝大家閱讀揪荣!