一、首先來看http的報文結構
1、請求報文
一個HTTP請求封包由請求行(request line)、請求頭部(header)、空白行和請求資料4個部分組成,下圖給出了請求封包的一般格式。
(1)請求行
請求行由請求方法欄位、URL欄位和HTTP協定版本欄位3個欄位組成,它們用空格分隔。例如,GET /index.html HTTP/1.1。
HTTP協定的請求方法有GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT。這裡介紹最常用的GET方法和POST方法。
GET:當客戶端要從伺服器讀取文件時,使用GET方法。 GET方法要求伺服器將URL定位的資源放在回應封包的資料部分,並回給客戶端。使用GET方法時,請求參數和對應的值附加在URL後面,利用一個問號(“?”)代表URL的結尾與請求參數的開始,傳遞參數長度受限。例如,/index.jsp?id=100&op=bind。
POST:當客戶端提供更多資訊時可以使用POST方法。 POST方法將請求參數封裝在HTTP請求資料中,以名稱/值的形式出現,可以傳輸大量資料。
(2)請求頭部
請求頭部由關鍵字/值對組成,每行一對,關鍵字和值以英文冒號「:」分隔。請求頭部通知伺服器有關於客戶端請求的信息,典型的請求頭有:
User-Agent:產生請求的瀏覽器類型。
Accept:客戶端可識別的內容類型清單。
Host:請求的主機名,允許多個網域同處一個IP位址,即虛擬主機。
(3)空行
最後一個請求頭之後是一個空行,發送回車符和換行符,通知伺服器以下不再有請求頭。
(4)請求數據
請求資料不在GET方法中使用,而是在POST方法中使用。 POST方法適用於需要客戶填寫表單的場合。與請求資料相關的最常使用的請求頭是Content-Type和Content-Length。
2、響應報文
回應報文的格式大致上和請求報文類似,只是第一行有所不同,讀者可以自己在網路上找這方面的介紹,這裡不再贅述。
二、程序的實現
程式的實作步驟如下:
1、接收客戶端瀏覽器的請求;
2、建立一個新的線程,用於處理該次請求;
3.讀取報文數據,判斷報文正確與否,分析報文內容;
4.建立回應報文,發送到客戶端;
5.結束該處理線程,處理其他客戶請求;
程式碼如下:
view plaincopy to clipboardprint?
import java.net.*;
import java.io.*;
import java.util.*;
import java.lang.*;
public class WebServer {
public static void main(String [] args){
int port;
ServerSocket server_socket;
try{
port=Integer.parseInt(args[0]);
}
catch (Exception e){
port=8080;
}
try{
server_socket=new ServerSocket(port);
System.out.println("WebServer running on port"+server_socket.getLocalPort());
while(true){
Socket socket=server_socket.accept();
System.out.println("New connection accepted"+socket.getInetAddress()+":"+socket.getPort());
//針對特定的請求建立處理該請求的執行緒
try{
httpRequestHandler request=new httpRequestHandler(socket);
Thread thread=new Thread(request);
thread.start();
}
catch(Exception e){
System.out.println(e);
}
}
}
catch(IOException e){
System.out.println(e);
}
}
}
//處理請求的線程類
class httpRequestHandler implements Runnable{
final static String CRLF="rn";
Socket socket;
InputStream input;
OutputStream output;
BufferedReader br;
//判斷請求的檔案類型是否正確
boolean fileType=true;
//初始化參數
public httpRequestHandler(Socket socket) throws Exception{
this.socket=socket;
this.input=socket.getInputStream();
this.output=socket.getOutputStream();
this.br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
//啟動該執行緒
public void run(){
try{
processRequest();
}
catch(Exception e){
System.out.println(e);
}
}
//處理請求的核心函數
private void processRequest() throws Exception{
while(true){
String headerLine=br.readLine();
System.out.println("the client request is"+headerLine);
if(headerLine.equals(CRLF)||headerLine.equals(""))
break;
StringTokenizer s=new StringTokenizer(headerLine);
String temp=s.nextToken();
if(temp.equals("GET")){
String fileName=s.nextToken();
fileName="."+fileName;
FileInputStream fis=null;
boolean fileExists=true;
if(!(fileName.endsWith(".htm")||fileName.endsWith(".html")))
{
this.fileType=false;
try{
fis=new FileInputStream("error.html");
}
catch(FileNotFoundException e){
fileExists=false;
}
}
else{
try{
fis=new FileInputStream(fileName);
}
catch(FileNotFoundException e){
fileExists=false;
}
}
String serverLine="Server:a simple java WebServer";
String statusLine=null;
String contentTypeLine=null;
String entityBody=null;
String contentLengthLine="error";
if(fileExists&&this.fileType){
statusLine="HTTP/1.0 200 OK"+CRLF;
contentTypeLine="Content-type:"+this.contentType(fileName)+CRLF;
contentLengthLine="Content-Length:"+(new Integer(fis.available())).toString()+CRLF;
}
else{
if(fileExists&&this.fileType==false){
statusLine="HTTP/1.0 400 BadRequest"+CRLF;
contentTypeLine="text/html";
entityBody="<HTML>400 Not BadRequest</TITLE></HEAD>"+
"<BODY>400 BadRequest"+
"<br>usage:http://yourHostName:port/"+
"fileName.html</BODY></HTML>";
}
else if(fileExists==false){
statusLine="HTTP/1.0 404 Not Found"+CRLF;
contentTypeLine="text/html";
entityBody="<HTML>404 Not Found</TITLE></HEAD>"+
"<BODY>404 Not Found"+
"<br>usage:http://yourHostName:port/"+
"fileName.html</BODY></HTML>";
}
}
output.write(statusLine.getBytes());
output.write(serverLine.getBytes());
output.write(contentTypeLine.getBytes());
output.write(contentLengthLine.getBytes());
output.write(CRLF.getBytes());
if(fileExists&&this.fileType){
sendBytes(fis,output);
fis.close();
}
else{
output.write(entityBody.getBytes());
}
}
}
try{
output.close();
br.close();
socket.close();
}
catch(Exception e){}
}
//將客戶端請求的頁面傳送出去
private static void sendBytes(FileInputStream fis,OutputStream os) throws Exception{
byte[] buffer=new byte[1024];
int bytes=0;
while((bytes=fis.read(buffer))!=-1){
os.write(buffer,0,bytes);
}
}
//設定contentType的內容
private static String contentType(String fileName){
if(fileName.endsWith(".htm")||fileName.endsWith(".html")){
return "text/html";
}
return "fileName";
}