java 手写http服务器,Socket手写HTTP服务器

public class JHttp {

private static final Logger logger = Logger.getLogger(JHttp.class.getCanonicalName());

private static final int NUM_THREADS = 50;

private static final String INDEX_FILE = "index.html";

private final File rootDirectory;

private final int port;

public JHttp(File rootDirectory, int port) {

this.rootDirectory = rootDirectory;

this.port = port;

}

public void start() throws IOException {

ExecutorService pool = Executors.newFixedThreadPool(NUM_THREADS);

try (ServerSocket server = new ServerSocket(port)) {

logger.info("Accepting connections on port " + server.getLocalPort());

logger.info("Document Root: " + rootDirectory);

while (true) {

try {

Socket request = server.accept();

RequestProcessor r = new RequestProcessor(rootDirectory, INDEX_FILE, request);

pool.submit(r);

} catch (IOException e) {

logger.log(Level.WARNING, "Error accepting connection", e);

}

}

}

}

public static void main(String[] args) {

try {

URL url = JHttp.class.getClassLoader().getResource("static");

JHttp jHttp = new JHttp(new File(url.getPath()), 8888);

jHttp.start();

} catch (IOException e) {

logger.log(Level.SEVERE, "server could not start", e);

}

}

private class RequestProcessor implements Runnable {

private File rootDirectory;

private String indexFileName = "index.html";

private Socket connection;

public RequestProcessor(File rootDirectory, String indexFileName, Socket connection) {

this.rootDirectory = rootDirectory;

this.indexFileName = indexFileName;

this.connection = connection;

}

@Override

public void run() {

// 安全检查省略

String root = rootDirectory.getPath();

try {

OutputStream raw = new BufferedOutputStream(connection.getOutputStream());

Writer out = new OutputStreamWriter(raw);

Reader in = new InputStreamReader(new BufferedInputStream(connection.getInputStream()));

// 取请求头的第一行 GET /xxx HTTP/1.1

StringBuilder requestLine = new StringBuilder();

while (true) {

int c = in.read();

if (c == '\r' || c == '\n') {

break;

}

requestLine.append((char) c);

}

String get = requestLine.toString();

logger.info(connection.getRemoteSocketAddress() + " " + get);

String[] tokens = get.split("\\s+");

String method = tokens[0];

String version = "";

if ("GET".equals(method)) {

String fileName = tokens[1];

if (fileName.endsWith("/")) {

fileName += indexFileName;// 给默认边界值

}

// 映射MIME类型

String contentType = URLConnection.getFileNameMap().getContentTypeFor(fileName);

if (tokens.length > 2) {

version = tokens[2];

}

File theFile = new File(rootDirectory, fileName.substring(1, fileName.length()));

if (theFile.canRead() && theFile.getCanonicalPath().startsWith(root)) {

// 不让客户端超出文档边界,安全考虑

byte[] theData = Files.readAllBytes(theFile.toPath());

if (version.startsWith("HTTP/")) { // 发送一个MIME首部

sendHeader(out, "HTTP/1.1 200 OK", contentType, theData.length);

}

// 发送文件,可能是图像或二进制数据,不能使用writer

raw.write(theData);

raw.flush();

} else {

// 无法找到文件

String body = new StringBuilder("\r\n")

.append("

File not Find\r\n")

.append("

")

.append("

HTTP Error 404 : File Not Found

\r\n")

.append("\r\n")

.toString();

sendHeader(out, "HTTP/1.1 404 FileNotFound", "text/html; charset=utf-8", body.length());

out.write(body);

out.flush();

}

} else {

// 不支持的请求动作

// 无法找到文件

String body = new StringBuilder("\r\n")

.append("

File not Find\r\n")

.append("

")

.append("

HTTP Error 501 : Not Support

\r\n")

.append("\r\n")

.toString();

sendHeader(out, "HTTP/1.1 501 Not Support", "text/html; charset=utf-8", body.length());

out.write(body);

out.flush();

}

out.close();

in.close();

} catch (IOException e) {

logger.log(Level.WARNING, "Error talking to " + connection.getRemoteSocketAddress(), e);

} finally {

try {

connection.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

private void sendHeader(Writer out, String responseCode, String contentType, int length) throws IOException {

out.write(responseCode + System.lineSeparator());

out.write("Date: " + new Date() + System.lineSeparator());

out.write("Server: JHTTP 1.0" + System.lineSeparator());

out.write("Content-length: " + length + System.lineSeparator());

out.write("Content-type: " + contentType + System.lineSeparator() + System.lineSeparator()); // 响应头和响应体之间多一行空行

out.flush();

}

}

}

后续可追加特性:

1.服务器管理页面

2.支持Java Servlet API

3.支持其它请求方法,如POST,PUT

4.支持多文档根目录

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值