實現HTTP伺服器的三種方法
阿新 • • 發佈:2019-01-09
1、使用socket簡單實現http協議:
- import java.io.*;
- import java.net.*;
- /**
- * MyHttpServer 實現一個簡單的HTTP伺服器端,可以獲取使用者提交的內容
- * 並給使用者一個response
- * 因為時間的關係,對http頭的處理顯得不規範
- * 對於上傳附件,暫時只能解析只上傳一個附件而且附件位置在第一個的情況
- * 轉載請註明來自http://blog.csdn.net/sunxing007
- *
- * http://blog.csdn.net/sunxing007/article/details/4305956
- * **/
- publicclass
- //伺服器根目錄,post.html, upload.html都放在該位置
- publicstatic String WEB_ROOT = "c:/root";
- //埠
- privateint port;
- //使用者請求的檔案的url
- private String requestPath;
- //mltipart/form-data方式提交post的分隔符,
- private String boundary = null;
- //post提交請求的正文的長度
- privateint contentLength = 0;
- public MyHttpServer(String root, int port) {
- WEB_ROOT = root;
- this.port = port;
- requestPath = null;
- }
- //處理GET請求
- privatevoid doGet(DataInputStream reader, OutputStream out) throws Exception {
- if (new File(WEB_ROOT + this.requestPath).exists()) {
- //從伺服器根目錄下找到使用者請求的檔案併發送回瀏覽器
- InputStream fileIn = new FileInputStream(WEB_ROOT + this.requestPath);
- byte[] buf = newbyte[fileIn.available()];
- fileIn.read(buf);
- out.write(buf);
- out.close();
- fileIn.close();
- reader.close();
- System.out.println("request complete.");
- }
- }
- //處理post請求
- privatevoid doPost(DataInputStream reader, OutputStream out) throws Exception {
- String line = reader.readLine();
- while (line != null) {
- System.out.println(line);
- line = reader.readLine();
- if ("".equals(line)) {
- break;
- } elseif (line.indexOf("Content-Length") != -1) {
- this.contentLength = Integer.parseInt(line.substring(line.indexOf("Content-Length") + 16));
- }
- //表明要上傳附件, 跳轉到doMultiPart方法。
- elseif(line.indexOf("multipart/form-data")!= -1){
- //得multiltipart的分隔符
- this.boundary = line.substring(line.indexOf("boundary") + 9);
- this.doMultiPart(reader, out);
- return;
- }
- }
- //繼續讀取普通post(沒有附件)提交的資料
- System.out.println("begin reading posted data......");
- String dataLine = null;
- //使用者傳送的post資料正文
- byte[] buf = {};
- int size = 0;
- if (this.contentLength != 0) {
- buf = newbyte[this.contentLength];
- while(size<this.contentLength){
- int c = reader.read();
- buf[size++] = (byte)c;
- }
- System.out.println("The data user posted: " + new String(buf, 0, size));
- }
- //傳送回瀏覽器的內容
- String response = "";
- response += "HTTP/1.1 200 OK/n";
- response += "Server: Sunpache 1.0/n";
- response += "Content-Type: text/html/n";
- response += "Last-Modified: Mon, 11 Jan 1998 13:23:42 GMT/n";
- response += "Accept-ranges: bytes";
- response += "/n";
- String body = "<html><head><title>test server</title></head><body><p>post ok:</p>" + new String(buf, 0, size) + "</body></html>";
- System.out.println(body);
- out.write(response.getBytes());
- out.write(body.getBytes());
- out.flush();
- reader.close();
- out.close();
- System.out.println("request complete.");
- }
- //處理附件
- privatevoid doMultiPart(DataInputStream reader, OutputStream out) throws Exception {
- System.out.println("doMultiPart ......");
- String line = reader.readLine();
- while (line != null) {
- System.out.println(line);
- line = reader.readLine();
- if ("".equals(line)) {
- break;
- } elseif (line.indexOf("Content-Length") != -1) {
- this.contentLength = Integer.parseInt(line.substring(line.indexOf("Content-Length") + 16));
- System.out.println("contentLength: " + this.contentLength);
- } elseif (line.indexOf("boundary") != -1) {
- //獲取multipart分隔符
- this.boundary = line.substring(line.indexOf("boundary") + 9);
- }
- }
- System.out.println("begin get data......");
- /*下面的註釋是一個瀏覽器傳送帶附件的請求的全文,所有中文都是說明性的文字*****
- <HTTP頭部內容略>
- ............
- Cache-Control: no-cache
- <這裡有一個空行,表明接下來的內容都是要提交的正文>
- -----------------------------7d925134501f6<這是multipart分隔符>
- Content-Disposition: form-data; name="myfile"; filename="mywork.doc"
- Content-Type: text/plain
- <附件正文>........................................
- .................................................
- -----------------------------7d925134501f6<這是multipart分隔符>
- Content-Disposition: form-data; name="myname"<其他欄位或附件>
- <這裡有一個空行>
- <其他欄位或附件的內容>
- -----------------------------7d925134501f6--<這是multipart分隔符,最後一個分隔符多兩個->
- ****************************************************************/
- /**
- * 上面的註釋是一個帶附件的multipart型別的POST的全文模型,
- * 要把附件去出來,就是要找到附件正文的起始位置和結束位置
- * **/
- if (this.contentLength != 0) {
- //把所有的提交的正文,包括附件和其他欄位都先讀到buf.
- byte[] buf = newbyte[this.contentLength];
- int totalRead = 0;
- int size = 0;
- while (totalRead < this.contentLength) {
- size = reader.read(buf, totalRead, this.contentLength - totalRead);
- totalRead += size;
- }
- //用buf構造一個字串,可以用字串方便的計算出附件所在的位置
- String dataString = new String(buf, 0, totalRead);
- System.out.println("the data user posted:/n" + dataString);
- int pos = dataString.indexOf(boundary);
- //以下略過4行就是第一個附件的位置
- pos = dataString.indexOf("/n", pos) + 1;
- pos = dataString.indexOf("/n", pos) + 1;
- pos = dataString.indexOf("/n", pos) + 1;
- pos = dataString.indexOf("/n", pos) + 1;
- //附件開始位置
- int start = dataString.substring(0, pos).getBytes().length;
- pos = dataString.indexOf(boundary+"--", pos) - 4;
- //附件結束位置
- int end = dataString.substring(0, pos).getBytes().length;
- //以下找出filename
- int fileNameBegin = dataString.indexOf("filename") + 10;
- int fileNameEnd = dataString.indexOf("/n", fileNameBegin);
- String fileName = dataString.substring(fileNameBegin, fileNameEnd);
- /**
- * 有時候上傳的檔案顯示完整的檔名路徑,比如c:/my file/somedir/project.doc
- * 但有時候只顯示檔案的名字,比如myphoto.jpg.
- * 所以需要做一個判斷。
- */
- if(fileName.lastIndexOf("//")!=-1){
- fileName = fileName.substring(fileName.lastIndexOf("//") + 1);
- }
- fileName = fileName.substring(0, fileName.length()-2);
- OutputStream fileOut = new FileOutputStream("c://" + fileName);
- fileOut.write(buf, start, end-start);
- fileOut.close();
- fileOut.close();
- }
- String response = "";
- response += "HTTP/1.1 200 OK/n";
- response += "Server: Sunpache 1.0/n";
- response += "Content-Type: text/html/n";
- response += "Last-Modified: Mon, 11 Jan 1998 13:23:42 GMT/n";
- response += "Accept-ranges: bytes";
- response += "/n";
- out.write("<html><head><title>test server</title></head><body><p>Post is ok</p></body></html>".getBytes());
- out.flush();
- reader.close();
- System.out.println("request complete.");
- }
- publicvoid service() throws Exception {
- ServerSocket serverSocket = new ServerSocket(this.port);
- System.out.println("server is ok.");
- //開啟serverSocket等待使用者請求到來,然後根據請求的類別作處理
- //在這裡我只針對GET和POST作了處理
- //其中POST具有解析單個附件的能力
- while (true) {
- try {
- Socket socket = serverSocket.accept();
- System.out.println("new request coming.");
- DataInputStream reader = new DataInputStream((socket.getInputStream()));
- String line = reader.readLine();
- if(line!=null){
- String method = line.substring(0, 4).trim();
- OutputStream out = socket.getOutputStream();
- this.requestPath = line.split(" ")[1];
- System.out.println(method);
- if ("GET".equalsIgnoreCase(method)) {
- System.out.println("do get......");
- this.doGet(reader, out);
- } elseif ("POST".equalsIgnoreCase(method)) {
- System.out.println("do post......");
- this.doPost(reader, out);
- }
- }else{
- System.err.println("line is null!");
- }
- socket.close();
- System.out.println("socket closed.");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- publicstaticvoid main(String args[]) throws Exception {
- MyHttpServer server = new MyHttpServer("c:/root", 8080);
- server.service();
- }
- }
2、使用jdk自導的httpserver元件
- import java.io.IOException;
- import java.io.OutputStream;
- import java.net.InetSocketAddress;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Set;
- import java.util.concurrent.Executors;
- import com.sun.net.httpserver.Headers;
- import com.sun.net.httpserver.HttpExchange;
- import com.sun.net.httpserver.HttpHandler;
- import com.sun.net.httpserver.HttpServer;
- //在eclipse中會出現Access restriction: The type Headers is not accessible due to restriction on required library
- //解決辦法:把Windows-Preferences-Java-Complicer- Errors/Warnings裡面的Deprecated and restricted API中的Forbidden references(access rules)選為Warning就可以編譯通過。
- /**
- * 使用jdk自帶sun httpserver元件構建Http伺服器,
- * JDK自帶的HttpServer是一個非常輕量級的Http服務端框架,但是它非常靈活,易於擴充套件,
- * @author Administrator
- *
- */
- publicclass HttpServerDemo {
- publicstaticvoid main(String[] args) throws IOException {
- InetSocketAddress addr = new InetSocketAddress(8080);
- HttpServer server = HttpServer.create(addr, 0);
- server.createContext("/", new MyHandler());
- server.setExecutor(Executors.newCachedThreadPool());
- server.start();
- System.out.println("Server is listening on port 8080");
- }
- }
- class MyHandler implements HttpHandler {
- publicvoid handle(HttpExchange exchange) throws IOException {
- String requestMethod = exchange.getRequestMethod();
- System.out.println("處理新請求:"+requestMethod);
- if (requestMethod.equalsIgnoreCase("GET")) {
- Headers responseHeaders = exchange.getResponseHeaders();
- responseHeaders.set("Content-Type", "text/plain");
- exchange.sendResponseHeaders(200, 0);
- OutputStream responseBody = exchange.getResponseBody();
- Headers requestHeaders = exchange.getRequestHeaders();
- Set<String> keySet = requestHeaders.keySet();
- Iterator<String> iter = keySet.iterator();
- while (iter.hasNext()) {
- String key = iter.next();
- List values = requestHeaders.get(key);
- String s = key + " = " + values.toString() + "\n";
- responseBody.write(s.getBytes());
- }
- responseBody.close();
- }
- }
- }
3、使用apache開源的httpcore元件實現。
- /*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
- package org.apache.http.examples;
- import java.io.File;
- import java.io.IOException;
- import java.io.InterruptedIOException;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.net.URL;
- import java.net.URLDecoder;
- import java.nio.charset.Charset;
- import java.security.KeyStore;
- import java.util.Locale;
- import org.apache.http.ConnectionClosedException;
- import org.apache.http.HttpConnectionFactory;
- import org.apache.http.HttpEntity;
- import org.apache.http.HttpEntityEnclosingRequest;
- import org.apache.http.HttpException;
- import org.apache.http.HttpRequest;
- import org.apache.http.HttpResponse;
- import org.apache.http.HttpServerConnection;
- import org.apache.http.HttpStatus;
- import org.apache.http.MethodNotSupportedException;
- import org.apache.http.entity.ContentType;
- import org.apache.http.entity.FileEntity;
- import org.apache.http.entity.StringEntity;
- import org.apache.http.impl.DefaultBHttpServerConnection;
- import org.apache.http.impl.DefaultBHttpServerConnectionFactory;
- import org.apache.http.protocol.BasicHttpContext;
- import org.apache.http.protocol.HttpContext;
- import org.apache.http.protocol.HttpProcessor;
- import org.apache.http.protocol.HttpProcessorBuilder;
- import org.apache.http.protocol.HttpRequestHandler;
- import org.apache.http.protocol.HttpService;
- import org.apache.http.protocol.ResponseConnControl;
- import org.apache.http.protocol.ResponseContent;
- import org.apache.http.protocol.ResponseDate;
- import org.apache.http.protocol.ResponseServer;
- import org.apache.http.protocol.UriHttpRequestHandlerMapper;
- import org.apache.http.util.EntityUtils;
- import javax.net.ssl.KeyManager;
- import javax.net.ssl.KeyManagerFactory;
- import javax.net.ssl.SSLContext;
- import javax.net.ssl.SSLServerSocketFactory;
- /**
- * Basic, yet fully functional and spec compliant, HTTP/1.1 file server.
- */
- publicclass ElementalHttpServer {
- publicstaticvoid main(String[] args) throws Exception {
- /**
- if (args.length < 1) {
- System.err.println("Please specify document root directory");
- System.exit(1);
- }
- // Document root directory
- String docRoot = args[0];*/
- String docRoot="c:/root";
- int port = 8080;
- if (args.length >= 2) {
- port = Integer.parseInt(args[1]);
- }
- // Set up the HTTP protocol processor
- HttpProcessor httpproc = HttpProcessorBuilder.create()
- .add(new ResponseDate())
- .add(new ResponseServer("Test/1.1"))
- .add(new ResponseContent())
- .add(new ResponseConnControl()).build();
- // Set up request handlers
- UriHttpRequestHandlerMapper reqistry = new UriHttpRequestHandlerMapper();
- reqistry.register("*", new HttpFileHandler(docRoot));
- // Set up the HTTP service
- HttpService httpService = new HttpService(httpproc, reqistry);
- SSLServerSocketFactory sf = null;
- if (port == 8443) {
- // Initialize SSL context
- ClassLoader cl = ElementalHttpServer.class.getClassLoader();
- URL url = cl.getResource("my.keystore");
- if (url == null) {
- System.out.println("Keystore not found");
- System.exit(1);
- }
- KeyStore keystore = KeyStore.getInstance("jks");
- keystore.load(url.openStream(), "secret".toCharArray());
- KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
- KeyManagerFactory.getDefaultAlgorithm());
- kmfactory.init(keystore, "secret".toCharArray());
- KeyManager[] keymanagers = kmfactory.getKeyManagers();
- SSLContext sslcontext = SSLContext.getInstance("TLS");
- sslcontext.init(keymanagers, null, null);
- sf = sslcontext.getServerSocketFactory();
- }
- Thread t = new RequestListenerThread(port, httpService, sf);
- t.setDaemon(false);
- t.start();
- }
- staticclass HttpFileHandler implements HttpRequestHandler {
- privatefinal String docRoot;
- public HttpFileHandler(final String docRoot) {
- super();
- this.docRoot = docRoot;
- }
- publicvoid handle(
- final HttpRequest request,
- final HttpResponse response,
- final HttpContext context) throws HttpException, IOException {
- String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH);
- if (!method.equals("GET") && !method.equals("HEAD") && !method.equals("POST")) {
- thrownew MethodNotSupportedException(method + " method not supported");
- }
- String target = request.getRequestLine().getUri();
- if (request instanceof HttpEntityEnclosingRequest) {
- HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
- byte[] entityContent = EntityUtils.toByteArray(entity);
- System.out.println("Incoming entity content (bytes): " + entityContent.length);
- }
- final File file = new File(this.docRoot, URLDecoder.decode(target, "UTF-8"));
- if (!file.exists()) {
- response.setStatusCode(HttpStatus.SC_NOT_FOUND);
- StringEntity entity = new StringEntity(
- "<html><body><h1>File" + file.getPath() +
- " not found</h1></body></html>",
- ContentType.create("text/html", "UTF-8"));
- response.setEntity(entity);
- System.out.println("File " + file.getPath() + " not found");
- } elseif (!file.canRead() || file.isDirectory()) {
- response.setStatusCode(HttpStatus.SC_FORBIDDEN);
- StringEntity entity = new StringEntity(
- "<html><body><h1>Access denied</h1></body></html>",
- ContentType.create("text/html", "UTF-8"));
- response.setEntity(entity);
- System.out.println("Cannot read file " + file.getPath());
- } else {
- response.setStatusCode(HttpStatus.SC_OK);
- FileEntity body = new FileEntity(file, ContentType.create("text/html", (Charset) null));
- response.setEntity(body);
- System.out.println("Serving file " + file.getPath());
- }
- }
- }
- staticclass RequestListenerThread extends Thread {
- privatefinal HttpConnectionFactory<DefaultBHttpServerConnection> connFactory;
- privatefinal ServerSocket serversocket;
- privatefinal HttpService httpService;
- public RequestListenerThread(
- finalint port,
- final HttpService httpService,
- final SSLServerSocketFactory sf) throws IOException {
- this.connFactory = DefaultBHttpServerConnectionFactory.INSTANCE;
- this.serversocket = sf != null ? sf.createServerSocket(port) : new ServerSocket(port);
- this.httpService = httpService;
- }
- @Override
- publicvoid run() {
- System.out.println("Listening on port " + this.serversocket.getLocalPort());
- while (!Thread.interrupted()) {
- try {
- // Set up HTTP connection
- Socket socket = this.serversocket.accept();
- System.out.println("Incoming connection from " + socket.getInetAddress());
- HttpServerConnection conn = this.connFactory.createConnection(socket);
- // Start worker thread
- Thread t = new WorkerThread(this.httpService, conn);
- t.setDaemon(true);
- t.start();
- } catch (InterruptedIOException ex) {
- break;
- } catch (IOException e) {
- System.err.println("I/O error initialising connection thread: "
- + e.getMessage());
- break;
- }
- }
- }
- }
- staticclass WorkerThread extends Thread {
- privatefinal HttpService httpservice;
- privatefinal HttpServerConnection conn;
- public WorkerThread(
- final HttpService httpservice,
- final HttpServerConnection conn) {
- super();
- this.httpservice = httpservice;
- this.conn = conn;
- }
- @Override
- publicvoid run() {
- System.out.println("New connection thread");
- HttpContext context = new BasicHttpContext(null);
- try {
- while (!Thread.interrupted() && this.conn.isOpen()) {
- this.httpservice.handleRequest(this.conn, context);
- }
- } catch (ConnectionClosedException ex) {
- System.err.println("Client closed connection");
- } catch (IOException ex) {
- System.err.println("I/O error: " + ex.getMessage());
- } catch (HttpException ex) {
- System.err.println("Unrecoverable HTTP protocol violation: " + ex.getMessage());
- } finally {
- try {
- this.conn.shutdown();
- } catch