HTTPServer
1. HTTPServer从0到1
1.1 socket基础
通过ServerSocket绑定端口,提供TCP服务
//通过创建ServerSocket对象,绑定端口
ServerSoker serverSocket = new ServerSocket(port,ip);
//通过accept()监听客户端请求
serverSocket.accept();
//接受请求数据
socket.getInputStream();
//返回响应数据
out = socker.getOutputStream();
out.print(response);
out.flush();
//关闭连接
socket.close();
serverSocket.close();
1.2 http协议
http请求信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zCqc6bzH-1589806934847)(E:\笔记\00.jpg)]
http响应信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tkacNJyM-1589806934849)(E:\笔记\01.jpg)]
1.3 http服务器设计
-
请求数据解析
获取Socket中的数据,封装成Request对象,解析为http请求
@Data public static class Request { /** * 请求方法 GET/POST/PUT/DELETE/OPTION... */ private String method; /** * 请求的uri */ private String uri; /** * http版本 */ private String version; /** * 请求头 */ private Map<String, String> headers; /** * 请求参数相关 */ private String message; }
http解析过程包括:请求行,请求头和正文
-
请求行的解析:请求方法+URI+HTTP版本
/** * 根据标准的http协议,解析请求行 * * @param reader * @param request */ private static void decodeRequestLine(BufferedReader reader, Request request) throws IOException { String[] strs = StringUtils.split(reader.readLine(), " "); assert strs.length == 3; request.setMethod(strs[0]); request.setUri(strs[1]); request.setVersion(strs[2]); }
-
请求头的解析:请求头为key:value的形式
/** * 根据标准http协议,解析请求头 * * @param reader * @param request * @throws IOException */ private static void decodeRequestHeader(BufferedReader reader, Request request) throws IOException { Map<String, String> headers = new HashMap<>(16); String line = reader.readLine(); String[] kv; while (!"".equals(line)) { kv = StringUtils.split(line, ":"); assert kv.length == 2; headers.put(kv[0].trim(), kv[1].trim()); line = reader.readLine(); } request.setHeaders(headers); }
-
正文的解析:为空或非空
/** * 根据标注http协议,解析正文 * * @param reader * @param request * @throws IOException */ private static void decodeRequestMessage(BufferedReader reader, Request request) throws IOException { //获取请求头中Content-Length的长度,新建长度为contentLen的char[]数组 int contentLen = Integer.parseInt(request.getHeaders().getOrDefault("Content-Length", "0")); if (contentLen == 0) { // 表示没有message,直接返回 // 如get/options请求就没有message return; } char[] message = new char[contentLen]; reader.read(message); request.setMessage(new String(message)); }
最后将三种解析封装起来,完成request的解析
/** * http的请求可以分为三部分 * * 第一行为请求行: 即 方法 + URI + 版本 * 第二部分到一个空行为止,表示请求头 * 空行 * 第三部分为接下来所有的,表示发送的内容,message-body;其长度由请求头中的 Content-Length 决定 * * 几个实例如下 * * @param reqStream * @return */ public static Request parse2request(InputStream reqStream) throws IOException { BufferedReader httpReader = new BufferedReader(new InputStreamReader(reqStream, "UTF-8")); Request httpRequest = new Request(); decodeRequestLine(httpReader, httpRequest); decodeRequestHeader(httpReader, httpRequest); decodeRequestMessage(httpReader, httpRequest); return httpRequest; }
-
-
请求任务HTTPTask
为了支持并发,每个请求单独分配一个任务,对于ServerSocket,接收到了一个请求,就创建一个HTTPTaskr任务来实现HTTP通信
HTTPTask的作用
-
从请求中获取数据
-
响应请求
-
封装结果并返回
public class HttpTask implements Runnable { private Socket socket; public HttpTask(Socket socket) { this.socket = socket; } @Override public void run() { if (socket == null) { throw new IllegalArgumentException("socket can't be null."); } try { //获取socket的输出流 OutputStream outputStream = socket.getOutputStream(); PrintWriter out = new PrintWriter(outputStream); //获取HTTP请求并解析 HttpMessageParser.Request httpRequest = HttpMessageParser.parse2request(socket.getInputStream()); try { // 根据请求结果进行响应,省略返回 String result = ...; //org.apache.http.io.HttpMessageParser String httpRes = HttpMessageParser.buildResponse(httpRequest, result); out.print(httpRes); } catch (Exception e) { String httpRes = HttpMessageParser.buildResponse(httpRequest, e.toString()); out.print(httpRes); } out.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
演示案例
@Data public static class Response { private String version; private int code; private String status; private Map<String, String> headers; private String message; } public static String buildResponse(Request request, String response) { //设置响应信息 Response httpResponse = new Response(); httpResponse.setCode(200); httpResponse.setStatus("ok"); httpResponse.setVersion(request.getVersion()); Map<String, String> headers = new HashMap<>(); headers.put("Content-Type", "application/json"); headers.put("Content-Length", String.valueOf(response.getBytes().length)); httpResponse.setHeaders(headers); httpResponse.setMessage(response); StringBuilder builder = new StringBuilder(); buildResponseLine(httpResponse, builder); buildResponseHeaders(httpResponse, builder); buildResponseMessage(httpResponse, builder); return builder.toString(); } //设置响应行 private static void buildResponseLine(Response response, StringBuilder stringBuilder) { stringBuilder.append(response.getVersion()).append(" ").append(response.getCode()).append(" ") .append(response.getStatus()).append("\n"); } //设置响应头 private static void buildResponseHeaders(Response response, StringBuilder stringBuilder) { for (Map.Entry<String, String> entry : response.getHeaders().entrySet()) { stringBuilder.append(entry.getKey()).append(":").append(entry.getValue()).append("\n"); } stringBuilder.append("\n"); } //设置响应正文 private static void buildResponseMessage(Response response, StringBuilder stringBuilder) { stringBuilder.append(response.getMessage()); }
-
-
HTTP服务器搭建
创建ServerSocket,绑定接受请求
public class BasicHttpServer { //创建线程池 private static ExecutorService bootstrapExecutor = Executors.newSingleThreadExecutor(); private static ExecutorService taskExecutor; private static int PORT = 8999; static void startHttpServer() { int nThreads = Runtime.getRuntime().availableProcessors(); //创建线程池 taskExecutor = new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(100), new ThreadPoolExecutor.DiscardPolicy()); while (true) { try { //绑定接受请求端口 ServerSocket serverSocket = new ServerSocket(PORT); bootstrapExecutor.submit(new ServerThread(serverSocket)); break; } catch (Exception e) { try { //重试 TimeUnit.SECONDS.sleep(10); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } } bootstrapExecutor.shutdown(); } private static class ServerThread implements Runnable { private ServerSocket serverSocket; public ServerThread(ServerSocket s) throws IOException { this.serverSocket = s; } @Override public void run() { while (true) { try { Socket socket = this.serverSocket.accept(); HttpTask eventTask = new HttpTask(socket); taskExecutor.submit(eventTask); } catch (Exception e) { e.printStackTrace(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } } } } }