您的位置:首页 > 其它

嵌入式jetty相关知识总结

2015-01-27 17:12 295 查看

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的Web2.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.1HTTPSERVER端实现

2.1.1.1HTTPSERVER端实现概述

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

l创建Server

l添加/配置Connectors

l添加/配置Handlers

l添加/配置Servlets/Webapps到Handlers

l启动服务器并join

2.1.1.2创建一个Server

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

Serverserver=newServer();

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

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

简单示例如下:
publicclassSimplestServer
{
publicstaticvoidmain(String[]args)throwsException
{
Serverserver=newServer(8080);
server.start();
server.join();
}
}

2.1.1.3配置Connectors

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

//实例化

SelectChannelConnectorconnector=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(newExecutorThreadPool(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的实现:

publicclassHelloHandlerextendsAbstractHandler

{

publicvoidhandle(Stringtarget,RequestbaseRequest,HttpServletRequestrequest,HttpServletResponseresponse)

throwsIOException,ServletException

{

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

response.setStatus(HttpServletResponse.SC_OK);

baseRequest.setHandled(true);

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

}

}


handle为AbstractHandler抽象类中定义的抽象方法,我们对请求的处理操作主要通过此方法来完成,handle中参数包括:

target:请求的目标,可以是一个URI或者一个命名dispatcher中的名字。

baseRequest:Jetty的可变请求对象,总是unwrapped。

Request:不变的请求对象,可能会被过滤器或servlet包装

Response:响应,可能会被过滤器或servlet包装

hadler设定响应的状态,内容类型,并在使用writer生成响应体之前标记请求为已处理。

下面是运行Jettyserver使用HelloWorldHandler的例子:

publicstaticvoidmain(String[]args)throwsException

{

Serverserver=newServer(8080);

server.setHandler(newHelloHandler());


server.start();

server.join();

}


2.1.1.5启动Server并join

server.start();

server.join();

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

2.1.1.6理解Handler的几个重要概念

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

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

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

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

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

2.1.1.7配置文件服务器

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

publicclassFileServer

{

publicstaticvoidmain(String[]args)throwsException

{

Serverserver=newServer();

SelectChannelConnectorconnector=newSelectChannelConnector();

connector.setPort(8080);

server.addConnector(connector);


ResourceHandlerresource_handler=newResourceHandler();

resource_handler.setDirectoriesListed(true);

resource_handler.setWelcomeFiles(newString[]{"index.html"});


resource_handler.setResourceBase(".");


HandlerListhandlers=newHandlerList();

handlers.setHandlers(newHandler[]{resource_handler,newDefaultHandler()});

server.setHandler(handlers);


server.start();

server.join();

}

}

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

2.1.1.8设置Contexts

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

下面是一个使用例子:

publicclassOneContext

{

publicstaticvoidmain(String[]args)throwsException

{

Serverserver=newServer(8080);


ContextHandlercontext=newContextHandler();

context.setContextPath("/hello");

context.setResourceBase(".");

context.setClassLoader(Thread.currentThread().getContextClassLoader());

server.setHandler(context);


context.setHandler(newHelloHandler());


server.start();

server.join();

}

}


2.1.1.9创建Servlets

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

publicclassHelloServletextendsHttpServlet

{

privateStringgreeting="HelloWorld";

publicHelloServlet(){}

publicHelloServlet(Stringgreeting)

{

this.greeting=greeting;

}

protectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,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注册的三个helloworldservlet的实例:

publicclassOneServletContext

{

publicstaticvoidmain(String[]args)throwsException

{

Serverserver=newServer(8080);


ServletContextHandlercontext=newServletContextHandler(ServletContextHandler.SESSIONS);

context.setContextPath("/");

server.setHandler(context);


context.addServlet(newServletHolder(newHelloServlet()),"/*");

context.addServlet(newServletHolder(newHelloServlet("BuongiornoMondo")),"/it/*");

context.addServlet(newServletHolder(newHelloServlet("BonjourleMonde")),"/fr/*");


server.start();

server.join();

}

}


2.1.1.11设置WebApplicationContext

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

示例如下:

publicclassOneWebApp

{

publicstaticvoidmain(String[]args)throwsException

{

Stringjetty_home=System.getProperty("jetty.home","..");


Serverserver=newServer(8080);


WebAppContextwebapp=newWebAppContext();

webapp.setContextPath("/");

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

server.setHandler(webapp);


server.start();

server.join();

}

}

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

publicclassOneWebAppUnassembled

{

publicstaticvoidmain(String[]args)throwsException

{

Serverserver=newServer(8080);


WebAppContextcontext=newWebAppContext();

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配置ContextHandlerCollection

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

示例如下:

publicclassManyContexts
{
publicstaticvoidmain(String[]args)throwsException
{
Serverserver=newServer(8080);

ServletContextHandlercontext0=newServletContextHandler(ServletContextHandler.SESSIONS);
context0.setContextPath("/ctx0");
context0.addServlet(newServletHolder(newHelloServlet()),"/*");
context0.addServlet(newServletHolder(newHelloServlet("BuongiornoMondo")),"/it/*");
context0.addServlet(newServletHolder(newHelloServlet("BonjourleMonde")),"/fr/*");

WebAppContextwebapp=newWebAppContext();
webapp.setContextPath("/ctx1");
webapp.setWar(jetty_home+"/webapps/test.war");

ContextHandlerCollectioncontexts=newContextHandlerCollection();
contexts.setHandlers(newHandler[]{context0,webapp});

server.setHandler(contexts);

server.start();
server.join();
}
}

2.1.2HTTPCLIENT端实现

2.1.2.1HttpClient设置

在和HTTP服务器通信前,需要先设置HttpClient,然后启动它。
HttpClient
client=newHttpClient();
client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
client.start();

还可以为HttpClient添加其他的设置:
HttpClientclient=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(HttpExchangeexchange)方法,客户端和服务器间的通信就被发起了。
exchange必须设置;两个重要的参数:要连接的地址和请求的URI。示例如下:
HttpExchangeexchange=newHttpExchange();

//OptionallysettheHTTPmethod
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(BufferhttpVersion,intstatusCode,BufferstatusMessage),当响应行被处理时调用;三个参数分别表示HTTP版本号(e.g."HTTP/1.1"),服务器响应状态码(e.g.200),服务器响应状态信息(e.g."OK")。
onResponseHeader(Buffername,Buffervalue),在每个响应头信息被处理时调用;两个参数分别表示每个息的名字(e.g."Content-Length")和值(e.g."16384")。
onResponseHeaderComplete(),当所有的响应头信息被处理完后调用。
onResponseContent(Buffercontent),收到每个响应内容块后都被调用。参数表示对应响应内容块的内容。
onResponseComplete(),在响应内容被完全收到后调用。

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

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

通常,我们通过重写onResponseComplete()函数来实现服务器响应信息的获取(比如响应头和响应体)和执行其他的操作:
ContentExchangeexchange=newContentExchange(true)
{
protectedvoidonResponseComplete()throwsIOException
{
intstatus=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;

publicclassTestClient{

publicstaticvoidmain(String[]args)throwsException{

HttpClientclient=newHttpClient();

client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);

client.setThreadPool(newQueuedThreadPool(50));

client.setMaxConnectionsPerAddress(10);

client.setTimeout(5000);

//启动之前先配置好

client.start();

HttpExchangeexchange=newHttpExchange(){

@Override

publicvoidonConnectionFailed(Throwablearg0){

//TODOAuto-generatedmethodstub

System.err.println("failedtoconnectwebserver");

}

@Override

publicvoidonException(Throwablearg0){

//TODOAuto-generatedmethodstub

System.err.println("unknownerror");

}

@Override

publicvoidonExpire(){

//TODOAuto-generatedmethodstub

System.err.println("timeout");

}

@Override

publicvoidonRequestCommitted()throwsIOException{

//TODOAuto-generatedmethodstub

System.out.println("requestcommited");

}

@Override

publicvoidonRequestComplete()throwsIOException{

//TODOAuto-generatedmethodstub

System.out.println("requestcomplete");

}

@Override

publicvoidonResponseComplete()throwsIOException{

//TODOAuto-generatedmethodstub

System.out.println("responsecomplete");

}

@Override

publicvoidonResponseContent(Bufferarg0)throwsIOException{

//TODOAuto-generatedmethodstub

System.out.println("responsecontent");

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

}

@Override

publicvoidonResponseHeader(Bufferarg0,Bufferarg1)throwsIOException{

//TODOAuto-generatedmethodstub

System.out.println("responseheader");

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

}

@Override

publicvoidonResponseStatus(Bufferarg0,intarg1,Bufferarg2)

throwsIOException{

//TODOAuto-generatedmethodstub

System.out.println("responsestatus");

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

}

@Override

publicvoidonRetry(){

//TODOAuto-generatedmethodstub

System.out.println("retryrequest");

}

@Override

publicvoidonResponseHeaderComplete()throwsIOException{

//TODOAuto-generatedmethodstub

System.out.println("responseheadercomplete");

}

};

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()方法来实现同步请求。

HttpClientclient=newHttpClient();
client.start();

ContentExchangeexchange=newContentExchange(true);
exchange.setURL("http://foobar.com/baz");

client.send(exchange);

//Waitsuntiltheexchangeisterminated
intexchangeState=exchange.waitForDone();

if(exchangeState==HttpExchange.STATUS_COMPLETED)
doSomething();
elseif(exchangeState==HttpExchange.STATUS_EXCEPTED)
handleError();
elseif(exchangeState==HttpExchange.STATUS_EXPIRED)
handleSlowServer();

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

2.2HTTP认证实现

2.2.1HTTP对内认证的实现

2.2.1.1对内认证需求

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

2.2.1.2对内认证实现方式

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

下面是一个实现的demo:

Server端:

packagetest;

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;

importorg.eclipse.jetty.server.handler.DefaultHandler;

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

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

publicclassSServer{

privateServerserver=null;

publicstaticfinalStringHOST="192.168.0.158";

publicstaticfinalintPOST=40404;

publicstaticvoidmain(String[]args)throwsFileNotFoundException{

newSServer().startHttpServer();

}

publicvoidstartHttpServer()throwsFileNotFoundException{

server=newServer();

SelectChannelConnectorconnector=newSelectChannelConnector();

connector.setHost(HOST);

connector.setPort(POST);

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

ContextHandlertest=newContextHandler();

test.setContextPath("/test");

Handlerhandler=newHHandler();

test.setHandler(handler);

ContextHandlerCollectioncontexts=newContextHandlerCollection();

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

HandlerCollectionhandlers=newHandlerCollection();

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

server.setHandler(handlers);

start();

}

privatevoidstart(){

try{

server.start();

server.join();

}catch(Exceptione){

e.printStackTrace();

}

}

}

Handle类:

packagetest;

importjava.io.IOException;

importjavax.servlet.ServletException;

importjavax.servlet.http.HttpServletRequest;

importjavax.servlet.http.HttpServletResponse;

importorg.eclipse.jetty.server.Request;

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

publicclassHHandlerextendsAbstractHandler{

publicHHandler(){

}

@Override

publicvoidhandle(finalStringtarget,finalRequestrequest,finalHttpServletRequestservlet_request,

finalHttpServletResponseresponse)throwsIOException,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端:

packagetest;

importorg.eclipse.jetty.client.HttpClient;

importorg.eclipse.jetty.client.HttpExchange;

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

publicclassCClient{

/**

*@paramargs

*/

publicstaticvoidmain(String[]args)throwsException{

HttpClientclient=newHttpClient();

client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);

client.setThreadPool(newQueuedThreadPool(50));

client.setMaxConnectionsPerAddress(10);

client.setTimeout(5000);

//启动之前先配置好

client.start();

HttpExchangeexchange=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端:

packagetest;

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;

importorg.eclipse.jetty.server.handler.DefaultHandler;

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

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

publicclassSServer{

privateServerserver=null;

publicstaticfinalStringHOST="192.168.0.158";

publicstaticfinalintPOST=40404;

publicstaticvoidmain(String[]args)throwsFileNotFoundException{

newSServer().startHttpServer();

}

publicvoidstartHttpServer()throwsFileNotFoundException{

server=newServer();

SelectChannelConnectorconnector=newSelectChannelConnector();

connector.setHost(HOST);

connector.setPort(POST);

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

ContextHandlertest=newContextHandler();

test.setContextPath("/test");

Handlerhandler=newHHandler();

test.setHandler(handler);

ContextHandlerCollectioncontexts=newContextHandlerCollection();

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

HandlerCollectionhandlers=newHandlerCollection();

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

server.setHandler(handlers);

start();

}

privatevoidstart(){

try{

server.start();

server.join();

}catch(Exceptione){

e.printStackTrace();

}

}

}

Handle类:

packagetest;

importjava.io.ByteArrayOutputStream;

importjava.io.IOException;

importjava.io.InputStream;

importjavax.servlet.ServletException;

importjavax.servlet.http.HttpServletRequest;

importjavax.servlet.http.HttpServletResponse;

importorg.eclipse.jetty.http.HttpStatus;

importorg.eclipse.jetty.server.Request;

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

publicclassHHandlerextendsAbstractHandler{

publicHHandler(){

}

@Override

publicvoidhandle(finalStringtarget,finalRequestrequest,finalHttpServletRequestservlet_request,

finalHttpServletResponseresponse)throwsIOException,ServletException{

request.setHandled(true);

InputStreaminputStream=request.getInputStream();

ByteArrayOutputStreamout=newByteArrayOutputStream();

byte[]b=newbyte[1024];

inti=0;

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

out.write(b,0,i);

}

//处理获取到的数据

try{

Stringstr=out.toString();

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

System.out.println(str);

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

response.setStatus(HttpStatus.OK_200);

}

}catch(Exceptione){//NotExistsException

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

response.setStatus(HttpStatus.FAILED_DEPENDENCY_424);

}finally{

out.close();

inputStream.close();

b=null;

}

response.getWriter().flush();

response.getWriter().close();

}

}

Client端:

packagetest;

importorg.eclipse.jetty.client.HttpClient;

importorg.eclipse.jetty.client.HttpExchange;

importorg.eclipse.jetty.io.ByteArrayBuffer;

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

publicclassCClient{

/**

*@paramargs

*/

publicstaticvoidmain(String[]args)throwsException{

testClient("www","123456");

}

publicstaticvoidtestClient(Stringname,Stringpwd)throwsException{

HttpClientclient=newHttpClient();

client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);

client.setThreadPool(newQueuedThreadPool(50));

client.setMaxConnectionsPerAddress(10);

client.setTimeout(5000);

//启动之前先配置好

client.start();

HttpExchangeexchange=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.3HTTPKEEPALIVE实现

在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=newHttpClient();

client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);

client.setThreadPool(newQueuedThreadPool(50));

client.setMaxConnectionsPerAddress(10);

client.setTimeout(5000);

//启动之前先配置好

client.start();

HttpExchangeexchange=newHttpExchange();

exchange.setMethod("POST");

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

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

client.send(exchange);

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

2.4HTTPS的实现

2.4.1生成keystore

切换到JAVA_HOME/bin目录下

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

./keytool-keystorefstoreKS-aliasfstore-genkey-keyalgRSA

Enterkeystorepassword:

Re-enternewpassword:

Whatisyourfirstandlastname?

[Unknown]:fstore

Whatisthenameofyourorganizationalunit?

[Unknown]:fstore

Whatisthenameofyourorganization?

[Unknown]:fstore

WhatisthenameofyourCityorLocality?

[Unknown]:bj

WhatisthenameofyourStateorProvince?

[Unknown]:bj

Whatisthetwo-lettercountrycodeforthisunit?

[Unknown]:cn

IsCN=fstore,OU=fstore,O=fstore,L=bj,ST=bj,C=cncorrect?

[no]:yes

Enterkeypasswordfor<fstore>

(RETURNifsameaskeystorepassword):

Re-enternewpassword:

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

2.4.2服务端设置ssl连接

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

Serverserver=newServer();

SslSelectChannelConnectorssl_connector=newSslSelectChannelConnector();

ssl_connector.setHost("192.168.0.158");

ssl_connector.setPort(40404);

ContextHandlertest=newContextHandler();

test.setContextPath("/test");

Handlerhandler=newHHandler();

test.setHandler(handler);

ContextHandlerCollectioncontexts=newContextHandlerCollection();

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

HandlerCollectionhandlers=newHandlerCollection();

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

server.setHandler(handlers);

SslContextFactorycf=ssl_connector.getSslContextFactory();

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

cf.setKeyStorePassword("123456");

cf.setKeyManagerPassword("123456");

server.addConnector(ssl_connector);

try{

server.start();

server.join();

}catch(Exceptione){

//TODOAuto-generatedcatchblock

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-keystoremycacerts-storepass123456-aliasfstore-filefstore_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;

publicclassCClient{

/**

*@paramargs

*/

publicstaticvoidmain(String[]args)throwsException{

testClient("www","123456");

}

publicstaticvoidtestClient(Stringname,Stringpwd)throwsException{

SslContextFactorysslCF=newSslContextFactory();

sslCF.setTrustAll(false);

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

HttpClientclient=newHttpClient(sslCF);

client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);

client.setThreadPool(newQueuedThreadPool(50));

client.setMaxConnectionsPerAddress(10);

client.setTimeout(5000);

//启动之前先配置好

client.start();

HttpExchangeexchange=newHttpExchange();

exchange.setMethod("POST");

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

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

client.send(exchange);

}

}

2.5HTTPcurl上传以及浏览器上传实现

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;

importmy.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;

importmy.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;

publicclassSServer{

privateServerserver=null;

publicstaticfinalStringHOST="192.168.0.158";

publicstaticfinalintPOST=40404;

publicstaticvoidmain(String[]args)throwsFileNotFoundException{

newSServer().startHttpServer();

}

publicvoidstartHttpServer()throwsFileNotFoundException{

server=newServer();

SelectChannelConnectorconnector=newSelectChannelConnector();

connector.setHost(HOST);

connector.setPort(POST);

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

ServletContextHandlertest=newServletContextHandler(ServletContextHandler.SESSIONS);

test.setContextPath("/test");

ServletHoldermainHolder=newServletHolder(newHHandler());

mainHolder.getRegistration().setMultipartConfig(

newMultipartConfigElement("data/tmp",250*1024*1024,250*1024*1024,250*1024*1024));

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

ContextHandlerCollectioncontexts=newContextHandlerCollection();

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

HandlerCollectionhandlers=newHandlerCollection();

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

server.setHandler(handlers);

start();

}

privatevoidstart(){

try{

server.start();

server.join();

}catch(Exceptione){

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;

publicclassHHandlerextendsHttpServlet{

privatefinalintfileNameLength=250;

@Override

protectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{

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

Partpart=request.getParts().iterator().next();

//Partpart=request.getPart(HttpServerContantVal._FILENAME);

finalInputStreaminputStream=part.getInputStream();

finalByteArrayOutputStreamoutputStream=newByteArrayOutputStream();

//获得上传文件的长度

//finalintdataLength=(int)part.getSize();

Stringfilename=request.getPathInfo().substring(1);

Stringnamespace=request.getParameter(HttpServerContantVal._NAMESPACE);

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

StringpartName=part.getName();

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

filename=partName;

}else{

response.setStatus(HttpStatus.FORBIDDEN_403);

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

response.getWriter().flush();

response.getWriter().close();

return;

}

}

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

response.setStatus(HttpStatus.FORBIDDEN_403);

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

response.getWriter().flush();

response.getWriter().close();

return;

}

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

namespace=ConstantVal.DEFAULTPATH;

}

//读取文件内容到缓冲

byte[]b=newbyte[1024];

inti=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(Exceptione){

response.setStatus(HttpStatus.FORBIDDEN_403);

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

response.getWriter().flush();

response.getWriter().close();

}

}

}

Html脚本:

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

<head>

<title>downloadfilebypost</title>

</head>

<body>

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

<inputid="filename"type="file"name="filename"/>

<inputtype="submit"value="submit1"/>

</form>

</body>

</html>

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

mainHolder.getRegistration().setMultipartConfig(newMultipartConfigElement("data/tmp",250*1024*1024,250*1024*1024,250*1024*1024));方法是从jettyv8.x以后才支持的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: