Java 结合多线程实现简单 HTTP 服务器

能自己实现一个简单的 HTTP 服务器,还有些小激动的。
本简单 HTTP Server 实现给客户端返回 HTML 文本和二进制文件(图片),
啥也不说了,直接上码吧:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

class HttpRequestHandler implements Runnable {
    private Socket socket;
    private String basePath;

    public HttpRequestHandler(Socket socket, String basePath) {
        this.socket = socket;
        this.basePath = basePath;
    }

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        BufferedReader reader = null;

        // 向客户端输出内容
        PrintStream printStream = null;
        InputStream in = null;
        BufferedReader br = null;

        try {
            // 在 socket 上获得输入流,使用默认字符集的 InputStreamReader
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            // 读取 HTTP 报文的请求行
            String header = reader.readLine();
            System.out.println("thread name: " + threadName + ", HTTP 报文请求行: " + header);

            // 读取HTTP请求报文的起始行,并根据空格分割开,存入数组,得到请求的资源
            // 由相对路径计算出绝对路径
            String filePath = this.basePath + header.split(" ")[1];

            printStream = new PrintStream(socket.getOutputStream(), true);

            // 如果请求资源的后缀为jpg 或者 ico,则读取资源并输出
            if (filePath.endsWith("jpg") || filePath.endsWith("ico")) {
                System.out.println("thread name: " + threadName + ", filePath: " + filePath);

                in = new FileInputStream(filePath);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();

                byte[] buff = new byte[1024];
                int i;
                while ((i = in.read(buff)) != -1) {
                    baos.write(buff, 0, i);
                }
                byte[] array = baos.toByteArray();

                // 设置响应报文
                printStream.println("HTTP/1.1 200 OK");
                printStream.println("Server: MyServer");
                printStream.println("Content-Type: image/jpeg");
                printStream.println("Content-Length: " + array.length);
                // 根据 HTTP 协议, 空行将结束头信息
                printStream.println();
                printStream.write(array);
            } else if (filePath.endsWith("html")) {
                // 客户端请求的是 html 文件
                br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath)));
                printStream.println("HTTP/1.1 200 OK");
                printStream.println("Server: MyServer");
                printStream.println("Content-Type: text/html; charset=UTF-8");
                printStream.println();

                String line;
                while ((line = br.readLine()) != null) {
                    printStream.println(line);
                }
            }
            printStream.flush();
        } catch (Exception ex) {
            ex.printStackTrace();
            printStream.println("HTTP/1.1 500");
            printStream.println();
            printStream.flush();
        } finally {
            this.close(br, in, reader, socket, printStream);
        }

    }

    private void close(Closeable... closeables) {
        if (closeables != null) {
            for (Closeable closeable : closeables) {
                try {
                    if (closeable != null) {
                        closeable.close();
                    }
                } catch (Exception e) {

                }
            }
        }

    }
}

public class MySimpleHttpServer {

    // 处理 HttpRequest 的线程池
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, new SynchronousQueue<>());

    /**
     * SimpleHttpServer 的根路径
     */
    private String basePath;

    /**
     * 服务监听端口
     */
    private int port = 8080;

    public void start() throws Exception {
        System.out.println("start at " + port + ", basePath: " + this.basePath);

        // serverSocket 接受客户端的请求
        ServerSocket serverSocket = new ServerSocket(port);
        Socket socket;
        /**
         * 服务端 serverSocket 接受客户端请求,并创建客户端 socket 对象
         */
        while ((socket = serverSocket.accept()) != null) {
            /**
             * 接收一个客户端Socket,生成一个HttpRequestHandler,放入线程池队列
             */
            executor.execute(new HttpRequestHandler(socket, this.basePath));
        }
        serverSocket.close();
    }

    public void setBasePath(String basePath) {
        if (basePath != null && new File(basePath).exists() &&
                new File(basePath).isDirectory()) {
            this.basePath = basePath;
        }
    }

    public void setPort(int port) {
        if (port > 0) {
            this.port = port;
        }
    }
}

运行server:

public class ServerStart {
    public static void main(String[] args) throws Exception {
        MySimpleHttpServer httpServer = new MySimpleHttpServer();
        httpServer.setBasePath("/data/simpleHttpServer");
        httpServer.start();
    }
}

写一个 HTML 页面测试一下吧:

// index.html 
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel=”icon” href=”favicon.ico” mce_href=”favicon.ico” type=”image/x-icon”>
<title>simple server test</title>
</head>

<body>
文档内容......
<h1>第一张图片</h1>
<img src="1.jpg" align="middle" />
<h1>第二张图片</h1>
<img src="2.jpg" align="middle" />
<h1>第三张图片</h1>
<img src="3.jpg" align="middle" />

</body>

</html>

index.html1.jpg等资源放到basePath下,然后在浏览器的地址栏里输入:http://127.0.0.1:8080/index.html,然后就看到这个奇妙的网页了:
这里写图片描述

浏览器并发的请求,因为有了多线程,这个简单的 HTTP Server 的吞吐量也还可以的,响应时间也很小。

参考: 用java实现的一个简单web服务器程序
java实现简单的http服务器
HTTP头部详解及使用Java套接字处理HTTP请求

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值