一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情。
简介
后端写接口服务, 总离不开http服务器的支持, 为了加深对于http的理解, 今天尝试使用Socket写一个相对简易的服务器.
前言
写之前, 我们先熟悉一下http请求的报文, 即从前端/请求工具中发送到服务器时, 我们接受到了什么. 首先写一个熟悉的
main()方法, 启动一个ServerSocket, 然后接受一次请求去查看输出了什么.
package com.demo;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Main {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8080);
Socket accept = serverSocket.accept();
InputStream inputStream = accept.getInputStream();
int i = -1;
while ((i = inputStream.read()) != -1) {
System.out.print((char) i);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
百度看下解析, 熟悉下其中的元素
看完之后我们就可以动手写了
请求
新建一个项目, 写下上述代码. 处理思路: 启动服务, 然后监听请求, 处理请求, 获取报文, 解析报文, 返回响应, 直到服务关闭, 然后我们想清楚思路后就可以开始了.
package com.demo;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
Socket accept = serverSocket.accept();
handlerConnection(accept);
}
}
/**
* 处理连接请求
*
* @param accept 请求
* @throws IOException 异常
*/
private static void handlerConnection(Socket accept) throws IOException {
// 获取socket的输入流
InputStream inputStream = accept.getInputStream();
// 我们可以用StringBuilder把报文存储起来
StringBuilder message = new StringBuilder();
byte[] buf = new byte[1];
while (inputStream.read(buf,0,buf.length) != -1) {
String str = new String(buf);
message.append(str);
}
// 解析包文
parseMessage(message);
}
/**
* 解析请求报文
*
* @param message 报文
*/
private static void parseMessage(StringBuilder message) {
// 先通过 \r\n分行
String[] split = message.toString().split("\r\n");
// 第一行是请求方式, 请求url, 请求协议
String firstRow = split[0];
// 通过空格分开
String[] methodPathWithProtocol = firstRow.split(" ");
String method = methodPathWithProtocol[0];
String path = methodPathWithProtocol[1];
String protocol = methodPathWithProtocol[2];
System.out.printf("请求方法为:%s,请求路径为:%s,请求协议:%s", method, path, protocol);
Map<String, String> headers = new HashMap<>();
// 从第一行开始解析请求头
String line;
int index = 1;
while (index < split.length && !(line = split[index++]).equals(" ")) {
String[] header = line.split(":");
headers.put(header[0], header[1].trim());
}
System.out.printf("请求头:%s", headers);
}
}
对方法协议和路径进行解析, 对请求头进行解析
响应
一次完整的http调用, 除了请求外, 还应该有响应, 我们查一下响应体是什么格式
组成对应的字符串, 然后write()到客户端即可
/**
* 处理响应
*
* @param accept 请求
*/
private static void handlerResponse(Socket accept) throws IOException {
OutputStream outputStream = accept.getOutputStream();
PrintStream printStream = new PrintStream(outputStream);
printStream.println("HTTP/1.1 200 OK");
printStream.println("Content-Type:text/html;charset:utf-8");
printStream.println();
printStream.println("<h1>HELLO WORLD</h1>");
printStream.flush();
accept.shutdownOutput();
}