【问题标题】:Simple HTTP server in Java using only Java SE APIJava 中的简单 HTTP 服务器,仅使用 Java SE API
【发布时间】:2022-01-19 14:56:28
【问题描述】:

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

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

【问题讨论】:

  • 嗯...简短的回答是否定的。如果您想要处理 post 和 get 请求而无需手动编写 http 标头的东西,那么您可以使用 servlet。但那是java ee。如果您不想使用类似的东西,那么套接字和手动解析是我所知道的唯一其他选择。
  • 我知道这不符合 SO 的精神,但我会敦促您重新考虑您对 Java EE API 的厌恶。正如一些答案所提到的,有一些非常直接的实现,例如 Jetty,允许您在独立应用程序中嵌入 Web 服务器,同时仍然利用 servlet api。如果您出于某种原因绝对不能使用 Java EE API,请忽略我的评论 :-)
  • “Servlets”并不是真正的“Java EE”。它们只是一种编写插件的方式,可以由周围的应用程序调用以响应消息活动(现在通常是 HTTP 请求)。提供一个“仅使用 Java SE API”的 servlet 托管环境正是 Jetty 和 Tomcat 所做的。当然,您可能希望排除不必要的复杂性,但您可能需要决定 GET/POST 的允许属性和配置的子集。但这通常不值得,除了特殊的安全/嵌入式问题。
  • 在做出决定之前,可能值得仔细阅读这个 http 服务器列表。 java-source.net/open-source/web-servers

标签: java http httpserver


【解决方案1】:

查看“Jetty”网络服务器Jetty。一款出色的开源软件,似乎可以满足您的所有要求。

如果您坚持自己滚动,请查看“httpMessage”类。

【讨论】:

  • 我认为jetty api依赖于servlet。
  • @Irreputable:不,Jetty 是一个高度模块化的 Web 服务器,它有一个 servlet 容器作为它的可选模块之一。
  • “是服务器功能的模拟”——是的,它是“servlet”API。 servlet 容器在解析标题、cookie 等后调用您的类。
  • 仅作记录——Jetty 自带了自己的 Servlet API 实现,并且与 Java SE 一起工作得很好
  • Jetty 太大,在实际生产使用成为可能之前有太多的学习曲线。
【解决方案2】:

查看NanoHttpd

NanoHTTPD 是一种轻量级 HTTP 服务器,专为嵌入其他应用程序而设计,根据修改后的 BSD 许可证发布。

它正在 Github 上开发,并使用 Apache Maven 进行构建和单元测试”

【讨论】:

  • 一个警告:NanoHTTPD 可能没有针对树遍历攻击的保护 - 如果它将在公共地址上提供服务,您应该检查这一点。我的意思是这样的攻击:发出GET /../../blahblah http/1.1 之类的请求,服务器越过网站根目录并进入系统文件区域,提供可用于破坏或远程攻击系统的文件,如密码文件。
  • 这似乎是固定的。当前版本生成 403 if ( uri.startsWith( ".." ) || uri.endsWith( ".." ) || uri.indexOf( "../" ) >= 0 )。
  • 我不明白这是如何回答这个问题的。
【解决方案3】:

从 Java SE 6 开始,Sun Oracle JRE 中有一个内置的 HTTP 服务器。 com.sun.net.httpserver package summary 概述了所涉及的类并包含示例。

这是他们文档中的一个启动示例copypasted(尽管如此,所有尝试编辑它的人,因为它是一段丑陋的代码,请不要,这是复制粘贴,不是我的,此外,除非原始来源中的引文已更改,否则切勿编辑引文)。你可以在 Java 6+ 上复制'n'paste'n'运行它。

package com.stackoverflow.q3732109;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

public class Test {

    public static void main(String[] args) throws Exception {
        HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
        server.createContext("/test", new MyHandler());
        server.setExecutor(null); // creates a default executor
        server.start();
    }

    static class MyHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange t) throws IOException {
            String response = "This is the response";
            t.sendResponseHeaders(200, response.length());
            OutputStream os = t.getResponseBody();
            os.write(response.getBytes());
            os.close();
        }
    }

}

注意应该是他们示例中的response.length() 部分不好,应该是response.getBytes().length。即使这样,getBytes() 方法也必须显式指定您随后在响应标头中指定的字符集。唉,虽然对初学者有误导,但这毕竟只是一个基本的启动示例。

执行它并转到http://localhost:8000/test,您将看到以下响应:

这是回复


关于使用com.sun.* 类,请注意,与一些开发人员的想法相反,众所周知的常见问题解答Why Developers Should Not Write Programs That Call 'sun' Packages 绝对不会禁止这样做。该常见问题解答涉及供 Oracle JRE 内部使用的 sun.* 包(例如 sun.misc.BASE64Encoder)(因此,当您在不同的 JRE 上运行应用程序时,它会杀死您的应用程序),而不是 com.sun.* 包。 Sun/Oracle 也只是像 Apache 等其他公司一样,自己在 Java SE API 之上开发软件。仅当涉及到某个 Java API 的实现时,才不鼓励(但不禁止)使用 com.sun.* 类,例如 GlassFish (Java EE impl)、Mojarra (JSF impl)、Jersey (JAX- RS impl) 等

【讨论】:

  • @Waldheinz:就像@Software 一样,您将sun.*com.sun.* 混淆了。例如,您是否看到任何有关 sun.* API 的文档?看这里:java.sun.com/products/jdk/faq/faq-sun-packages.html 它是否告诉任何关于com.sun.* 的信息? com.sun.* 仅用于他们自己的公共软件,它不是 Java API 的一部分。他们还像其他公司一样在 Java API 之上开发软件。
  • 我认为这是一个非常好的用于集成测试用例的 http 服务器。感谢您的提示!
  • 如果您使用 Eclipse 并收到类似“访问限制:由于所需库的限制而无法访问 HttpExchange 类型...”之类的错误,stackoverflow.com/a/10642163 会告诉您如何禁用该访问检查.
  • FWIW 这也存在于 OpenJDK 中。
  • 这里提到的类在 OpenJDK 源代码中被标记为 @jdk.Exported,这意味着该 API 被认为是公共的,并且将在 Java 9 上可用(其他一些 com.sun.* 包将不可用,因为项目拼图)。
【解决方案4】:

我强烈建议您查看Simple,特别是如果您不需要 Servlet 功能而只需访问请求/响应对象。如果您需要 REST,您可以将 Jersey 放在上面,如果您需要输出 HTML 或类似内容,则可以使用 Freemarker。我真的很喜欢你可以用这种组合做些什么,而且要学习的 API 相对较少。

【讨论】:

  • +1。我喜欢 Simple 背后的想法。但是,当尝试使用 HTTPS 时会出现问题,因为 Mamba 取消了 Simple 的“可嵌入”功能。
【解决方案5】:

你也可以看看一些 NIO 应用框架,例如:

  1. 网络:http://jboss.org/netty
  2. Apache Mina:http://mina.apache.org/ 或其子项目 AsyncWeb:http://mina.apache.org/asyncweb/

【讨论】:

    【解决方案6】:

    这段代码比我们的好,你只需要添加2个库:javax.servelet.jarorg.mortbay.jetty.jar

    类码头:

    package jetty;
    
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import org.mortbay.http.SocketListener;
    import org.mortbay.jetty.Server;
    import org.mortbay.jetty.servlet.ServletHttpContext;
    
    public class Jetty {
    
        public static void main(String[] args) {
            try {
                Server server = new Server();
                SocketListener listener = new SocketListener();      
    
                System.out.println("Max Thread :" + listener.getMaxThreads() + " Min Thread :" + listener.getMinThreads());
    
                listener.setHost("localhost");
                listener.setPort(8070);
                listener.setMinThreads(5);
                listener.setMaxThreads(250);
                server.addListener(listener);            
    
                ServletHttpContext context = (ServletHttpContext) server.getContext("/");
                context.addServlet("/MO", "jetty.HelloWorldServlet");
    
                server.start();
                server.join();
    
            /*//We will create our server running at http://localhost:8070
            Server server = new Server();
            server.addListener(":8070");
    
            //We will deploy our servlet to the server at the path '/'
            //it will be available at http://localhost:8070
            ServletHttpContext context = (ServletHttpContext) server.getContext("/");
            context.addServlet("/MO", "jetty.HelloWorldServlet");
    
            server.start();
            */
    
            } catch (Exception ex) {
                Logger.getLogger(Jetty.class.getName()).log(Level.SEVERE, null, ex);
            }
    
        }
    } 
    

    Servlet 类:

    package jetty;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class HelloWorldServlet extends HttpServlet
    {
        @Override
        protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException
        {
            String appid = httpServletRequest.getParameter("appid");
            String conta = httpServletRequest.getParameter("conta");
    
            System.out.println("Appid : "+appid);
            System.out.println("Conta : "+conta);
    
            httpServletResponse.setContentType("text/plain");
            PrintWriter out = httpServletResponse.getWriter();
            out.println("Hello World!");
            out.close();
        }
    }
    

    【讨论】:

    • 这个问题要求一个纯粹的 Java SE 解决方案。您会发现 jetty 实现了 Java EE API。
    • Jetty 使用标准 Java SE 运行良好,因此符合要求。它实现部分Java EE API,它并不需要它。有区别。
    • 这不符合条件。 “仅使用 Java SE API”*.Servlet.jar*.jetty.jar 显然不是 Java SE 的一部分。
    • 我需要设置码头吗?或者我可以取消这两个 jar 并运行这个文件吗?
    【解决方案7】:

    Apache Commons HttpCore 项目怎么样?

    来自网站:... HttpCore 目标

    • 最基本的 HTTP 传输方面的实现
    • 在良好的性能与清晰度和表现力之间取得平衡 API
    • 内存占用小(可预测)
    • 自包含库(除 JRE 之外没有外部依赖项)

    【讨论】:

    • 这可能太低级了。至少应该以一种在 servlet API 级别调用您的代码的解决方案为目标,除非您想自己处理所有概念,如分块、编码等。不过,这可能很有趣。
    【解决方案8】:

    只需几行代码,只需使用 JDK 和 servlet api,就可以创建一个为 J2EE servlet 提供基本支持的 httpserver。

    我发现这对于单元测试 servlet 非常有用,因为它的启动速度比其他轻量级容器快得多(我们使用 jetty 进行生产)。

    大多数非常轻量级的 httpserver 不提供对 servlet 的支持,但我们需要它们,所以我想我会分享。

    下面的例子提供了基本的 servlet 支持,或者对于尚未实现的东西抛出和 UnsupportedOperationException。它使用 com.sun.net.httpserver.HttpServer 来提供基本的 http 支持。

    import java.io.*;
    import java.lang.reflect.*;
    import java.net.InetSocketAddress;
    import java.util.*;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    
    import com.sun.net.httpserver.HttpExchange;
    import com.sun.net.httpserver.HttpHandler;
    import com.sun.net.httpserver.HttpServer;
    
    @SuppressWarnings("deprecation")
    public class VerySimpleServletHttpServer {
        HttpServer server;
        private String contextPath;
        private HttpHandler httpHandler;
    
        public VerySimpleServletHttpServer(String contextPath, HttpServlet servlet) {
            this.contextPath = contextPath;
            httpHandler = new HttpHandlerWithServletSupport(servlet);
        }
    
        public void start(int port) throws IOException {
            InetSocketAddress inetSocketAddress = new InetSocketAddress(port);
            server = HttpServer.create(inetSocketAddress, 0);
            server.createContext(contextPath, httpHandler);
            server.setExecutor(null);
            server.start();
        }
    
        public void stop(int secondsDelay) {
            server.stop(secondsDelay);
        }
    
        public int getServerPort() {
            return server.getAddress().getPort();
        }
    
    }
    
    final class HttpHandlerWithServletSupport implements HttpHandler {
    
        private HttpServlet servlet;
    
        private final class RequestWrapper extends HttpServletRequestWrapper {
            private final HttpExchange ex;
            private final Map<String, String[]> postData;
            private final ServletInputStream is;
            private final Map<String, Object> attributes = new HashMap<>();
    
            private RequestWrapper(HttpServletRequest request, HttpExchange ex, Map<String, String[]> postData, ServletInputStream is) {
                super(request);
                this.ex = ex;
                this.postData = postData;
                this.is = is;
            }
    
            @Override
            public String getHeader(String name) {
                return ex.getRequestHeaders().getFirst(name);
            }
    
            @Override
            public Enumeration<String> getHeaders(String name) {
                return new Vector<String>(ex.getRequestHeaders().get(name)).elements();
            }
    
            @Override
            public Enumeration<String> getHeaderNames() {
                return new Vector<String>(ex.getRequestHeaders().keySet()).elements();
            }
    
            @Override
            public Object getAttribute(String name) {
                return attributes.get(name);
            }
    
            @Override
            public void setAttribute(String name, Object o) {
                this.attributes.put(name, o);
            }
    
            @Override
            public Enumeration<String> getAttributeNames() {
                return new Vector<String>(attributes.keySet()).elements();
            }
    
            @Override
            public String getMethod() {
                return ex.getRequestMethod();
            }
    
            @Override
            public ServletInputStream getInputStream() throws IOException {
                return is;
            }
    
            @Override
            public BufferedReader getReader() throws IOException {
                return new BufferedReader(new InputStreamReader(getInputStream()));
            }
    
            @Override
            public String getPathInfo() {
                return ex.getRequestURI().getPath();
            }
    
            @Override
            public String getParameter(String name) {
                String[] arr = postData.get(name);
                return arr != null ? (arr.length > 1 ? Arrays.toString(arr) : arr[0]) : null;
            }
    
            @Override
            public Map<String, String[]> getParameterMap() {
                return postData;
            }
    
            @Override
            public Enumeration<String> getParameterNames() {
                return new Vector<String>(postData.keySet()).elements();
            }
        }
    
        private final class ResponseWrapper extends HttpServletResponseWrapper {
            final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            final ServletOutputStream servletOutputStream = new ServletOutputStream() {
    
                @Override
                public void write(int b) throws IOException {
                    outputStream.write(b);
                }
            };
    
            private final HttpExchange ex;
            private final PrintWriter printWriter;
            private int status = HttpServletResponse.SC_OK;
    
            private ResponseWrapper(HttpServletResponse response, HttpExchange ex) {
                super(response);
                this.ex = ex;
                printWriter = new PrintWriter(servletOutputStream);
            }
    
            @Override
            public void setContentType(String type) {
                ex.getResponseHeaders().add("Content-Type", type);
            }
    
            @Override
            public void setHeader(String name, String value) {
                ex.getResponseHeaders().add(name, value);
            }
    
            @Override
            public javax.servlet.ServletOutputStream getOutputStream() throws IOException {
                return servletOutputStream;
            }
    
            @Override
            public void setContentLength(int len) {
                ex.getResponseHeaders().add("Content-Length", len + "");
            }
    
            @Override
            public void setStatus(int status) {
                this.status = status;
            }
    
            @Override
            public void sendError(int sc, String msg) throws IOException {
                this.status = sc;
                if (msg != null) {
                    printWriter.write(msg);
                }
            }
    
            @Override
            public void sendError(int sc) throws IOException {
                sendError(sc, null);
            }
    
            @Override
            public PrintWriter getWriter() throws IOException {
                return printWriter;
            }
    
            public void complete() throws IOException {
                try {
                    printWriter.flush();
                    ex.sendResponseHeaders(status, outputStream.size());
                    if (outputStream.size() > 0) {
                        ex.getResponseBody().write(outputStream.toByteArray());
                    }
                    ex.getResponseBody().flush();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    ex.close();
                }
            }
        }
    
        public HttpHandlerWithServletSupport(HttpServlet servlet) {
            this.servlet = servlet;
        }
    
        @SuppressWarnings("deprecation")
        @Override
        public void handle(final HttpExchange ex) throws IOException {
            byte[] inBytes = getBytes(ex.getRequestBody());
            ex.getRequestBody().close();
            final ByteArrayInputStream newInput = new ByteArrayInputStream(inBytes);
            final ServletInputStream is = new ServletInputStream() {
    
                @Override
                public int read() throws IOException {
                    return newInput.read();
                }
            };
    
            Map<String, String[]> parsePostData = new HashMap<>();
    
            try {
                parsePostData.putAll(HttpUtils.parseQueryString(ex.getRequestURI().getQuery()));
    
                // check if any postdata to parse
                parsePostData.putAll(HttpUtils.parsePostData(inBytes.length, is));
            } catch (IllegalArgumentException e) {
                // no postData - just reset inputstream
                newInput.reset();
            }
            final Map<String, String[]> postData = parsePostData;
    
            RequestWrapper req = new RequestWrapper(createUnimplementAdapter(HttpServletRequest.class), ex, postData, is);
    
            ResponseWrapper resp = new ResponseWrapper(createUnimplementAdapter(HttpServletResponse.class), ex);
    
            try {
                servlet.service(req, resp);
                resp.complete();
            } catch (ServletException e) {
                throw new IOException(e);
            }
        }
    
        private static byte[] getBytes(InputStream in) throws IOException {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            while (true) {
                int r = in.read(buffer);
                if (r == -1)
                    break;
                out.write(buffer, 0, r);
            }
            return out.toByteArray();
        }
    
        @SuppressWarnings("unchecked")
        private static <T> T createUnimplementAdapter(Class<T> httpServletApi) {
            class UnimplementedHandler implements InvocationHandler {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    throw new UnsupportedOperationException("Not implemented: " + method + ", args=" + Arrays.toString(args));
                }
            }
    
            return (T) Proxy.newProxyInstance(UnimplementedHandler.class.getClassLoader(),
                    new Class<?>[] { httpServletApi },
                    new UnimplementedHandler());
        }
    }
    

    【讨论】:

    • 这在 ServletOutputStream 和 ServletInputStream 上缺少一些方法
    • 较新版本的 servlet api,以上适合 3.0 及以下。只需根据需要在示例中添加缺少的方法
    【解决方案9】:

    com.sun.net.httpserver 解决方案不能跨 JRE 移植。最好使用 javax.xml.ws 中的官方 webservices API 来引导一个最小的 HTTP 服务器...

    import java.io._
    import javax.xml.ws._
    import javax.xml.ws.http._
    import javax.xml.transform._
    import javax.xml.transform.stream._
    
    @WebServiceProvider
    @ServiceMode(value=Service.Mode.PAYLOAD) 
    class P extends Provider[Source] {
      def invoke(source: Source) = new StreamSource( new StringReader("<p>Hello There!</p>"));
    }
    
    val address = "http://127.0.0.1:8080/"
    Endpoint.create(HTTPBinding.HTTP_BINDING, new P()).publish(address)
    
    println("Service running at "+address)
    println("Type [CTRL]+[C] to quit!")
    
    Thread.sleep(Long.MaxValue)
    

    编辑:这确实有效!上面的代码看起来像 Groovy 什么的。这是我测试过的 Java 翻译:

    import java.io.*;
    import javax.xml.ws.*;
    import javax.xml.ws.http.*;
    import javax.xml.transform.*;
    import javax.xml.transform.stream.*;
    
    @WebServiceProvider
    @ServiceMode(value = Service.Mode.PAYLOAD)
    public class Server implements Provider<Source> {
    
        public Source invoke(Source request) {
            return  new StreamSource(new StringReader("<p>Hello There!</p>"));
        }
    
        public static void main(String[] args) throws InterruptedException {
    
            String address = "http://127.0.0.1:8080/";
            Endpoint.create(HTTPBinding.HTTP_BINDING, new Server()).publish(address);
    
            System.out.println("Service running at " + address);
            System.out.println("Type [CTRL]+[C] to quit!");
    
            Thread.sleep(Long.MAX_VALUE);
        }
    }
    

    【讨论】:

    • +1 便于携带。太糟糕了,您无法将响应内容类型设置为text/xml
    • 我认为你可以做 class Server implements Provider<datasource> { </datasource> ...然后在 DataSource 的 getContentType()中指定 Content-Type > 方法。此外,您还可以注入 WebServiceContext: @Resource WebServiceContext ctx; 以设置其他标头并读取请求参数。不幸的是,通过 WebServiceContext 设置内容类型不起作用。
    • 您能解释一下为什么 com.sun.net.HttpServer 不能跨 JRE 移植吗?
    • 不,我不这么认为。它不适用于 IBM 的 Java 实现,也可能不适用于其他实现。即使它现在可以工作,内部 API 也可以更改。为什么不直接使用官方 API?
    • 此链接:docs.oracle.com/javase/9/docs/api/java.xml.ws-summary.html 表示 java.xml.ws 模块自 Java 9 以来已被弃用。
    【解决方案10】:

    我喜欢这个问题,因为这是一个不断创新的领域,并且总是需要一个轻型服务器,尤其是在谈到小型(呃)设备中的嵌入式服务器时。我认为答案分为两大类。

    1. 瘦服务器:以最少的处理、上下文或会话处理来建立服务器的静态内容。
    2. 小型服务器:表面上 a 具有许多类似 httpD 的服务器特性,并且占用空间尽可能小。

    虽然我可能会认为像 JettyApache Http ComponentsNetty 等 HTTP 库更像是原始 HTTP 处理工具。标签是非常主观的,并且取决于您被要求为小型站点提供的东西的种类。我根据问题的精神做出这种区分,特别是关于...的评论

    • "...无需编写代码来手动解析 HTTP 请求并手动格式化 HTTP 响应..."

    这些原始工具可让您做到这一点(如其他答案中所述)。他们并不真正适合制作轻型、嵌入式或迷你服务器的现成风格。迷你服务器可以为您提供与全功能 Web 服务器类似的功能(例如,Tomcat),没有花里胡哨、低容量、99% 的时间都具有良好的性能。瘦服务器似乎更接近原始措辞,只是比原始措辞多一点,可能具有有限的子集功能,足以让您在 90% 的时间看起来都不错。我对 raw 的想法是让我在 75% 到 89% 的时间看起来都不错,而无需额外的设计和编码。我认为如果/当您达到 WAR 文件的级别时,我们已经为 bonsi 服务器留下了“小”,看起来就像大型服务器所做的一切都更小。

    瘦服务器选项

    迷你服务器选项:

    • Spark Java ... 很多帮助结构(如过滤器、模板等)让好事成为可能。
    • MadVoc ...旨在成为盆景,很可能就是这样;-)

    在其他要考虑的事情中,我会包括身份验证、验证、国际化,使用类似FreeMaker 或其他模板工具来呈现页面输出。否则,管理 HTML 编辑和参数化可能会使使用 HTTP 看起来像 noughts-n-crosses。当然,这一切都取决于您需要有多灵活。如果它是一个菜单驱动的传真机,它可以非常简单。交互越多,您的框架就需要越“”。好问题,祝你好运!

    【讨论】:

      【解决方案11】:

      Spark 是最简单的,这里有一个快速入门指南:http://sparkjava.com/

      【讨论】:

      • 哇又小又干净:)
      【解决方案12】:

      结帐Simple。它是一个非常简单的嵌入式服务器,内置支持多种操作。我特别喜欢它的线程模型..

      太棒了!

      【讨论】:

        【解决方案13】:

        查看takes。查看https://github.com/yegor256/takes 了解快速信息

        【讨论】:

          【解决方案14】:

          曾几何时,我一直在寻找类似的东西 - 一个轻量级但功能齐全的 HTTP 服务器,我可以轻松嵌入和自定义。我发现了两种潜在的解决方案:

          • 不那么轻量级或简单的完整服务器(对于轻量级的极端定义。)
          • 真正的轻量级服务器,不完全是 HTTP 服务器,而是美化了 ServerSocket 示例,它们甚至不符合远程 RFC 并且不支持常用的基本功能。

          所以...我开始写JLHTTP - The Java Lightweight HTTP Server

          您可以将其作为单个(如果相当长的)源文件嵌入到任何项目中,也可以作为没有依赖关系的 ~50K jar(~35K 剥离)嵌入到任何项目中。它力求符合 RFC,并包含大量文档和许多有用的功能,同时将臃肿程度降至最低。

          功能包括:虚拟主机、从磁盘提供文件、通过标准 mime.types 文件进行 mime 类型映射、目录索引生成、欢迎文件、支持所有 HTTP 方法、条件 ETag 和 If-* 标头支持、分块传输编码、 gzip/deflate 压缩、基本 HTTPS(由 JVM 提供)、部分内容(继续下载)、文件上传的多部分/表单数据处理、通过 API 或注释的多个上下文处理程序、参数解析(查询字符串或 x-www- form-urlencoded body)等

          我希望其他人觉得它有用:-)

          【讨论】:

          • main 方法是一个很好的基本用法示例,FAQ 涉及到许多细节。如果您有改进现有文档的建议,请随时直接与我联系!
          • 终于是一款实用的、可工作的服务器,能够将 ISO 映像作为虚拟媒体,以普通用户身份通过​​ iLO 引导服务器。 Python 的 http.server 或其衍生版本无法完整或分块地提供如此大的文件;这个没有使用 sudo 就完成了这项工作。感谢分享!
          【解决方案15】:

          您可以编写一个非常简单的embedded Jetty Java 服务器。

          嵌入式 Jetty 是指服务器 (Jetty) 与应用程序一起提供,而不是将应用程序部署在外部 Jetty 服务器上。

          因此,如果在非嵌入式方法中,您的 webapp 内置到 WAR 文件中,该文件部署到某个外部服务器(Tomcat / Jetty / 等),在嵌入式 Jetty 中,您编写 webapp 并在相同的代码库中实例化 jetty 服务器.

          嵌入式 Jetty Java 服务器的示例,您可以git clone 并使用:https://github.com/stas-slu/embedded-jetty-java-server-example

          【讨论】:

            【解决方案16】:

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

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

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

            例如,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;
            }
            

            【讨论】:

              【解决方案17】:

              以上所有关于单主线程请求处理程序的详细回答。

              设置:

               server.setExecutor(java.util.concurrent.Executors.newCachedThreadPool());
              

              允许使用执行器服务通过多个线程提供多个请求。

              所以结束代码如下所示:

              import java.io.IOException;
              import java.io.OutputStream;
              import java.net.InetSocketAddress;
              import com.sun.net.httpserver.HttpExchange;
              import com.sun.net.httpserver.HttpHandler;
              import com.sun.net.httpserver.HttpServer;
              public class App {
                  public static void main(String[] args) throws Exception {
                      HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
                      server.createContext("/test", new MyHandler());
                      //Thread control is given to executor service.
                      server.setExecutor(java.util.concurrent.Executors.newCachedThreadPool());
                      server.start();
                  }
                  static class MyHandler implements HttpHandler {
                      @Override
                      public void handle(HttpExchange t) throws IOException {
                          String response = "This is the response";
                          long threadId = Thread.currentThread().getId();
                          System.out.println("I am thread " + threadId );
                          response = response + "Thread Id = "+threadId;
                          t.sendResponseHeaders(200, response.length());
                          OutputStream os = t.getResponseBody();
                          os.write(response.getBytes());
                          os.close();
                      }
                  }
              }
              

              【讨论】:

                【解决方案18】:

                自 Java 11 以来,旧的 com.sun.net.httpserver 再次成为公共且被接受的 API。您可以将其作为 HttpServer 类获取,作为 jdk.httpserver 模块的一部分提供。见https://docs.oracle.com/en/java/javase/11/docs/api/jdk.httpserver/com/sun/net/httpserver/HttpServer.html

                这个类实现了一个简单的 HTTP 服务器。 HttpServer 绑定到一个 IP 地址和端口号,并在该地址上侦听来自客户端的传入 TCP 连接。子类 HttpsServer 实现了一个处理 HTTPS 请求的服务器。

                因此,除了它的局限性之外,没有理由再避免使用它了。

                我用它在服务器应用程序中发布一个控制接口。从客户端请求中读取 User-agent 标头,我什至会在 text/plain 中响应 curl 等 CLI 工具,或者以更优雅的 HTML 方式响应任何其他浏览器。

                酷又简单。

                【讨论】:

                • 这个解决方案已经被其他用户提出了,com.sun.net.httpserver.HttpServer 从 Java 1.1 开始比从 Java 11 开始更多。
                • 对不起。你说对了一部分。从 1.6 开始就存在。 docs.oracle.com/en/java/javase/11/docs/api/jdk.httpserver/com/…
                • 谢谢。我已经编辑了我的答案以明确重要的事情:HttpServer 类再次被 Oracle 接受为适当的类。
                【解决方案19】:

                一个非常基本的 TCP 套接字级别的 HTTP 服务器示例:

                import java.io.BufferedReader;
                import java.io.IOException;
                import java.io.InputStreamReader;
                import java.io.PrintWriter;
                import java.net.InetAddress;
                import java.net.ServerSocket;
                import java.net.Socket;
                
                public class NaiveHttpServer {
                
                  public static void main(String[] args) throws IOException {
                    String hostname = InetAddress.getLocalHost().getHostName();
                    ServerSocket serverSocket = new ServerSocket(8089);
                    while (true) {
                      Socket clientSocket = serverSocket.accept();
                      PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
                      BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                      String s = in.readLine();
                      System.out.println(s);
                      while ("\r\n".equals(in.readLine())); 
                      if ("GET /hostname HTTP/1.1".equals(s)) {
                        out.println("HTTP/1.1 200 OK");
                        out.println("Connection: close");
                        out.println("Content-Type: text/plain");
                        out.println("Content-Length:" + hostname.length());
                        out.println();
                        out.println(hostname);
                      } else {
                        out.println("HTTP/1.1 404 Not Found");
                        out.println("Connection: close");
                        out.println();    
                      }
                      out.flush();
                    }
                  }
                }
                

                该示例提供计算机的主机名。

                【讨论】:

                  猜你喜欢
                  • 2011-02-12
                  • 1970-01-01
                  • 2011-01-19
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-06-25
                  • 1970-01-01
                  相关资源
                  最近更新 更多