是否有一种方法可以使用Java SE API在Java中创建一个非常基本的HTTP服务器(只支持GET/POST),而不需要编写代码手动解析HTTP请求和手动格式化HTTP响应?Java SE API在HttpURLConnection中很好地封装了HTTP客户机功能,但是是否有类似的HTTP服务器功能呢?

需要明确的是,我在网上看到的许多ServerSocket示例的问题是,它们自己进行请求解析/响应格式化和错误处理,这很乏味,容易出错,而且不太全面,出于这些原因,我试图避免使用它。


当前回答

检出简单。它是一个非常简单的嵌入式服务器,内置了对各种操作的支持。我特别喜欢它的线程模型..

神奇的!

其他回答

我玩得很开心,我玩转了一下,拼凑出了这个。我希望这对你有所帮助。 你需要安装Gradle或者使用Maven插件。

build.gradle

plugins {
    id 'application'
}

group 'foo.bar'
version '1.0'

repositories {
    mavenCentral()
}

application{
    mainClass.set("foo.FooServer")
}

dependencies {}

FooServer 主入口点,你的主类。

package foo;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FooServer {

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(7654);
        serverSocket.setPerformancePreferences(0, 1, 2);

        /* the higher the numbers, the better the concurrent performance, ha!
           we found that a 3:7 ratio to be optimal
           3 partitioned executors to 7 network executors */

        ExecutorService executors = Executors.newFixedThreadPool(3);
        executors.execute(new PartitionedExecutor(serverSocket));
    }


    public static class PartitionedExecutor implements Runnable {
        ServerSocket serverSocket;

        public PartitionedExecutor(ServerSocket serverSocket) {
            this.serverSocket = serverSocket;
        }

        @Override
        public void run() {
            ExecutorService executors = Executors.newFixedThreadPool(30);
            executors.execute(new NetworkRequestExecutor(serverSocket, executors));
        }
    }


    public static class NetworkRequestExecutor implements Runnable{

        String IGNORE_CHROME = "/favicon.ico";
        String BREAK = "\r\n";
        String DOUBLEBREAK = "\r\n\r\n";

        Integer REQUEST_METHOD = 0;
        Integer REQUEST_PATH = 1;
        Integer REQUEST_VERSION = 2;

        String RENDERER;

        Socket socketClient;
        ExecutorService executors;
        ServerSocket serverSocket;

        public NetworkRequestExecutor(ServerSocket serverSocket, ExecutorService executors){
            this.serverSocket = serverSocket;
            this.executors = executors;
        }

        @Override
        public void run() {
            try {

                socketClient = serverSocket.accept();
                Thread.sleep(19);//do this for safari, its a hack but safari requires something like this.
                InputStream requestInputStream = socketClient.getInputStream();

                OutputStream clientOutput = socketClient.getOutputStream();

                if (requestInputStream.available() == 0) {
                    requestInputStream.close();
                    clientOutput.flush();
                    clientOutput.close();
                    executors.execute(new NetworkRequestExecutor(serverSocket, executors));
                    return;
                }

                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                int bytesRead;
                while ((bytesRead = requestInputStream.read(byteBuffer.array())) != -1) {
                    byteArrayOutputStream.write(byteBuffer.array(), 0, bytesRead);
                    if (requestInputStream.available() == 0) break;
                }

                String completeRequestContent = byteArrayOutputStream.toString();
                String[] requestBlocks = completeRequestContent.split(DOUBLEBREAK, 2);

                String headerComponent = requestBlocks[0];
                String[] methodPathComponentsLookup = headerComponent.split(BREAK);
                String methodPathComponent = methodPathComponentsLookup[0];

                String[] methodPathVersionComponents = methodPathComponent.split("\\s");

                String requestVerb = methodPathVersionComponents[REQUEST_METHOD];
                String requestPath = methodPathVersionComponents[REQUEST_PATH];
                String requestVersion = methodPathVersionComponents[REQUEST_VERSION];


                if (requestPath.equals(IGNORE_CHROME)) {
                    requestInputStream.close();
                    clientOutput.flush();
                    clientOutput.close();
                    executors.execute(new NetworkRequestExecutor(serverSocket, executors));
                    return;
                }

                ConcurrentMap<String, String> headers = new ConcurrentHashMap<>();
                String[] headerComponents = headerComponent.split(BREAK);
                for (String headerLine : headerComponents) {
                    String[] headerLineComponents = headerLine.split(":");
                    if (headerLineComponents.length == 2) {
                        String fieldKey = headerLineComponents[0].trim();
                        String content = headerLineComponents[1].trim();
                        headers.put(fieldKey.toLowerCase(), content);
                    }
                }

                clientOutput.write("HTTP/1.1 200 OK".getBytes());
                clientOutput.write(BREAK.getBytes());

                Integer bytesLength = "hi".length();
                String contentLengthBytes = "Content-Length:" + bytesLength;
                clientOutput.write(contentLengthBytes.getBytes());
                clientOutput.write(BREAK.getBytes());

                clientOutput.write("Server: foo server".getBytes());
                clientOutput.write(BREAK.getBytes());

                clientOutput.write("Content-Type: text/html".getBytes());

                clientOutput.write(DOUBLEBREAK.getBytes());
                clientOutput.write("hi".getBytes());

                clientOutput.close();
                socketClient.close();

                executors.execute(new NetworkRequestExecutor(serverSocket, executors));

            } catch (IOException ex) {
                ex.printStackTrace();
            } catch (InterruptedException ioException) {
                ioException.printStackTrace();
            }
        }
    }
}

运行该程序:

gradle run

浏览:

http://localhost:7654/

我强烈建议考虑Simple,特别是如果您不需要Servlet功能,而只是访问请求/响应对象。如果你需要REST,你可以把Jersey放在上面,如果你需要输出HTML或类似的东西,有Freemarker。我真的很喜欢使用这个组合可以做的事情,而且需要学习的API相对较少。

Apache Commons HttpCore项目怎么样?

来自网站:…… HttpCore目标

实现最基本的HTTP传输方面 良好的表现和清晰的表达之间的平衡 API 小(可预测的)内存占用 自包含库(没有JRE以外的外部依赖)

试试这个https://github.com/devashish234073/Java-Socket-Http-Server/blob/master/README.md

这个API使用套接字创建了一个HTTP服务器。

它以文本的形式从浏览器获取请求 解析它来检索URL信息、方法、属性等。 使用定义的URL映射创建动态响应 将响应发送到浏览器。

例如,下面是response .java类中的构造函数如何将原始响应转换为http响应:

public Response(String resp){
    Date date = new Date();
    String start = "HTTP/1.1 200 OK\r\n";
    String header = "Date: "+date.toString()+"\r\n";
    header+= "Content-Type: text/html\r\n";
    header+= "Content-length: "+resp.length()+"\r\n";
    header+="\r\n";
    this.resp=start+header+resp;
}

这是我简单的web服务器,在JMeter中用于测试webhook(这就是为什么它会在收到请求后关闭并结束自己)。

import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class HttpServer {

    private static int extractContentLength(StringBuilder sb) {
        int length = 0;
        String[] lines = sb.toString().split("\\n");
        for (int i = 0; i < lines.length; i++) {
            String s = lines[i];
            if (s.toLowerCase().startsWith("Content-Length:".toLowerCase()) && i <= lines.length - 2) {
                String slength = s.substring(s.indexOf(":") + 1, s.length()).trim();
                length = Integer.parseInt(slength);
                System.out.println("Length = " + length);
                return length;
            }
        }
        return 0;
    }

    public static void main(String[] args) throws IOException {
        
        
        int port = Integer.parseInt(args[0]);
        System.out.println("starting HTTP Server on port " + port);

        StringBuilder outputString = new StringBuilder(1000);

        ServerSocket serverSocket = new ServerSocket(port);
        serverSocket.setSoTimeout(3 * 60 * 1000); // 3 minutes timeout
        while (true) {

            outputString.setLength(0); // reset buff

            Socket clientSocket = serverSocket.accept(); // blocking
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);

            try {

                boolean isBodyRead = false;
                int dataBuffer;
                while ((dataBuffer = clientSocket.getInputStream().read()) != -1) {

                    if (dataBuffer == 13) { // CR
                        if (clientSocket.getInputStream().read() == 10) { // LF
                            outputString.append("\n");
                        }
                    } else {
                        outputString.append((char) dataBuffer);
                    }
                    
                    // do we have Content length
                    int len = extractContentLength(outputString);
                    if (len > 0) {
                        int actualLength = len - 1; // we need to substract \r\n
                        for (int i = 0; i < actualLength; i++) {
                            int body = clientSocket.getInputStream().read();
                            outputString.append((char) body);
                        }
                        isBodyRead = true;
                        break;
                    }

                } // end of reading while

                if (isBodyRead) {
                    // response headers
                    out.println("HTTP/1.1 200 OK");
                    out.println("Connection: close");
                    out.println(); // must have empty line for HTTP
                    
                    out.flush(); 
                    out.close(); // close clients connection
                }

            } catch (IOException ioEx) {
                System.out.println(ioEx.getMessage());
            }

            System.out.println(outputString.toString());
            break; // stop server - break while true
            
        } // end of outer while true
        
        serverSocket.close();

    } // end of method

}

你可以这样测试:

curl -X POST -H "Content-Type: application/json" -H "Connection: close" -d '{"name": "gustinmi", "email": "gustinmi at google dot com "}' -v http://localhost:8081/