這個程式改寫自O'reilly的Java網路程式設計書中的jhttp.java程式,我將啟動的介面作了一些改變,並服務客戶端的執行緒部份獨立出來,使這個程式的服務類別更具一般性,不受限制於伺服器介面的管理方式。
這個程式可以判別客戶端請求的資源型態,並傳回適當的標頭,因為這是一個最簡單的HTTP伺服器,它不處理客戶端的標頭,並只接受基本的GET方法。
基本上這個程式一點都不難,執行緒與Socket的使用都很基本,只要您稍微懂得HTTP協定,都可以看得懂這個程式,有興趣的可以參照HTTP協定繼續完成功能更豐富的HTTP伺服器。
以下為程式碼內容:
package onlyfun.caterpillar;
import java.io.*; import java.net.*; import java.util.Date;
class HttpClient implements Runnable { private Socket clientSkt; private File docRoot; private String defaultFile;;
public HttpClient(Socket s, String root, String indexFile) { clientSkt = s; docRoot = new File(root); defaultFile = indexFile; }
// 根據要求的檔名判斷傳回型態 public String returnContentType(String fileName) { if(fileName.endsWith(".html") || fileName.endsWith(".htm")) return "text/html"; // HTML網頁 else if(fileName.endsWith(".txt") || fileName.endsWith(".java")) return "text/plain"; // 文字型態 else if(fileName.endsWith(".gif")) return "image/gif"; // GIF圖檔 else if(fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) return "image/jpeg"; // JPEG圖檔 else if(fileName.endsWith(".class")) return "application/octec-stream"; // 二進位應用?{式檔案 else // 其它不可判別的檔案一律視為文字檔型態 return "text/plain"; }
public void run() { String request; // 要求的方法 String contentType; // 檔案型態 String httpVersion = ""; // HTTP協定版本 File requestedFile; // 要求的檔案
try { PrintStream printStream = new PrintStream( clientSkt.getOutputStream()); BufferedReader clientReader = new BufferedReader( new InputStreamReader(clientSkt.getInputStream()));
String get = clientReader.readLine(); // 分離Request的內容 String[] tokens = get.split("[ \t]"); request = tokens[0];
if(request.equals("GET")) { String file = tokens[1];
if(file.endsWith("/")) // GET / file += defaultFile; contentType = returnContentType(file);
if(tokens.length >= 3) { httpVersion = tokens[2]; }
// 我們不處理客戶端的標頭 while((get = clientReader.readLine()) != null) { if(get.trim().equals("")) break; }
try { requestedFile = new File( docRoot, file.substring(1, file.length())); FileInputStream fileInputStream = new FileInputStream(requestedFile);
// 讀入請求的檔案 int fileLength = (int)requestedFile.length(); byte[] requestedData = new byte[fileLength]; fileInputStream.read(requestedData); fileInputStream.close();
// 寫出標頭至客戶端 if(httpVersion.startsWith("HTTP/")) { printStream.print("HTTP/1.0 200 OK\r\n"); Date now = new Date(); printStream.print("Date: " + now + "\r\n"); printStream.print("Server: TinyHttpd v0.1\r\n"); printStream.print( "Content-length: " + fileLength + "\r\n"); printStream.print( "Content-type: " + contentType + "\r\n\r\n"); }
// 將檔案傳給客戶端 printStream.write(requestedData); printStream.close(); } catch(IOException e) { // 找不到檔案 if(httpVersion.startsWith("HTTP/")) { printStream.print("HTTP/1.0 404 File Not Found\r\n"); Date now = new Date(); printStream.print("Date: " + now + "\r\n"); printStream.print("Server: TinyHttpd v0.1\r\n"); printStream.print("Content-type: text/html\r\n\r\n"); } }
// 顯示錯誤訊息網頁 printStream.println( "<HTML><HEAD><TITLE>File Not Found</TITLE></HEAD>" + "<BODY><H1>HTTP Error 404: File Not Found" + "</H1></BODY></HTML>"); printStream.close(); } else { // 客戶端請求了一個沒有實作的方法,例如POST等 if(httpVersion.startsWith("HTTP/")) { printStream.print("HTTP/1.0 501 Not Implemented\r\n"); Date now = new Date(); printStream.print("Date: " + now + "\r\n"); printStream.print("Server: TinyHttpd v1.0\r\n"); printStream.print("Content-type: text/html\r\n\r\n"); } } printStream.println( "<HTML><HEAD><TITLE>Not Implemented</TITLE></HEAD>" + "<BODY><H1>HTTP Error 501: Not Implemented" + "</H1></BODY></HTML>"); printStream.close(); } catch(IOException e) { e.printStackTrace(); }
try { clientSkt.close(); } catch(IOException e) { e.printStackTrace(); } } }
package onlyfun.caterpillar;
import java.io.*; import java.net.*;
public class SimpleHttpServer { public static void main(String args[]) { String docRoot; String indexFile; int port;
ServerSocket serverSkt; Socket clientSkt;
try { BufferedReader buf = new BufferedReader(new InputStreamReader(System.in)); System.out.println("TinyHttpd v0.1"); System.out.print("伺服器文件根目錄: "); docRoot = buf.readLine(); System.out.print("索引文件: "); indexFile = buf.readLine(); System.out.print("伺服器連接埠: "); port = Integer.parseInt(buf.readLine());
if(port < 0 || port > 65535) { port = 80; } } catch(Exception e) { System.err.println("錯誤: " + e.toString()); System.out.print("採用內定選項"); docRoot = "/var/www/html/"; indexFile = "index.html"; port = 80; System.out.println("文件根目錄: " + docRoot + "\n索引文件: " + indexFile + "\n連接埠: " + port); }
try { serverSkt = new ServerSocket(port); System.out.println("傾聽客戶端於 " + serverSkt.getLocalPort() + " 連接埠....");
while(true) { clientSkt = serverSkt.accept(); System.out.println("客戶端連線: " + clientSkt.getInetAddress());
// 啟動一個客戶端執行緒 Thread clientThread = new Thread( new HttpClient(clientSkt, docRoot, indexFile)); clientThread.start(); } } catch(IOException e) { e.printStackTrace(); } } }
|
|