二、如何编写一个简单的HTTP服务器

一、目标

诸如tomcat等web服务器中间件简化了我们web的开发成本,但有时候我们或许并不需要这么一个完备的服务器,只是希望做一个简单地处理或者做特殊用途的服务器。

本文将提供一个HTTP的服务器示例,采用Java的ServerSocket进行编码。随着计算机硬件的提升,以及Java地不断优化,使用Java网络编程实现web服务器在实际性能上已经开始可以跟C进行竞争。

二、代码示例

以下代码分为两块:

1)HttpServer:主要包含一个ServerSocket,用于接收客户端请求。并通过线程池将请求从主线程剥离,分散到各个线程中去处理;

2)RequestHandler:实现了Runnable接口,将获取一个线程来处理各个请求;流的读取和响应遵循HTTP协议。

注意:编写Http服务器和一般的socket程序并没有太大不同,但是你需要遵循HTTP协议,这样对于采用HTTP协议的客户端或者其它服务器就可以直接进行HTTP请求来通讯。

HttpServer

import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import java.util.concurrent.*;/** * Http服务器端示例,端口设置为8080,编码设置为UTF-8 * @author lay * @date 2019-01-01 */public class HttpServer { private static final int port = 8080; /** * 启动HTTP服务器 */ public void start() throws IOException { // 初始化线程池 ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); // 初始化服务器socket ServerSocket serverSocket = new ServerSocket(port); System.out.println("ServerSocket启动完成"); while (true) { // 阻塞等待socket连接 System.out.println("等待socket"); Socket socket = serverSocket.accept(); // 提交至线程池处理 executor.submit(new RequestHandler(socket)); System.out.println("提交线程池处理请求"); } } public static void main(String[] args) { try { new HttpServer().start(); } catch (IOException e) { throw new RuntimeException(e); } }}

RequestHandler

import java.io.*;import java.net.Socket;/** * 请求处理类 * @author lay * @date 2019-01-01 */public class RequestHandler implements Runnable { /** * HTTP响应头 */ private static final String response = "http/1.1 200 ok"; private static final String splitStr = "\r\n"; private Socket socket; private BufferedReader reader; private BufferedWriter writer; public RequestHandler(Socket socket) throws IOException { this.socket = socket; this.reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); this.writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); } /** * 响应结果 * @param content 响应内容 * @throws IOException IO异常抛出 */ public void sendResponse(String content) throws IOException { writer.write(String.format("%s%s", response, splitStr)); writer.write(splitStr); writer.write(content); } /** * 获得请求 * @return 请求文本 * @throws IOException IO异常抛出 */ public String getRequest() throws IOException { StringBuilder stringBuilder = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { stringBuilder.append(line); stringBuilder.append(splitStr); // 空字符串 if (line.isEmpty()) { break; } } System.out.println("request:\r\n" + stringBuilder); return stringBuilder.toString(); } @Override public void run() { try { String request = getRequest(); // 这里直接把请求数据响应回去 sendResponse(request); writer.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } try { writer.close(); } catch (IOException e) { e.printStackTrace(); } try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } }}

请求处理器这里直接将请求的HTTP内容返回回去了,如果你使用浏览器请求8080端口,你会看到如下内容:

不过浏览器会默认请求一个icon,所以针对一个URL地址会有两个请求

你可以在ico请求的时候返回一个二进制的ico文件流,它将显示在浏览器的tab上。

扩展点:

1)我们可以像tomcat一样去支持Java Servlet API

2)支持如GET、POST、PUT、DELETE等restful请求

3)将程序配置进行XML配置

4)增加管理界面

5)请求跟踪处理

6)缓存、非阻塞IO、通道来增加性能

 

相关文章