仅使用 Java SE API 的 Java 中的简单 HTTP 服务器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3732109/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
simple HTTP server in Java using only Java SE API
提问by asker
Is there a way to create a very basic HTTP server (supporting only GET/POST) in Java using just the Java SE API, without writing code to manually parse HTTP requests and manually format HTTP responses? The Java SE API nicely encapsulates the HTTP client functionality in HttpURLConnection, but is there an analog for HTTP server functionality?
有没有办法只使用 Java SE API 在 Java 中创建一个非常基本的 HTTP 服务器(仅支持 GET/POST),而无需编写代码来手动解析 HTTP 请求和手动格式化 HTTP 响应?Java SE API 很好地封装了 HttpURLConnection 中的 HTTP 客户端功能,但是否有类似的 HTTP 服务器功能?
Just to be clear, the problem I have with a lot of ServerSocket examples I've seen online is that they do their own request parsing/response formatting and error handling, which is tedious, error-prone, and not likely to be comprehensive, and I'm trying to avoid it for those reasons.
为了清楚起见,我在网上看到的很多 ServerSocket 示例的问题是它们自己进行请求解析/响应格式化和错误处理,这很乏味,容易出错,而且不太可能全面,由于这些原因,我试图避免它。
As an example of the manual HTTP manipulation that I'm trying to avoid:
作为我试图避免的手动 HTTP 操作的示例:
http://java.sun.com/developer/technicalArticles/Networking/Webserver/WebServercode.html
http://java.sun.com/developer/technicalArticles/Networking/Webserver/WebServercode.html
回答by James Anderson
回答by letronje
Check out NanoHttpd
"NanoHTTPD is a light-weight HTTP server designed for embedding in other applications, released under a Modified BSD licence.
“NanoHTTPD 是一个轻量级的 HTTP 服务器,设计用于嵌入其他应用程序,在修改后的 BSD 许可下发布。
It is being developed at Github and uses Apache Maven for builds & unit testing"
它正在 Github 上开发,并使用 Apache Maven 进行构建和单元测试”
回答by BalusC
Since Java SE 6, there's a builtin HTTP server in SunOracle JRE. The com.sun.net.httpserver
package summaryoutlines the involved classes and contains examples.
从 Java SE 6 开始,SunOracle JRE 中有一个内置的 HTTP 服务器。该com.sun.net.httpserver
包的摘要概述了参与类和包含的例子。
Here's a kickoff example copypastedfrom their docs (to all people trying to edit it nonetheless, because it's an ugly piece of code, please don't, this is a copy paste, not mine, moreover you should never edit quotations unless they have changed in the original source). You can just copy'n'paste'n'run it on Java 6+.
这里有一个开球例如copypasted从自己的文件(所有的人仍然试图编辑它,因为它是一个丑陋的代码,请不要,这是一个复制粘贴,不是我的,而且你永远不应该修改报价,除非他们已经改变在原始来源)。你可以在 Java 6+ 上复制'n'paste'n'run它。
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(); } } }
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(); } } }
Noted should be that the response.length()
part in their example is bad, it should have been response.getBytes().length
. Even then, the getBytes()
method must explicitly specify the charset which you then specify in the response header. Alas, albeit misguiding to starters, it's after all just a basic kickoff example.
应该注意的response.length()
是,他们示例中的部分很糟糕,应该是response.getBytes().length
. 即便如此,该getBytes()
方法也必须明确指定您随后在响应标头中指定的字符集。唉,虽然对初学者有误导,但这毕竟只是一个基本的开球示例。
Execute it and go to http://localhost:8000/testand you'll see the following response:
执行它并转到http://localhost:8000/test,您将看到以下响应:
This is the response
这是回应
As to using com.sun.*
classes, do note that this is, in contrary to what some developers think, absolutely not forbidden by the well known FAQ Why Developers Should Not Write Programs That Call 'sun' Packages. That FAQ concerns the sun.*
package (such as sun.misc.BASE64Encoder
) for internal usage by the Oracle JRE (which would thus kill your application when you run it on a different JRE), not the com.sun.*
package. Sun/Oracle also just develop software on top of the Java SE API themselves like as every other company such as Apache and so on. Using com.sun.*
classes is only discouraged (but not forbidden) when it concerns an implementationof a certain Java API, such as GlassFish (Java EE impl), Mojarra (JSF impl), Jersey (JAX-RS impl), etc.
至于使用com.sun.*
类,请注意,与某些开发人员的想法相反,众所周知的常见问题解答为什么开发人员不应编写调用 'sun' 包的程序绝对没有禁止这样做。该常见问题解答涉及供 Oracle JRE 内部使用的sun.*
包(例如sun.misc.BASE64Encoder
)(因此,当您在不同的 JRE 上运行它时会终止您的应用程序),而不是com.sun.*
包。Sun/Oracle 也只是在 Java SE API 本身之上开发软件,就像其他所有公司(例如 Apache 等)一样。com.sun.*
仅当涉及某个 Java API的实现时不鼓励(但不禁止)使用类,例如 GlassFish(Java EE impl)、Mojarra(JSF impl)、Jersey(JAX-RS impl)等。
回答by Waldheinz
I can strongly recommend looking into Simple, especially if you don'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's Freemarker. I really love what you can do with this combination, and there is relatively little API to learn.
我强烈建议您查看Simple,特别是如果您不需要 Servlet 功能而只需访问请求/响应对象。如果您需要 REST,您可以将 Jersey 放在上面,如果您需要输出 HTML 或类似内容,则可以使用 Freemarker。我真的很喜欢你可以用这个组合做什么,而且需要学习的 API 相对较少。
回答by ThiamTeck
You may also have a look at some NIO application framework such as:
您还可以查看一些 NIO 应用程序框架,例如:
- Netty: http://jboss.org/netty
- Apache Mina: http://mina.apache.org/or its subproject AsyncWeb: http://mina.apache.org/asyncweb/
- Netty:http: //jboss.org/netty
- Apache Mina:http://mina.apache.org/ 或其子项目 AsyncWeb:http://mina.apache.org/asyncweb/
回答by leandro
This code is better than ours, you only need to add 2 libs: javax.servelet.jarand org.mortbay.jetty.jar.
这段代码比我们的好,你只需要添加 2 个库:javax.servelet.jar和org.mortbay.jetty.jar。
Class Jetty:
类码头:
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 class:
服务端类:
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();
}
}
回答by Iqbal
How about Apache Commons HttpCoreproject?
Apache Commons HttpCore项目怎么样?
From the web site:... HttpCore Goals
来自网站:... HttpCore 目标
- Implementation of the most fundamental HTTP transport aspects
- Balance between good performance and the clarity & expressiveness of API
- Small (predictable) memory footprint
- Self contained library (no external dependencies beyond JRE)
- 最基本的 HTTP 传输方面的实现
- 在良好的性能和 API 的清晰性和表现力之间取得平衡
- 小(可预测)内存占用
- 自包含库(没有 JRE 之外的外部依赖项)
回答by f.carlsen
It's possible to create an httpserver that provides basic support for J2EE servlets with just the JDK and the servlet api in a just a few lines of code.
只需几行代码,就可以使用 JDK 和 servlet api 创建一个为 J2EE servlet 提供基本支持的 httpserver。
I've found this very useful for unit testing servlets, as it starts much faster than other lightweight containers (we use jetty for production).
我发现这对于单元测试 servlet 非常有用,因为它比其他轻量级容器(我们使用 jetty 进行生产)启动得快得多。
Most very lightweight httpservers do not provide support for servlets, but we need them, so I thought I'd share.
大多数非常轻量级的 httpservers 不提供对 servlet 的支持,但我们需要它们,所以我想我会分享。
The below example provides basic servlet support, or throws and UnsupportedOperationException for stuff not yet implemented. It uses the com.sun.net.httpserver.HttpServer for basic http support.
下面的示例提供了基本的 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());
}
}
回答by Nikos
A very basic web server written in javacan be found here http://library.sourcerabbit.com/v/?id=19
可以在此处找到 用 Java 编写的非常基本的 Web 服务器http://library.sourcerabbit.com/v/?id=19
回答by gruenewa
The com.sun.net.httpserversolution is not portable across JREs. Its better to use the official webservices API in javax.xml.wsto bootstrap a minimal HTTP server...
该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)
EDIT: this actually works! The above code looks like Groovy or something. Here is a translation to Java which I tested:
编辑:这实际上有效!上面的代码看起来像 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);
}
}