首页 文章

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

提问于
浏览
296

有没有办法只使用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

18 回答

  • 8

    我强烈建议你查看Simple,特别是如果你没有't need Servlet capabilities but simply access to the request/reponse objects. If you need REST you can put Jersey on top of it, if you need to output HTML or similar there'的Freemarker . 我真的很喜欢你能用这种组合做什么,并且学习的API相对较少 .

  • 3

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

  • 17

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

    这是他们的文档中的启动示例 copypasted (尽管如此,所有人都试图编辑它,因为它是复制粘贴,而不是我的,而且除非原始源中已更改,否则不应编辑引用) . 你可以复制'n'粘贴'n'在Java 6上运行它 .

    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 static void main(String [] args)throws Exception {
    HttpServer server = HttpServer.create(new InetSocketAddress(8000),0);
    server.createContext(“/ test”,new MyHandler());
    server.setExecutor(NULL); //创建一个默认执行程序
    server.start();
    }

    静态类MyHandler实现HttpHandler {
    @覆盖
    public void handle(HttpExchange t)抛出IOException {
    字符串响应=“这是响应”;
    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.* 类,请注意,与一些开发人员的想法相反,这绝对不是众所周知的FAQ Why Developers Should Not Write Programs That Call 'sun' Packages所禁止的 . 该FAQ涉及 sun.* 包(例如 sun.misc.BASE64Encoder )供Oracle JRE内部使用(因此当您在不同的JRE上运行它时会因您的应用程序而终止),而不是 com.sun.* 包 . Sun / Oracle也只是在Java SE API之上开发软件,就像Apache等所有其他公司一样 . 仅当涉及某个Java API的 implementation 时,不鼓励(但不禁止)使用 com.sun.* 类,例如GlassFish(Java EE impl),Mojarra(JSF impl),Jersey(JAX-RS impl)等 .

  • 21

    看看NanoHttpd

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

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

  • 2

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

    看看"Jetty"网络服务器Jetty . 精湛的开源软件,似乎满足您的所有要求 .

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

  • 25

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

    • Thin-server :具有最少处理,上下文或会话处理的服务器静态内容 .

    • Small-server :表面上看,它具有许多类似httpD的服务器质量,并且占用的空间很小,可以随意使用 .

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

    • "...without writing code to manually parse HTTP requests and manually format HTTP responses..."

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

    瘦服务器选项

    迷你服务器选项:

    • Spark Java ...有很多辅助构造,如过滤器,模板等,可能会有很多好处 .

    • MadVoc ...旨在成为盆景,很可能就是这样;-)

    在需要考虑的其他事项中,我将包括身份验证,验证,国际化,使用类似FreeMaker或其他模板工具来呈现页面输出 . 否则,管理HTML编辑和参数化可能会使HTTP看起来像noughts-n-crosses . 当然,这一切都取决于你需要多么灵活 . 如果它's a menu-driven FAX machine it can be very simple. The more interactions, the '更厚'你的框架需要 . 好问题,祝你好运!

  • 15

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

    • 完全没有轻量级或简单的服务器(对于轻量级的极端定义 . )

    • 真正轻量级的服务器不支持常用的基本功能 .

    所以......我开始写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身体)等

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

  • 3

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

  • 7
  • 1

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

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

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

    下面的示例为尚未实现的内容提供基本的servlet支持或throws和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());
        }
    }
    
  • 2

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

    class 码头:

    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();
        }
    }
    
  • 6

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

    惊人!

  • 9

    看看 takes . 请查看https://github.com/yegor256/takes以获取快速信息

  • 0

    Apache Commons HttpCore项目怎么样?

    来自网站:... HttpCore目标

    • 实现最基本的HTTP传输方面

    • 良好的性能与API的清晰度和表现力之间的 balancer

    • 小(可预测)内存占用

    • 自包含库(除JRE外没有外部依赖项)

  • 41

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

    设置:

    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();
            }
        }
    }
    
  • 5

    你可以写一个非常简单的embedded Jetty Java服务器 .

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

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

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

  • 5

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

    这个api使用套接字创建了一个http服务器 . 细节:
    1.它从浏览器获取文本请求
    2.Parses它检索网址信息,方法,属性等 .
    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; }

相关问题