Java中的简单HTTP服务器,仅使用Java SE API

问题描述 投票:300回答:18

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

为了清楚起见,我在网上看到的许多ServerSocket示例的问题是,他们自己做了请求解析/响应格式化和错误处理,这很乏味,容易出错,而且不太全面,我出于这些原因试图避免它。

作为我试图避免的手动HTTP操作的示例:

http://java.sun.com/developer/technicalArticles/Networking/Webserver/WebServercode.html

java http httpserver
18个回答
439
投票

从Java SE 6开始,内置了一个HTTP服务器 太阳 Oracle JRE。 qazxsw poi概述了所涉及的类并包含了示例。

这是一个从他们的文档中复制的启动示例(对于所有试图编辑它的人来说,因为它是一段丑陋的代码,请不要,这是一个复制粘贴,不是我的,而且你不应该编辑引用,除非它们已经改变在原始来源)。你可以在Java 6+上复制'n'paste'n'run它。

com.sun.net.httpserver package summary

注意到应该是他们的例子中的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类,请注意,这与一些开发人员的想法相反,绝对不会被众所周知的FAQ com.sun.*所禁止。这个常见问题解答关于Why Developers Should Not Write Programs That Call 'sun' Packages包(例如sun.*)供Oracle JRE内部使用(当你在不同的JRE上运行它时会破坏你的应用程序),而不是sun.misc.BASE64Encoder包。 Sun / Oracle也只是在Java SE API之上开发软件,就像Apache等所有其他公司一样。当涉及某个Java API的实现时,例如GlassFish(Java EE impl),Mojarra(JSF impl),Jersey(JAX-RS impl)等,仅阻止(但不禁止)使用com.sun.*类。


6
投票

我强烈建议您查看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()); } } ,特别是如果您不需要Servlet功能但只需访问请求/响应对象。如果您需要REST,可以将Jersey置于其上,如果您需要输出HTML或类似的Freemarker。我真的很喜欢你能用这种组合做什么,并且学习的API相对较少。


5
投票

您还可以查看一些NIO应用程序框架,例如:

  1. Netty:Simple
  2. Apache Mina:http://jboss.org/netty或其子项目AsyncWeb:http://mina.apache.org/

5
投票

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

班级码头:

http://mina.apache.org/asyncweb/

Servlet类:

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);
        }

    }
} 

3
投票

结帐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(); } } 。它是一个非常简单的可嵌入服务器,内置支持各种操作。我特别喜欢它的穿线模型..

惊人!


3
投票

看看Simple。请查看takes以获取快速信息


2
投票

Apache Commons https://github.com/yegor256/takes项目怎么样?

来自网站:... HttpCore目标

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

2
投票

所有上述答案详细介绍了单主线程请求处理程序。

设置:

HttpCore

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

所以最终代码如下所示:

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

1
投票

你可以编写一个非常简单的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(); } } } Java服务器。

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

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

嵌入式Jetty Java服务器的一个例子,你可以Tomcat和使用:git clone


1
投票

试试这个https://github.com/stas-slu/embedded-jetty-java-server-example

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

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

例如,这里是https://github.com/devashish234073/Java-Socket-Http-Server/blob/master/README.md类中的构造函数如何将原始响应转换为http响应:

Response.java

41
投票

看看com.sun.*

“NanoHTTPD是一种轻量级HTTP服务器,设计用于嵌入其他应用程序,在Modified BSD许可下发布。

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


24
投票

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

NanoHttpd

编辑:这实际上有效!上面的代码看起来像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) 
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)

21
投票

看看“Jetty”网络服务器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); } } 。精湛的开源软件,似乎满足您的所有要求。

如果你坚持自己滚动,那么看看“httpMessage”类。


18
投票

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

  1. 瘦服务器:具有最少处理,上下文或会话处理的服务器静态内容。
  2. 小型服务器:表面上具有许多类似httpD的服务器质量,占用的空间小,可以随意使用。

虽然我可能会考虑像以下的HTTP库:JettyJettyApache Http Components和其他更像是原始HTTP处理设施。标签是非常主观的,取决于您为小型网站提供的服务种类。我在问题的精神上做出了这种区分,尤其是关于......的评论。

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

这些原始工具允许您这样做(如其他答案中所述)。他们并没有真正适应制作轻型,嵌入式或迷你服务器的现成风格。迷你服务器可以提供与全功能网络服务器(例如,Netty)类似的功能,没有花里胡哨,低音量,99%的时间都有良好的性能。瘦服务器似乎比原始语言更接近原始语言,可能只有有限的子集功能,足以让你在90%的时间内看起来很好。我对raw的想法会让我看起来很好75% - 89%的时间没有额外的设计和编码。我认为,如果/当你达到WAR文件的级别时,我们已经为bonsi服务器留下了“小”,看起来像大型服务器做得更小。

瘦服务器选项

迷你服务器选项:

  • NanoHTTPD ...很多东西都可能有很多辅助构造,如过滤器,模板等。
  • Spark Java ...旨在成为盆景,很可能就是这样;-)

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


16
投票

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

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

所以......我开始写FreeMaker

您可以将它作为单个(如果相当长)源文件嵌入到任何项目中,或者作为~50K jar(~35K剥离)嵌入,没有依赖项。它力求符合RFC标准,并包含大量文档和许多有用的功能,同时将膨胀保持在最低限度。

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

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


9
投票

Spark是最简单的,这是一个快速入门指南:JLHTTP - The Java Lightweight HTTP Server


8
投票

在这里可以找到一个非常http://sparkjava.com/ basic web server written in java


7
投票

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

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

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

下面的示例为尚未实现的内容提供基本的servlet支持或throws和UnsupportedOperationException。它使用com.sun.net.httpserver.HttpServer进行基本的http支持。

http://library.sourcerabbit.com/v/?id=19
© www.soinside.com 2019 - 2024. All rights reserved.