前言
有个需求,需要内嵌http服务,要求体积尽可能小,http并发很小,那么就自行实现一个http服务。其实http只是一种应用层协议:超文本传输协议。传输层协议为TCP。
1. http协议
http协议的内容通过抓包如下:
可以看见Hypertext Transfer Protocol下
标红的是http协议的核心,http header是说明协议,协议mime-type;body才是返回的内容。
2. BIO HTTP demo
package com.feng.server.http;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;
public class HttpServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8282);
ThreadPoolExecutor executor = new ThreadPoolExecutor(3,
10,
5l,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100),
r -> {
Thread t = new Thread(r);
t.setName("http-8282-"+t.getId());
return t;
});
Socket socket;
while (true) {
socket = serverSocket.accept();
executor.execute(new SocketRunnable(socket));
}
}
private static class SocketRunnable implements Runnable {
private Socket socket;
public SocketRunnable(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (InputStream is = socket.getInputStream();
Reader reader = new InputStreamReader(is);
BufferedReader br = new BufferedReader(reader);
OutputStream out = socket.getOutputStream()) {
System.out.println("adress: " + socket.getLocalAddress() + ":" + socket.getLocalPort());
String line;
while ((line = br.readLine()) != null && line.trim().length()!=0) {
System.out.println("--------"+line);
}
PrintWriter pw = new PrintWriter(out);
pw.write("HTTP/1.1 200 \r\n");
// pw.write("Transfer-Encoding: chunked");
pw.write("Content-Type: text/html;charset=UTF-8\r\n");
pw.write("\r\n");
pw.write("helloWord\r\n");
pw.flush();
pw.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
可以通过serverSocket创建连接,提供http能力,只需要返回的header写明http协议,Content-Type即可。测试OK
其实还可以通过NIO实现http服务,channel select buffer,性能更强,配合Linux内核的epoll,提供强大的性能。
3. JDK内置http server
其实JDK也内置了Http Server的API,还支持https,毕竟https普及已经好几年了。
package com.feng.server.http;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.net.InetSocketAddress;
public class InnerHttpServer {
public static void main(String[] args) throws IOException {
// 创建 http 服务器, 绑定本地 xxxx 端口
HttpServer httpServer = HttpServer.create(new InetSocketAddress(8383), 0);
httpServer.createContext("/", new HttpHandler() {
@Override
public void handle(HttpExchange httpExchange) throws IOException {
System.out.println("url: " + httpExchange.getRequestURI().getQuery());
httpExchange.sendResponseHeaders(200, "hello".length());
httpExchange.getResponseBody().write("hello".getBytes());
}
});
httpServer.createContext("/hello", new HttpHandler() {
@Override
public void handle(HttpExchange httpExchange) throws IOException {
System.out.println("---url: " + httpExchange.getRequestURI().getQuery());
httpExchange.sendResponseHeaders(200, "hello".length());
httpExchange.getResponseBody().write("hello".getBytes());
}
});
httpServer.start();
// httpServer.stop(0);
}
}
其中有requestbody requestURI responseBody,可以更方便的自定义,可以支持https,需要证书
HttpsServer
其实观其源码也是ServerSocketChannel实现的
总结
这里通过手动实现了java http,其实现在的Tomcat jetty都是根据这些原理来实现的,封装的异常强大。以Tomcat为例,支持BIO、NIO、APR模式,除了APR,其他都是Tomcat通过Java socket实现的。openjdk的官网可以下载源码,使用需要遵循GNU v2协议