嵌入式jetty相关知识总结

1嵌入式jetty简介

在介绍嵌入式jetty之前,首先介绍一下jetty。

Jetty 是一个开源的servlet容器,它为基于Java的web内容,例如JSP和servlet提供运行环境。Jetty是使用Java语言编写的,它的API以一组JAR包的形式发布。开发人员可以将Jetty容器实例化成一个对象,可以迅速为一些独立运行(stand-alone)的Java应用提供网络和Web连接。

Jetty主要有以下特性:

易用性

易用性是 Jetty 设计的基本原则,易用性主要体现在以下几个方面:

通过 XML 或者 API 来对Jetty进行配置;默认配置可以满足大部分的需求;将 Jetty 嵌入到应用程序当中只需要非常少的代码;

可扩展性

在使用了 Ajax 的 Web 2.0 的应用程序中,每个连接需要保持更长的时间,这样线程和内存的消耗量会急剧的增加。这就使得我们担心整个程序会因为单个组件陷入瓶颈而影响整个程序的性能。但是有了 Jetty:即使在有大量服务请求的情况下,系统的性能也能保持在一个可以接受的状态。利用Continuation 机制来处理大量的用户请求以及时间比较长的连接。 另外 Jetty 设计了非常良好的接口,因此在 Jetty 的某种实现无法满足用户的需要时,用户可以非常方便地对 Jetty 的某些实现进行修改,使得
Jetty 适用于特殊的应用程序的需求。

易嵌入性

Jetty 设计之初就是作为一个优秀的组件来设计的,这也就意味着 Jetty 可以非常容易的嵌入到应用程序当中而不需要程序为了使用 Jetty 做修改。从某种程度上,你也可以把 Jetty 理解为一个嵌入式的Web服务器。

Jetty有一个口号:不要把你的应用部署到Jetty中,把Jetty部署到你的应用中。Jetty可以在Java应用程序中向其他POJO一样被实例化,换句话说,以嵌入式的模式运行Jetty是指将Http模块放入你的应用程序中,而非部署你的程序到一个HTTP服务器。这就是所谓的嵌入式jetty。

JETTY嵌入式Web容器体积小,启动速度快,性能好,免费开源,是一款很适合开发调试和简单演示的轻量级Web容器。而且它的嵌入式的特点,使得开发者可以直接将容器的操作包含在程序逻辑里,得以摆脱TOMCAT,JBOSS等独立容器带来的安装,维护,部署等一系列令人头疼的问题。

2    嵌入式jetty的HTTP实现

2.1简单HTTP实现

2.1.1 HTTP SERVER端实现

2.1.1.1 HTTP SERVER端实现概述

在代码中嵌入一个Jetty server,通常需要以下步骤:

l 创建Server

l 添加/配置Connectors

l 添加/配置Handlers

l 添加/配置Servlets/Webapps到Handlers

l  启动服务器并join

2.1.1.2 创建一个Server

Jetty的Service对象就是Jetty容器,实例化出这样一个对象就产生了一个容器。

Server server = new Server();

可以为Server设置线程池,如果不设置Server处理请求默认为阻塞状态.

server.setThreadPool(newExecutorThreadPool(25, 50, 30000));

简单示例如下:

public class SimplestServer

{

public static void main(String[] args) throwsException

{

Serverserver = new Server(8080);

server.start();

server.join();

}

}

2.1.1.3 配置Connectors

创建Server实例时传入了一个端口号参数,程序内部会创建一个Connector的默认实例,在指定的端口上监听请求。然而,通常嵌入式的Jetty需要为一个Server实例显式地实例化并配置一个或多个Connector。

//实例化

SelectChannelConnector connector = newSelectChannelConnector();

//设置主机地址

connector.setHost("localhost");

//设置端口号

connector.setPort(40404);

/*设置最大空闲时间,最大空闲时间在以下几种情况下应用:

1)  等待一个连接的新的请求时;

2)  读取请求的头信息和内容时;

3)   写响应的头信息和内容时;*/

connector.setMaxIdleTime(30000);

/*为connector设置线程池,如果不设置connector处理请求默认为阻塞状态。如上所示Sever中也有个setThreadPool方法,我觉得如果通过connector进行设置后,会覆盖Sever中的设置。

ExecutorThreadPool构造方法的三个参数分别为:

1)  corePoolSize:线程池的基本大小也就是线程池的目标大小,即在没有任务执行时线程池的大小( 线程池维护线程的最少数量);

2)  maximunPoolSize:线程池最大大小,表示可同时活动的线程数量的上限;

3)  keepAliveTime:存活时间,单位为毫秒,如果某个线程的空闲时间超过了存活时间,那么将被标记为可回收的;

*/

connector.setThreadPool(new ExecutorThreadPool(25, 50,30000));

/*设置这个Server的连接(可以是多个),每个连接有对应的线程池和Handler*/

server.setConnectors(newConnector[] { connector });

嵌入式Jetty服务器的实现的步骤通常为:

2.1.1.4 编写Handlers

为了对每个请求作出相应,Jetty要求为Server设定Handler,Handler可以处理:

1)  检查或修改Http请求

2)  生成完整的Http响应

3)  调用另一个Handler

4)  选择一个或多个Handlers调用

下述代码显示了一个简单HelloHandler的实现:

public class HelloHandler extends AbstractHandler
{
public void handle(String target,Request baseRequest,HttpServletRequest request,HttpServletResponse response)
throwsIOException, ServletException
{
response.setContentType("text/html;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
baseRequest.setHandled(true);
response.getWriter().println("<h1>Hello World</h1>");
}
}
 
handle为AbstractHandler抽象类中定义的抽象方法,我们对请求的处理操作主要通过此方法来完成,handle中参数包括:
target:请求的目标,可以是一个URI或者一个命名dispatcher中的名字。
baseRequest:Jetty的可变请求对象,总是unwrapped。
Request:不变的请求对象,可能会被过滤器或servlet包装
Response:响应,可能会被过滤器或servlet包装
hadler设定响应的状态,内容类型,并在使用writer生成响应体之前标记请求为已处理。

下面是运行Jetty server 使用HelloWorld Handler的例子:

publicstaticvoid main(String[]args)throwsException
{
    Server server=new Server(8080);
server.setHandler(new HelloHandler());
 
server.start();
server.join();
}
 

2.1.1.5 启动Server并join

server.start();

server.join();

至此,嵌入式jetty的Server端基本编写方式已经完成,然而复杂的请求是建立在很多Handlers的基础上的。下面将详细介绍Handlers的其他内容。

2.1.1.6 理解Handler的几个重要概念

复杂的请求处理通常是建立在多个Handler以不同的方式进行组合上的。

Handler Collection 维护一个handlers的集合,顺序调用每一个handler。

Handler List维护一个handlers的集合,依次执行每个handler直到发生异常,或者响应被提交,或者request.isHandled()返回true。

Handler Wrapper:handler的基类,在面向方面编程中能够很好地把一系列处理操作联系在一起。例如,一个标准的Web应用程序是由一连串的context,session,security和servlet处理程序进行执行的。

Context Handler CollectionContextHandler
Collection
利用URI最长的前缀来选择特定的context,即拥有相同的URI最长的前缀的context属于同一个ContextHandler
Collection

2.1.1.7 配置文件服务器

下面的代码使用HandlerList来组合ResourceHandler 和DefaultHandler,其中ResourceHandler可以用于处理当前目录下的静态内容:

public class FileServer
{
    public static void main(String[] args) throws Exception
    {
        Server server = new Server();
        SelectChannelConnector connector = new SelectChannelConnector();
        connector.setPort(8080);
        server.addConnector(connector);
 
        ResourceHandler resource_handler = new ResourceHandler();
        resource_handler.setDirectoriesListed(true);
        resource_handler.setWelcomeFiles(new String[]{ "index.html" });
 
        resource_handler.setResourceBase(".");
 
        HandlerList handlers = new HandlerList();
        handlers.setHandlers(new Handler[] { resource_handler, new DefaultHandler() });
        server.setHandler(handlers);
 
        server.start();
        server.join();
    }
}

HandlerList包括ResourceHandler和DefaultHandler,后者主要用于当请求不匹配静态资源时返回404错误页面,在本例中即如果没发现文件index.html,则请求转向DefaultHandler,返回404错误页面。

2.1.1.8 设置Contexts

ContextHandler 是一个HandlerWrapper,它只响应特定URL的请求。请求不匹配则不予以处理。请求匹配则执行对应的处理程序。

下面是一个使用例子:

public class OneContext
{
    public static void main(String[] args) throws Exception
    {
        Server server = new Server(8080);
 
        ContextHandler context = new ContextHandler();
        context.setContextPath("/hello");
        context.setResourceBase(".");
        context.setClassLoader(Thread.currentThread().getContextClassLoader());
        server.setHandler(context);
 
        context.setHandler(new HelloHandler());
 
        server.start();
        server.join();
    }
}

2.1.1.9 创建 Servlets

Servlets是提供处理HTTP请求的应用逻辑的的标准方式,Servlets强制将特定的URI映射到特定的servlet。下面是HelloServlet的示例代码:

public class HelloServlet extends HttpServlet
{
    private String greeting="Hello World";
    public HelloServlet(){}
    public HelloServlet(String greeting)
    {
        this.greeting=greeting;
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        response.setContentType("text/html");
        response.setStatus(HttpServletResponse.SC_OK);
        response.getWriter().println("<h1>"+greeting+"</h1>");
        response.getWriter().println("session=" + request.getSession(true).getId());
    }
}

2.1.1.10 设置ServletContext

ServletContextHandler是一种支持标准servlets的特殊的ContextHandler。

下面的代码实现了通过ServletContextHandler注册的三个helloworld servlet的实例:

public class OneServletContext
{
    public static void main(String[] args) throws Exception
    {
        Server server = new Server(8080);
 
        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.setContextPath("/");
        server.setHandler(context);
 
        context.addServlet(new ServletHolder(new HelloServlet()),"/*");
        context.addServlet(new ServletHolder(new HelloServlet("Buongiorno Mondo")),"/it/*");
        context.addServlet(new ServletHolder(new HelloServlet("Bonjour le Monde")),"/fr/*");
 
        server.start();
        server.join();
    }
}

2.1.1.11 设置Web Application Context

Web Applications context是ServletContextHandler的一个变化,它使用标准布局和web.xml来配置servlets
,filters等。

示例如下:

public class OneWebApp
{
    public static void main(String[] args) throws Exception
    {
        String jetty_home = System.getProperty("jetty.home","..");
 
        Server server = new Server(8080);
 
        WebAppContext webapp = new WebAppContext();
        webapp.setContextPath("/");
        webapp.setWar(jetty_home+"/webapps/test.war");
        server.setHandler(webapp);
 
        server.start();
        server.join();
    }
}

在开发过程中如果你还没有把你的web应用变成WAR文件,那么你可以这样实现:

public classOneWebAppUnassembled

{
    public static void main(String[] args) throws Exception
    {
        Server server = new Server(8080);
 
        WebAppContext context = new WebAppContext();
        context.setDescriptor(webapp+"/WEB-INF/web.xml");
        context.setResourceBase("../test-jetty-webapp/src/main/webapp");
        context.setContextPath("/");
        context.setParentLoaderPriority(true);
 
        server.setHandler(context);
 
        server.start();
        server.join();
    }
}

2.1.1.12 配置Context Handler Collection

Context
Handler Collection
利用URI最长的前缀来选择特定的context,即拥有相同的URI最长的前缀的context属于同一个Context
Handler Collection

示例如下:

public class ManyContexts

{

public static void main(String[] args) throws Exception

{

Serverserver = new Server(8080);

ServletContextHandler context0 = newServletContextHandler(ServletContextHandler.SESSIONS);

context0.setContextPath("/ctx0");

context0.addServlet(new ServletHolder(newHelloServlet()),"/*");

context0.addServlet(new ServletHolder(newHelloServlet("Buongiorno Mondo")),"/it/*");

context0.addServlet(new ServletHolder(newHelloServlet("Bonjour le Monde")),"/fr/*");

WebAppContextwebapp = new WebAppContext();

webapp.setContextPath("/ctx1");

webapp.setWar(jetty_home+"/webapps/test.war");

ContextHandlerCollection contexts = newContextHandlerCollection();

contexts.setHandlers(new Handler[] { context0, webapp });

server.setHandler(contexts);

server.start();

server.join();

}

}

2.1.2 HTTP CLIENT端实现

2.1.2.1 HttpClient设置

在和HTTP服务器通信前,需要先设置HttpClient,然后启动它。

HttpClient
client = new HttpClient();

client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);

client.start();

还可以为HttpClient添加其他的设置:

HttpClient client = newHttpClient();

client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);

// 设置客户端连接每个服务器地址的的最大连接数

client.setMaxConnectionsPerAddress(200);

// 每个client最多起250个线程

client.setThreadPool(newQueuedThreadPool(250));

// 设置超时时间,如果这么长时间服务器都没有响应的话,则请求失败。

client.setTimeout(30000);

client.start();

一定要在HttpClient启动之前配置相关参数,否则配置无效。

正因为HttpClient没有关于HTTP服务器特定地址的设置,所以它可以同时连接多个服务器。

2.1.2.2异步请求

一旦HttpClient被设置以后,使用HttpClient.send(HttpExchange exchange)方法,客户端和服务器间的通信就被发起了。

exchange 必须设置;两个重要的参数:要连接的地址和请求的URI。示例如下:

HttpExchange exchange = newHttpExchange();

// Optionally set the HTTP method

exchange.setMethod("POST");

exchange.setAddress(newAddress("ping.host.com", 80));

exchange.setURI("/ping");

// Or, equivalently, this:

exchange.setURL("http://ping.host.com/ping");

client.send(exchange);

System.out.println("Exchangesent");

client.send(exchange)在把exchange分配到线程池执行后会立刻返回,所以System.out方法在client.send(exchange)执行后会立刻执行。所以exchange既有可能在System.out之后执行也有可能在System.out之前执行。

2.1.2.3 请求过程控制

HttpExchange提供了以下可被重写的回调函数来判断一个exchange所处的状态:

  • onRequestCommitted(), 当请求行和请求头被发送到HTTP服务器后调用。
  • onRequestComplete(),当请求内容被发送到HTTP服务器后调用。
  • onResponseStatus(Buffer httpVersion, int statusCode, Buffer statusMessage), 当响应行被处理时调用; 三个参数分别表示HTTP版本号(e.g. "HTTP/1.1"),服务器响应状态码(e.g. 200),服务器响应状态信息(e.g. "OK")。
  • onResponseHeader(Buffer name, Buffer value), 在每个响应头信息被处理时调用; 两个参数分别表示每个息的名字(e.g. "Content-Length")和值(e.g. "16384")。
  • onResponseHeaderComplete(), 当所有的响应头信息被处理完后调用。
  • onResponseContent(Buffer content), 收到每个响应内容块后都被调用。参数表示对应响应内容块的内容。
  • onResponseComplete(), 在响应内容被完全收到后调用。

下面是非正常条件下的回调函数:

  • onConnectionFailed(Throwable x), 当尝试与服务器进行连接失败时调用。参数表示尝试连接失败后的异常信息。
  • onException(Throwable x), 当与服务器连接成功但后来发生错误时调用。参数表示产生的异常信息。
  • onExpire(), 服务器响应超时时调用。
  • onRetry(), 当exchange 被重新发送时调用。

通常,我们通过重写onResponseComplete()函数来实现服务器响应信息的获取(比如响应头和响应体)和执行其他的操作:

ContentExchange exchange = newContentExchange(true)

{

protected void onResponseComplete() throwsIOException

{

int status = getResponseStatus();

if (status == 200)

doSomething();

else

handleError();

}

};

下面给出一个进行过程控制的异步请求的例子:

packagetest.jetty;

importjava.io.IOException;

importorg.eclipse.jetty.client.Address;

importorg.eclipse.jetty.client.HttpClient;

importorg.eclipse.jetty.client.HttpExchange;

importorg.eclipse.jetty.io.Buffer;

importorg.eclipse.jetty.util.thread.QueuedThreadPool;

public class TestClient{

public static void main(String[] args) throwsException {

HttpClient client = new HttpClient();

client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);

client.setThreadPool(new QueuedThreadPool(50));

client.setMaxConnectionsPerAddress(10);

client.setTimeout(5000);

//启动之前先配置好

client.start();

HttpExchange exchange = new HttpExchange(){

@Override

public void onConnectionFailed(Throwable arg0) {

// TODO Auto-generated method stub

System.err.println("failed to connect webserver");

}

@Override

public void onException(Throwable arg0) {

// TODO Auto-generated method stub

System.err.println("unknown error");

}

@Override

public void onExpire() {

// TODO Auto-generated method stub

System.err.println("timeout");

}

@Override

public void onRequestCommitted() throws IOException{

// TODO Auto-generated method stub

System.out.println("request commited");

}

@Override

public void onRequestComplete() throws IOException{

// TODO Auto-generated method stub

System.out.println("request complete");

}

@Override

public void onResponseComplete() throws IOException{

// TODO Auto-generated method stub

System.out.println("responsecomplete");

}

@Override

public void onResponseContent(Buffer arg0) throwsIOException {

// TODO Auto-generated method stub

System.out.println("response content");

System.out.println(arg0.toString());

}

@Override

public void onResponseHeader(Buffer arg0, Buffer arg1)throws IOException {

// TODO Auto-generated method stub

System.out.println("response header");

System.out.println(arg0.toString() + " " +arg1.toString());

}

@Override

public void onResponseStatus(Buffer arg0, int arg1,Buffer arg2)

throwsIOException {

// TODO Auto-generated method stub

System.out.println("response status");

System.out.println(arg0.toString() + " " +arg1 + " " + arg2.toString());

}

@Override

public void onRetry() {

// TODO Auto-generated method stub

System.out.println("retry request");

}

@Override

public void onResponseHeaderComplete() throwsIOException {

// TODO Auto-generated method stub

System.out.println("response headercomplete");

}

};

exchange.setMethod("GET");

exchange.setAddress(newAddress("www.baidu.com",80));

exchange.setRequestURI("/");

//client.send会立即返回,exchange会被分发给线程池去处理,

client.send(exchange);

System.out.println("send");

}

}

2.1.2.4 同步请求

异步请求在性能上表现的最好,然而有时候我们需要使用同步请求来实现自己的需求,我们可以使用HttpExchange.waitForDone()方法来实现同步请求。

HttpClient client = new HttpClient();

client.start();

ContentExchange exchange = newContentExchange(true);

exchange.setURL("http://foobar.com/baz");

client.send(exchange);

// Waits until the exchange is terminated

int exchangeState = exchange.waitForDone();

if (exchangeState == HttpExchange.STATUS_COMPLETED)

doSomething();

else if (exchangeState ==HttpExchange.STATUS_EXCEPTED)

handleError();

else if (exchangeState ==HttpExchange.STATUS_EXPIRED)

handleSlowServer();

waitForDone()方法会一直处于等待状态,直到exchange成功执行完,或者发生错误,或者响应超时。

2.2 HTTP认证实现

2.2.1HTTP对内认证的实现

2.2.1.1 对内认证需求

在http实现的过程中,有些url只用于程序内部的httpClient访问,这些url不需要或者说不能对外开放,这就需要程序内部得有这样一个机制,对于某些特定的url只允许程序内部访问,不允许在程序外部进行访问。

2.2.1.2 对内认证实现方式

为实现上述需求,我觉得一个很好的解决办法是,对于程序内部的httpClient,在发送请求的时候,可以在头信息中增加一个自定义的头信息,该信息不对外开放,比如:名字为check,值为123456(该值只有程序内部知道),HTTP服务端在收到头信息时要验证这一项,如果有名字为check的头信息,并且值和我们内部约定的一样,则执行相应操作,否则不执行。

下面是一个实现的demo:

Server端:

package test;

importjava.io.FileNotFoundException;

importorg.eclipse.jetty.server.Connector;

importorg.eclipse.jetty.server.Handler;

importorg.eclipse.jetty.server.Server;

importorg.eclipse.jetty.server.handler.ContextHandler;

importorg.eclipse.jetty.server.handler.ContextHandlerCollection;

import org.eclipse.jetty.server.handler.DefaultHandler;

importorg.eclipse.jetty.server.handler.HandlerCollection;

importorg.eclipse.jetty.server.nio.SelectChannelConnector;

public classSServer {

private Server server = null;

public static final String HOST ="192.168.0.158";

public static final int POST = 40404;

public static void main(String[] args)throws FileNotFoundException{

new SServer().startHttpServer();

}

public void startHttpServer() throwsFileNotFoundException {

server = new Server();

SelectChannelConnector connector= new SelectChannelConnector();

connector.setHost(HOST);

connector.setPort(POST);

server.setConnectors(newConnector[] { connector });

ContextHandler test = newContextHandler();

test.setContextPath("/test");

Handler handler = newHHandler();

test.setHandler(handler);

ContextHandlerCollectioncontexts = new ContextHandlerCollection();

contexts.setHandlers(newHandler[] { test });

HandlerCollection handlers = newHandlerCollection();

handlers.setHandlers(newHandler[] { contexts, new DefaultHandler() });

server.setHandler(handlers);

start();

}

private void start() {

try {

server.start();

server.join();

} catch (Exception e) {

e.printStackTrace();

}

}

}

Handle类:

package test;

importjava.io.IOException;

importjavax.servlet.ServletException;

importjavax.servlet.http.HttpServletRequest;

importjavax.servlet.http.HttpServletResponse;

importorg.eclipse.jetty.server.Request;

import org.eclipse.jetty.server.handler.AbstractHandler;

public classHHandler extends AbstractHandler {

public HHandler() {

}

@Override

public void handle(final String target,final Request request, final HttpServletRequest servlet_request,

final HttpServletResponse response)throws IOException, ServletException {

if(request.getHeader("check")!=null&&request.getHeader("check").equals("123456")){

System.out.println(request.getHeader("check"));

response.setContentType("text/html;charset=utf-8");

response.setStatus(HttpServletResponse.SC_OK);

request.setHandled(true);

response.getWriter().println("<h1>HelloWorld</h1>");

}

}

}

 

Client端:

package test;

importorg.eclipse.jetty.client.HttpClient;

import org.eclipse.jetty.client.HttpExchange;

importorg.eclipse.jetty.util.thread.QueuedThreadPool;

public classCClient {

/**

*@param args

*/

public static void main(String[] args)throwsException {

HttpClient client = newHttpClient();

client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);

client.setThreadPool(newQueuedThreadPool(50));

client.setMaxConnectionsPerAddress(10);

client.setTimeout(5000);

//启动之前先配置好

client.start();

HttpExchange exchange = newHttpExchange();

exchange.setMethod("POST");

exchange.setURL("http://192.168.0.158:40404/test/");

exchange.setRequestHeader("check", "123456");

client.send(exchange);

}

}

2.2.2HTTP对外认证的实现

2.2.2.1 对外认证需求

在http实现的过程中,对外开放的url中有些只能被特定的用户访问,为了避免所有的用户都能够访问所有的url,我们需要对不同的用户设置不同的权限,来限制某些用户访问其权限范围之外的url。

2.2.2.2 对外认证实现方式

为了给不同的用户赋予不同的权限,需要给每个用户名设置用户名和密码。所以需要使用数据库记录相关用户名,密码以及权限。

用户在向服务器发送请求的同时,需要向服务器发送自己的用户名及密码,服务器对用户名及密码进行验证从而确定该用户是否合法以及该用户是否有访问该url的权限。

对于调用直接调用程序接口访问url的用户来说,可以通过post方式,将用户名和密码放到请求体(请求内容)中发送给HTTP服务器。服务端接收到请求内容后进行解析。例如:

Server端:

package test;

importjava.io.FileNotFoundException;

import org.eclipse.jetty.server.Connector;

importorg.eclipse.jetty.server.Handler;

importorg.eclipse.jetty.server.Server;

importorg.eclipse.jetty.server.handler.ContextHandler;

importorg.eclipse.jetty.server.handler.ContextHandlerCollection;

import org.eclipse.jetty.server.handler.DefaultHandler;

importorg.eclipse.jetty.server.handler.HandlerCollection;

importorg.eclipse.jetty.server.nio.SelectChannelConnector;

public classSServer {

private Server server = null;

public static final String HOST = "192.168.0.158";

public static final int POST = 40404;

public static void main(String[] args)throws FileNotFoundException{

new SServer().startHttpServer();

}

public void startHttpServer() throwsFileNotFoundException {

server = new Server();

SelectChannelConnector connector= new SelectChannelConnector();

connector.setHost(HOST);

connector.setPort(POST);

server.setConnectors(newConnector[] { connector });

ContextHandler test = newContextHandler();

test.setContextPath("/test");

Handler handler = newHHandler();

test.setHandler(handler);

ContextHandlerCollectioncontexts = new ContextHandlerCollection();

contexts.setHandlers(newHandler[] { test });

HandlerCollection handlers = newHandlerCollection();

handlers.setHandlers(newHandler[] { contexts, new DefaultHandler() });

server.setHandler(handlers);

start();

}

private void start() {

try {

server.start();

server.join();

} catch (Exception e) {

e.printStackTrace();

}

}

}

Handle类:

package test;

importjava.io.ByteArrayOutputStream;

importjava.io.IOException;

importjava.io.InputStream;

importjavax.servlet.ServletException;

importjavax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

importorg.eclipse.jetty.http.HttpStatus;

importorg.eclipse.jetty.server.Request;

importorg.eclipse.jetty.server.handler.AbstractHandler;

public classHHandler extends AbstractHandler {

public HHandler() {

}

@Override

public void handle(final String target,final Request request, final HttpServletRequest servlet_request,

final HttpServletResponse response)throws IOException, ServletException {

request.setHandled(true);

InputStream inputStream = request.getInputStream();

ByteArrayOutputStream out = newByteArrayOutputStream();

byte[] b = new byte[1024];

int i = 0;

while ((i = inputStream.read(b, 0,1024)) > 0) {

out.write(b, 0, i);

}

// 处理获取到的数据

try {

String str=out.toString();

if(str.equals("www:123456")){

System.out.println(str);

response.getWriter().write("succeed");

response.setStatus(HttpStatus.OK_200);

}

} catch (Exception e){//NotExistsException

response.getWriter().write("Serverrespone error:" + e.getMessage());

response.setStatus(HttpStatus.FAILED_DEPENDENCY_424);

} finally {

out.close();

inputStream.close();

b = null;

}

response.getWriter().flush();

response.getWriter().close();

}

}

Client端:

package test;

import org.eclipse.jetty.client.HttpClient;

importorg.eclipse.jetty.client.HttpExchange;

importorg.eclipse.jetty.io.ByteArrayBuffer;

importorg.eclipse.jetty.util.thread.QueuedThreadPool;

public classCClient {

/**

*@param args

*/

public static void main(String[] args)throwsException {

testClient("www","123456");

}

public static void testClient(String name,String pwd)throws Exception{

HttpClient client = newHttpClient();

client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);

client.setThreadPool(newQueuedThreadPool(50));

client.setMaxConnectionsPerAddress(10);

client.setTimeout(5000);

//启动之前先配置好

client.start();

HttpExchange exchange = newHttpExchange();

exchange.setMethod("POST");

exchange.setURL("http://192.168.0.158:40404/test/");

exchange.setRequestContent(newByteArrayBuffer(name+":"+pwd));

client.send(exchange);

}

}

对于浏览器用户,有两种方式向服务器发送用户名和密码:

1)   在请求的URL地址后以?的形式直接带上用户名和密码交给服务器。如:http://127.0.0.1:8341/upload/testf001?name=abc&password=xyz;这种方式最大的缺点是将用户名和密码暴露在url中,容易被人获取,没有安全性保障。

2)  设置访问界面,在界面中同时输入用户名,密码,以及要访问的url,点击提交按钮,以post方式提交用户名及密码,验证通过后转至相应url的页面。

2.3 HTTP KEEP ALIVE实现

在HTTP1.0和HTTP1.1协议中都有对长连接的支持。其中HTTP1.0需要在request中增加”Connection: keep-alive“ header才能够支持,而HTTP1.1默认支持。

http1.0请求与服务端的交互过程:

1)   客户端发出带有包含一个header:”Connection: keep-alive“的请求;

2)   服务端接收到这个请求后,根据http1.0和”Connection: keep-alive“判断出这是一个长连接,就会在response的header中也增加”Connection: keep-alive“,同是不会关闭已建立的tcp连接。

3)   客户端收到服务端的response后,发现其中包含”Connection:keep-alive“,就认为是一个长连接,不关闭这个连接。并用该连接再发送request。转到第一步。

http1.1请求与服务端的交互过程:

客户端发出http1.1的请求

1)   服务端收到http1.1后就认为这是一个长连接,会在返回的response设置Connection: keep-alive,同时不会关闭已建立的连接.

2)   客户端收到服务端的response后,发现其中包含“Connection: keep-alive”,就认为是一个长连接,不关闭这个连接。并用该连接再发送request。转到第一步。

所以对于http1.1版本来说,默认情况下就是长连接,我们没有必要在客户端的请求头中设置“Connection: keep-alive”,当然我们也可以显式地设置,如:

HttpClientclient = new HttpClient();

client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);

client.setThreadPool(newQueuedThreadPool(50));

client.setMaxConnectionsPerAddress(10);

client.setTimeout(5000);

//启动之前先配置好

client.start();

HttpExchange exchange = newHttpExchange();

exchange.setMethod("POST");

exchange.setURL("http://192.168.0.158:40404/test/");

exchange.setRequestHeader("Connection","Keep-Alive");

client.send(exchange);

基于http协议的长连接减少了请求,减少了建立连接的时间,但是每次交互都是由客户端发起的,客户端发送消息,服务端才能返回客户端消息.因为客户端也不知道服务端什么时候会把结果准备好,所以客户端的很多请求是多余的,仅是维持一个心跳,浪费了带宽.

2.4 HTTPS的实现

2.4.1生成keystore

切换到JAVA_HOME/bin目录下

使用keytool命令,创建keystore。例如:

./keytool -keystorefstoreKS -alias fstore -genkey -keyalg RSA

Enter keystorepassword:

Re-enter newpassword:

What is your firstand last name?

[Unknown]: fstore

What is the name ofyour organizational unit?

[Unknown]: fstore

What is the name ofyour organization?

[Unknown]: fstore

What is the name ofyour City or Locality?

[Unknown]: bj

What is the name ofyour State or Province?

[Unknown]: bj

What is thetwo-letter country code for this unit?

[Unknown]: cn

Is CN=fstore,OU=fstore, O=fstore, L=bj, ST=bj, C=cn correct?

[no]: yes

Enter key passwordfor <fstore>

(RETURN if same as keystore password):

Re-enter newpassword:

需要用户输入keystore密码以及key密码,最后,会在当前目录下生成一个fstoreKS文件,这就是keystore文件

2.4.2服务端设置ssl连接

为jetty Server设置ssl Connector,需要指定keystore路径, keystore密码以及key密码。样例程序如下:

Server server = new Server();

SslSelectChannelConnector ssl_connector =new SslSelectChannelConnector();

ssl_connector.setHost("192.168.0.158");

ssl_connector.setPort(40404);

ContextHandler test = newContextHandler();

test.setContextPath("/test");

Handler handler = new HHandler();

test.setHandler(handler);

ContextHandlerCollection contexts = newContextHandlerCollection();

contexts.setHandlers(new Handler[] {test });

HandlerCollection handlers = newHandlerCollection();

handlers.setHandlers(new Handler[] {contexts, new DefaultHandler() });

server.setHandler(handlers);

SslContextFactory cf =ssl_connector.getSslContextFactory();

cf.setKeyStore("D:/fstoreKS");

cf.setKeyStorePassword("123456");

cf.setKeyManagerPassword("123456");

server.addConnector(ssl_connector);

try {

server.start();

server.join();

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

2.4.3https客户端实现

2.4.3.1 获取证书并导入本地keystore

1)   用IE打开需要连接的https网址,会弹出如下对话框:

2)   单击"查看证书",在弹出的对话框中选择"详细信息",然后再单击"复制到文件",根据提供的向导生成待访问网页的证书文件;

3)   向导第一步,欢迎界面,直接单击"Next";

4)   向导第二步,选择导出的文件格式,默认,单击"Next";

5)   向导第三步,输入导出的文件名,输入后,单击"Next";

6)   向导第四步,单击"Finish",完成向导;

7)   最后弹出一个对话框,显示导出成功

8)  用keytool工具把刚才导出的证书倒入本地keystore。如:

keytool -import -noprompt -keystore mycacerts -storepass 123456-alias fstore -file fstore_ks.cer

其中mycacerts是要导出到的keystore文件名,可自己命名;123456是指定密钥库的密码,自己设置;fstore是keystore关联的独一无二的别名,自己设置;fstore_ks.cer是上面生成证书的路径,根据实际情况设置。

2.4.3.1 编写客户端程序

在程序中通过System.setProperty()方法设置keystore路径,并把要访问的url路径的http改为https即可。示例程序如下:

packagetest;

importorg.eclipse.jetty.client.HttpClient;

importorg.eclipse.jetty.client.HttpExchange;

importorg.eclipse.jetty.io.ByteArrayBuffer;

importorg.eclipse.jetty.util.ssl.SslContextFactory;

importorg.eclipse.jetty.util.thread.QueuedThreadPool;

publicclass CClient {

/**

*@param args

*/

public static void main(String[] args)throwsException {

testClient("www","123456");

}

public static void testClient(String name,String pwd)throws Exception{

SslContextFactory sslCF=newSslContextFactory();

sslCF.setTrustAll(false);

System.setProperty("javax.net.ssl.trustStore","D:/mycacerts");

HttpClient client = newHttpClient(sslCF);

client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);

client.setThreadPool(newQueuedThreadPool(50));

client.setMaxConnectionsPerAddress(10);

client.setTimeout(5000);

//启动之前先配置好

client.start();

HttpExchange exchange = new HttpExchange();

exchange.setMethod("POST");

exchange.setURL("https://192.168.0.158:40404/test/");

exchange.setRequestContent(newByteArrayBuffer(name+":"+pwd));

client.send(exchange);

}

}

2.5 HTTP curl上传以及浏览器上传实现

2.5.1       需求分析

在基于http的开发中,有时我们需要通过浏览器和curl命令上传本地文件到服务器,与直接通过jetty的HttpClient上传文件内容不同,通过浏览器和curl上传本地时,请求内容中不仅仅只是文件内容,同时包含一些额外的http信息,如:

--ghFsUA_25eQJixJIK-SxXGwGsn_HTc4zaS

Content-Disposition:form-data; name="filename"; filename="we.txt"

Content-Type:application/octet-stream; charset=ISO-8859-1

Content-Transfer-Encoding:binary

…………

--ghFsUA_25eQJixJIK-SxXGwGsn_HTc4zaS--

所以我们需要区分实际要上传的文件内容和无关信息,从而实现浏览器和curl命令上传本地文件到服务器。

2.5.2实现方式

服务器处理请求时不能采用原先的Handler方式,而是jetty服务端设置ServletContext,通过在Servlet程序中重写doPost方法来实现,在doPost方法中取出对应文件内容的part中的内容,并进行处理。

程序示例如下:

Server端:

packagecn.ac.iie.fstore.http.server;

importjava.io.FileNotFoundException;

importmy.javax.servlet.MultipartConfigElement;

importmy.org.eclipse.jetty.server.Connector;

import my.org.eclipse.jetty.server.Handler;

importmy.org.eclipse.jetty.server.Server;

importmy.org.eclipse.jetty.server.handler.ContextHandlerCollection;

importmy.org.eclipse.jetty.server.handler.DefaultHandler;

import my.org.eclipse.jetty.server.handler.HandlerCollection;

importmy.org.eclipse.jetty.server.nio.SelectChannelConnector;

importmy.org.eclipse.jetty.servlet.ServletContextHandler;

importmy.org.eclipse.jetty.servlet.ServletHolder;

public classSServer {

private Server server = null;

public static final String HOST ="192.168.0.158";

public static final int POST = 40404;

public static void main(String[] args)throws FileNotFoundException{

new SServer().startHttpServer();

}

public void startHttpServer() throwsFileNotFoundException {

server = new Server();

SelectChannelConnector connector = newSelectChannelConnector();

connector.setHost(HOST);

connector.setPort(POST);

server.setConnectors(new Connector[] {connector });

ServletContextHandler test = newServletContextHandler(ServletContextHandler.SESSIONS);

test.setContextPath("/test");

ServletHolder mainHolder = newServletHolder(new HHandler());

mainHolder.getRegistration().setMultipartConfig(

new MultipartConfigElement("data/tmp",250 * 1024 * 1024, 250 * 1024 * 1024, 250 * 1024 * 1024));

test.addServlet(mainHolder,"/*");

ContextHandlerCollection contexts = newContextHandlerCollection();

contexts.setHandlers(new Handler[] {test });

HandlerCollection handlers = newHandlerCollection();

handlers.setHandlers(new Handler[] {contexts, new DefaultHandler() });

server.setHandler(handlers);

start();

}

private void start() {

try {

server.start();

server.join();

} catch (Exception e) {

e.printStackTrace();

}

}

}

处理类:

packagecn.ac.iie.fstore.http.server;

importjava.io.ByteArrayOutputStream;

importjava.io.IOException;

importjava.io.InputStream;

importmy.javax.servlet.ServletException;

importmy.javax.servlet.http.HttpServlet;

importmy.javax.servlet.http.HttpServletRequest;

importmy.javax.servlet.http.HttpServletResponse;

importmy.javax.servlet.http.Part;

importmy.org.eclipse.jetty.http.HttpStatus;

importcn.ac.iie.fstore.http.util.HttpServerContantVal;

importcn.ac.iie.fstore.util.ConstantVal;

public classHHandler extends HttpServlet {

private final int fileNameLength=250;

@Override

protected void doPost(HttpServletRequestrequest, HttpServletResponse response) throws ServletException, IOException {

// 获得上传文件的输入流

Part part =request.getParts().iterator().next();

//      Part part =request.getPart(HttpServerContantVal._FILENAME);

final InputStream inputStream =part.getInputStream();

final ByteArrayOutputStream outputStream= new ByteArrayOutputStream();

// 获得上传文件的长度

// final int dataLength = (int)part.getSize();

String filename =request.getPathInfo().substring(1);

String namespace =request.getParameter(HttpServerContantVal._NAMESPACE);

if (null == filename ||filename.trim().length() == 0) {

String partName = part.getName();

if (partName != null &&partName.trim().length() > 0) {

filename = partName;

}else{

response.setStatus(HttpStatus.FORBIDDEN_403);

response.getWriter().write("Checkfile name and name length");

response.getWriter().flush();

response.getWriter().close();

return;

}

}

if (filename.trim().length() >=fileNameLength) {

response.setStatus(HttpStatus.FORBIDDEN_403);

response.getWriter().write("Filename is too long");

response.getWriter().flush();

response.getWriter().close();

return;

}

if (null == namespace ||namespace.trim().length() == 0) {

namespace = ConstantVal.DEFAULTPATH;

}

// 读取文件内容到缓冲

byte[] b = new byte[1024];

int i = 0;

while ((i = inputStream.read(b, 0,1024)) > 0) {

outputStream.write(b, 0, i);

}

try {

System.out.println(outputStream.toString());

response.setStatus(HttpStatus.OK_200);

response.getWriter().write("OK");

response.getWriter().flush();

response.getWriter().close();

} catch (Exception e) {

response.setStatus(HttpStatus.FORBIDDEN_403);

response.getWriter().write("Forbidden\nFstoreerror");

response.getWriter().flush();

response.getWriter().close();

}

}

}

Html脚本:

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<title>download file bypost</title>

</head>

<body>

<form id="form1"action="http://192.168.0.158:40404/test/" method="POST"enctype ="multipart/form-data">

<input id="filename"type="file" name="filename" />

<input type="submit"value="submit1" />

</form>

</body>

</html>

在这种方式下,低版本的jetty是不支持的,因为

mainHolder.getRegistration().setMultipartConfig(newMultipartConfigElement("data/tmp", 250 * 1024 * 1024, 250 * 1024 *1024, 250 * 1024 * 1024));方法是从jetty v8.x以后才支持的。

时间: 2024-10-18 12:44:02

嵌入式jetty相关知识总结的相关文章

深入浅出安卓学习相关知识,如何从零学好移动开发

原文发表自我的个人主页,欢迎大家访问 http://purplesword.info/mobile-develop 由于近几年来互联网的飞速发展,安卓和iOS平台的大量普及推广,移动开发在当前是非常热门的一个方向. 有不少同学问我如何学习安卓,要学些什么,难不难学.之前一直没有想好应该怎么回答这个问题,只是简单的说安卓自身门槛不高,并不难学.因为我觉得准确回答一个类似这样的问题往往需要灵感.现在根据我的学习体验,做个大概的总结. 1.我为什么学安卓 我从刚开始接触安卓开发到现在也有两三年的时间了

数据库原理相关知识

数据库原理相关知识 made by @杨领well([email protected]) 一.基础知识 1. 简述数据库系统的特点. 数据结构化 : 这是数据库系统与文件系统的本质区别. 数据的共享性高.冗余度低且易扩充 : 数据共享可以大大减少数据冗余, 节约存储空间.数据共享还能够避免数据之间的不相容性和不一致性. 数据的独立性高 : 数据独立性包括物理独立性和逻辑独立性. 数据由数据库管理系统统一管理和控制 : 数据的安全性保护(保护数据以防止不合法使用造成的数据泄密和破坏).数据的完整性

嵌入式jetty的HTTP实现

2    嵌入式jetty的HTTP实现 2.1 简单HTTP实现 2.1.1 HTTP SERVER端实现 2.1.1.1 HTTP SERVER端实现概述 在代码中嵌入一个Jetty server,通常需要以下步骤: l 创建Server l 添加/配置Connectors l 添加/配置Handlers l 添加/配置Servlets/Webapps到Handlers l  启动服务器并join 2.1.1.2 创建一个Server Jetty的Service对象就是Jetty容器,实例化

OpenCV&amp;Qt学习之四——OpenCV 实现人脸检测与相关知识整理

开发配置 OpenCV的例程中已经带有了人脸检测的例程,位置在:OpenCV\samples\facedetect.cpp文件,OpenCV的安装与这个例子的测试可以参考我之前的博文Linux 下编译安装OpenCV. 网上能够找到关于OpenCV人脸检测的例子也比较多,大多也都是基于这个例程来更改,只是多数使用的是OpenCV 1.0的版本,而OpenCV2.0以后由于模块结构的更改,很多人并没有将例程运行起来.如果是新版的OpenCV跑旧的例程,编译运行出错的话,需要确保: #include

python的list相关知识

关于list的相关知识 list01 = ['alex',12,65,'xiaodong',100,'chen',5] list02 = [67,7,'jinjiao_dawang','relax1949',53] #打印list01.list02 print(list01) print(list02) #列表截取.切片 print(list01[1]) print(list01[-2]) print(list01[1:3]) #列表重复 print(list01 * 3) #列表组合 prin

三层交换机相关知识

三层交换机相关知识 这次的作死之路又要开始了.这次的对象主要是交换机:还是三层的: 这是这次实验的总体用图: 现在现根据图上的标志:将所有的主机配置好:目前没有做任何vlan:所以PC1和PC3是能够互通的: 接下来:我想先去探索下三层交换机关闭portswitch会怎么样: 第一步:先关闭了再说: 因为按照图中的设计:PC1的帧如果想要到达PC2,那么就必然要经过LSW1.但是现在我关闭了g0/0/1端口的portswitch:现在pc1并不能ping通pc2: 通过百度:三层交换机的端口不仅

php学习day7--函数的相关知识

今天我们主要学了函数的相关知识,是个比较基础的知识,但也是很重要的. 一.函数 函数就类似于一个工具,我们写好函数之后可以直接进行调用,可以很大的减少代码的从用性,提高页面性能和可读性. 1.函数的定义 在php中函数的定义方式为: function  name($形参1,$形参2.....){ 要执行的代码 return  123: } 在上方的函数定义式中,name代表函数名,小括号内是形参,是用来传递参数,花括号中的就是调用时需要执行的代码. 函数的调用方式: name(实参1,实参2,.

svn常规操作与相关知识

Svn常规操作与相关知识 一.何谓版本控制 它是一种软件工程籍以在开发的过程中,确保由不同人所编辑的同一档案都得到更新,它透过文档控制记录程序各个模块的改动,并为每次改动编上序号,并且编辑错误之后还可以回溯到以前的版本 二.可供我们选择的版本控制系统 1.VCS  (本地版本控制) 2.VSS.CVS(集中版本控制) 3.ClearCase.SVN.Perforce.TFS(集中版本控制) 4.Mercurial(水银/水星).Git(分布式版本控制) 差异: 1.Git和其他版本控制系统的主要

黑马程序员---Objective-C基础学习---类、对象、方法相关知识笔记

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 类.对象.方法相关知识笔记 Objective-C相对于C语言有了面向对象的特性,但是ObjC又没有其他面向对象语言那么多语法特性,ObjC本身对面向对象进行了精简.下面是一些相关知识笔记. 类定义 成员变量 方法和属性 self关键字 类定义 在C#.Java等其他高级语言中定义一个类是相当简单点的,直接一个关键字class加一对大括号基本就完成了,但是在ObjC中类的定义相对变化比较大.现