二十四、处理器(Handler )

24.1 编写一个常用的Handler

Jetty的Handler组件用来处理接收到的请求。

很多使用者不需要编写Jetty的Handler ,而是通过使用Servlet处理请求。你可以调用Jetty内置的Handler 来处理context、security、session、和servlet,且并不需要扩展它们的功能。然而,有些用户或许有一些特殊的需求,或者因为某些原因想禁用servlet API。所以为了通过最少的代码为他们提供提供解决方法,Jetty允许实现Jetty的Handler 。

可以在Jetty架构章节(未翻译,在第五部分,详见目录),来了解Handler 和Servlets的异同。

24.1.1 处理器的API

Handler接口提供Jetty核心组件的方法。实现这个接口的类用来处理请求、过滤请求和生成响应内容。

Handler 接口的AP的核心方法I是:

public voidhandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)throws IOException, ServletException

实现了这个方法可以处理一个请求,然后将请求传入另一个handler (或者servlet),或者它可以修改或者包装request然后将请求传递。这里有三种类型的handler:

协调其他Handlers - 用来将请求传递给其他的Handlers(HandlerCollection,ContextHandlerCollection)

过滤Handlers  - 增加一个请求处理程序,并将其传递给其他Handlers (HandlerWrapper,ContextHandler,SessionHandler)

生成Handlers  - 用来生成context(ResourceHandler,ServletHandler)

24.1.1.1 Handler的作用

Handler作用是对资源做一个标记这样可以对传入的请求作出处理。通常会在HTTP请求中解析URI。然而,在两种关键情况下目标将与传入的request不一致:

如果请求被转发到一个指定的资源,例如一个定义过的servlet,那么做为一个指定的servlet,目标将是资源的名称。

如果请求是由 Request Dispatcher产生的,那么目标将是包含资源和不同URI请求的URI。

24.1.1.2 请求和响应

handle方法的request和response对象实际上是指 Servlet Request和 Servlet Response。它们是标准的APIs并且对请求和响应做了适当的限制。通常情况下访问Jetty的这些实现类是很重要的:Request和Response。然而Request和Response对象可能被handlers、filters和servlets包装过,它们和直接传入的不一样。下面的代码是在任何包装过的对象中获得Request和Response的核心代码。

Request base_request = request instanceof Request ?(Request)request : HttpConnection.getCurrentConnection().getHttpChannel().getRequest();

Response base_response= response instanceof Response ? (Response)response : HttpConnection.getCurrentConnection().getHttpChannel().getResponse();

注意,如果一个handler将请求传递给另一个handler,它应该将request/response对象传入,而不是原始对象。这样可以保证request/response不被任何流处理器处理。

24.1.1.3 转发

转发的参数可以表明被调用时的状态,如下:

REQUEST == 1 - 从连接中获得的一个原始请求

FORWARD == 2 - 一个从RequestDispatcher中转发得来的请求

INCLUDE == 4 - 一个包含在RequestDispatcher中的请求

ERROR == 8 - 一个被容器转发到错误处理器的请求

这些对servlet和相关处理器都具有重要的意义。例如,安全处理器仅会应用身份验证和通过身份对请求进行转发。

24.1.2 处理器处理请求

一个Handler 可以对一个请求做如下处理:

生成一个Response

对一个Request 和/或 一个 Response进行过滤

将Request 和/或 Response 传递给另一个处理器

24.1.2.1 生成一个Response

之前OneHandler(http://www.cnblogs.com/yiwangzhibujian/p/5845623.html) 的嵌入式例子展示了一个简单的handler可以生成一个响应。

你可以使用普通的servlet response API,通常需要设置状态,头信息,然后写入内容。

response.setContentType("text/html");

response.setStatus(HttpServletResponse.SC_OK);

response.getWriter().println("

Hello OneHandler

");

handler应表明它已经处理完成这个请求,并且这个请求不应该传递给其它handler,这两件事是非常重要的:

Request base_request = (request instanceof Request) ?(Request)request:HttpConnection.getCurrentConnection().getHttpChannel().getRequest();

base_request.setHandled(true);

24.1.2.2 过滤请求 和/或 响应

一旦获得基础的请求和响应对象,你就可以修改它们。通常你修改它们是为了实现:

分解URI使之进入指定的contextPath、servletPath和匹配路径的组件

将请求与静态资源进行结合

将一个请求与session进行结合

将一个请求与主要的安全管理器进行结合

在请求转发到另一个资源的时候对URI和路径进行修改

你也可以对request的内容进行如下修改:

设置当前线程context的类加载器

设置线程本地类来标记当前的ServletContext

通常,Jetty会将一个修改过的request传入另一个handler,并将在finally语句块中取消修改:

try{

base_request.setSession(a_session);

next_handler.handle(target,request,response,dispatch);

}finally{

base_request.setSession(old_session);

}

实现HandlerWrapper的类通常可以具有过滤器的行为。

24.1.2.3 转发请求和响应到另一个Handler

一个handler可能只是简单的检查请求并且通过目标、请求的URI或者其它信息来选择下一个要传递的处理器。这些处理器通常实现HandlerContainer接口。

例子包括如下:

Class Handler Collection - 一个handler的集合,每一个handler将会被调用。这通常用来将一个请求传入ContextHandlerCollection并掉调用RequestLogHandler。

HandlerList - 一个handlers 的List集合,会按顺序调用,直到请求状态被设置为已处理。

ContextHandlerCollection - 一个Handlers的集合,通常根据路径来匹配处理器来处理请求。

24.1.3 处理器其他信息

可以通过阅读Jetty的JavaDco和Jetty的源码来获得其他handler的细节信息。

二十五、调试

25.1 调试选项

将向你展示Jetty如何简单的配置并部署应用到开发和生产环境上,这和在你环境上调试你的应用有很大的不同。在这一章节,我们将集中介绍主要的不同点,并向你展示如何使用它们。

25.2 开启远程调试

如果你把一个应用部署到Jetty那么你可以很简单的通过远程以调试的模式与应用进行交互。基本要求时,你需要以额外的参数启动远程JVM,然后在Eclipse中启动远程调试。这是很容易完成的。

+提示

这个例子默认你将web应用部署到Jetty标准安装包中。

25.2.1 启动Jetty

假设你已经将应用部署到Jetty中,下面有两种不同的方法来启动Jetty:

通过命令行

在命令行中增加必要的启动参数,如下:

$ java -Xdebug -agentlib:jdwp=transport=dt_socket,address=9999,server=y,suspend=n -jar start.jar

通过start.ini

如果你想对应用进行调试且不想记住复杂的参数那么这种方法将是最好的方法。

1、编辑start.ini文件,将--exec行的注释取消掉,这是非常必要的。

2、将上面命令行中的参数增加到文件中,如下所示:

#===========================================================# Configure JVM arguments.

# If JVM args are include in an ini file then--exec is needed

# to start anewJVM from start.jar with the extra args.

# If you wish to avoid an extra JVM running, place JVM args

# on the normal command line anddo not use --exec

#-----------------------------------------------------------

--exec-Xdebug-agentlib:jdwp=transport=dt_socket,address=9999,server=y,suspend=n

#-Xmx2000m

#-Xmn512m

#-XX:+UseConcMarkSweepGC

#-XX:ParallelCMSThreads=2#-XX:+CMSClassUnloadingEnabled

#-XX:+UseCMSCompactAtFullCollection

#-XX:CMSInitiatingOccupancyFraction=80#-verbose:gc

#-XX:+PrintGCDateStamps

#-XX:+PrintGCTimeStamps

#-XX:+PrintGCDetails

#-XX:+PrintTenuringDistribution

#-XX:+PrintCommandLineFlags

#-XX:+DisableExplicitGC

对任何你感兴趣的启动参数,你都可以取消注释然后进行设置。

3、不管你对属性如何设置,在Jetty启动时你将会看到如下头部信息:

Listening for transport dt_socket at address: 9999

25.2.2 使用你的IDE进行连接

根据你是用的IDE选择下面的文档进行阅读:

25.3 通过IntelliJ调试

这里有很多可用的选项,可以在IntelliJ中调试你的应用。

25.3.1 使用IntelliJ连接项目

接下来我们要使用IntelliJ 连接已部署的项目(截图源于官方文档)

1、在IntelliJ 中打开你想进行调试且部署到Jetty中的项目。选择 Run → Edit Configurations。通过“+”新增一个配置。选择 Remote。确保端口为你设置的端口。

2、接下来你可以在你想要的位置设置断点,当远程JVM线程运行到这个位置时会被触发。设置断点的方法很简单,选择class的源码,并且在行的左边单击(如下图红色的点),红点和红色背景的行为断点处。

3、通过浏览器访问你的servlet,当线程走到断点处后会被触发,并打开调试视图。

25.3.2 使用IntelliJ调试项目

自从Jetty的嵌入式代码越来越简单后,很多人通常会使用一个很小的main方法来来对web项目进行一个简单的测试。最后应该回顾下之前的两个章节嵌入式Jetty和嵌入式例子。

一旦你为你的应用程序定义了一个main方法,打开源码,在main方法上右键点击,选择 Debug或者使用快捷键CTRL+SHIFT+D,在你的控制台上你将会看到你的应用程序已启动,启动完成后你可以设置断点,然后通过浏览器访问来触发中断。同样的方法可以用于单元测试,用来替代使用main方法对你的方法进行测试。

IntelliJ 的调试功能是异常强大的。例如可以设置条件断点,即当条件满足时才会触发中断。

+技巧

你可以通过jetty-logging.properties文件来对Jetty的日志进行配置。如果这个文件在classpath中,那么Jetty启动时将会应用这个文件来对日志进行配置,我们使用这种扩展方法来使Jetty的开发变得异常简单。

25.4 通过Eclipse调试

这里有很多可用的选项,可以在Eclipse中调试你的应用。

25.4.1 使用Eclipse连接项目

接下来我们将使用Eclipse来连接部署的项目。

1、在Eclipse中,右键部署在Jetty的项目,选择Debug → Debug Configurations用来创建一个Remote Java Application配置。确保端口为配置的端口。

2、接下来你可以在你的应用中设置断点。

3、通过浏览器来访问你的servlet,当线程走到断点处时会被触发并进入Debug视图。

25.4.2 使用Eclipse调试项目

自从Jetty的嵌入式代码越来越简单后,很多人通常会使用一个很小的main方法来来对web项目进行一个简单的测试。最后应该回顾下之前的两个章节嵌入式Jetty和嵌入式例子。

一旦你为你的应用程序定义了一个main方法,打开源码,在main方法上右键点击,选择Debug As → Java Application,在你的控制台上你将会看到你的应用程序已启动,启动完成后你可以设置断点,然后通过浏览器访问来触发中断。同样的方法可以用于单元测试,用来替代使用main方法对你的方法进行测试。

+技巧

你可以通过jetty-logging.properties文件来对Jetty的日志进行配置。如果这个文件在classpath中,那么Jetty启动时将会应用这个文件来对日志进行配置,我们使用这种扩展方法来使Jetty的开发变得异常简单。

二十六、WebSocket 入门

WebSocket 基本特性:

WebSocket 是通过HTTP来实现的一个新的交互式协议

它是基于底层框架技术,提供UTF-8文本或二进制格式消息传递

单个的消息可以是任意大小(单个消息在底层最少为63bits)

发送消息的数量不被限制

消息是顺序发送的,协议不支持交互式读取

当WebSocket 关闭时,一个状态码和原因会被提供

WebSocket 会有以下状态的改变:

状态描述

CONNECTING

一个HTTP正在连接,升级为WebSocket

OPEN

HTTP升级成功,socket被打开,等待读和写

CLOSING

WebSocket 正在关闭

CLOSED

WebSocket已关闭,不能进行读和写

26.1 Jetty提供了什么

Jetty提供了以下标准和规范的一个实现。

RFC-6455

WebSocket 的一个协议

支持的13版本和最终发布的规范。

Jetty通过autobahn来测试WebSocket 协议的实现。

+重要

早期WebSocket 仅被Jetty7和Jetty8支持,但是Jetty9已经不再支持。这意味着Jetty9将不再支持实现旧版本WebSocket 的一些老的浏览器

+技巧

如果你想知道你选择的浏览器是否支持WebSocket,可以访问 caniuse.com/websockets

JSR-356

Java WebSocket API(javax.websocket)

这是Java官方APIs支持的WebSockets

不稳定的标准和规范:

perframe-compression

permessage-compression

26.2 WebSocket APIs

使用Jetty来实现WebSockets 的APIs和库。

Jetty WebSocket API

使用Jetty来创建和与WebSockets 工作的最基本APIs

Jetty WebSocket Server API

编写Jetty服务器端WebSocket

Jetty WebSocket Client API

使用Jetty来连接到WebSocket

Java WebSocket Client API

标准的JavaWebSocket 客户端API (javax.websocket) [JSR-356]

Java WebSocket Server API

标准的JavaWebSocket 服务端API (javax.websocket.server) [JSR-356]

26.3 启用WebSocket

为了启用websocket ,你必须启用websocket 模块。

一旦这个模块被你的Jetty基实例启用,它将会应用到所有部署到这的web项目。如果你想更有选择性的指定哪些web应用使用websocket,你可以这么做:

对单个的项目禁用jsr-356

你可以对单个的项目禁用jsr-356,通过设置context 的org.eclipse.jetty.websocket.jsr356属性为false来实现。这意味着websockets 将对你的项目不可用,但是部署时扫描websocket-related类,如终端,仍然会进行。这会在如果你的项目包含过的类和jar包时是个较大的开销。为了完全禁用websockets 并避免额外的开销,使用context属性org.eclipse.jetty.containerInitializerExclusionPattern,接下来描述你要排除的web应用。

对一个项目完全禁用jsr-356

设置context的org.eclipse.jetty.containerInitializerExclusionPattern属性,用来包含住org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer。下面有一个例子,通过代码禁用jsr-356,当然你也可以在配置文件中设置:

WebAppContext context = newWebAppContext();

context.setAttribute("org.eclipse.jetty.containerInitializerExclusionPattern","org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer|com.acme.*");

二十七、Jetty Websocket APIs

这些页面还在改进,还没有移动到它们各自的章节。

27.1 Jetty WebSocket API使用

Jetty提供它自己的更强力的API,有着一个为服务器和客户端WebSockets使用的基本核心API。

这是一个基于WebSocket 消息的事件驱动API。

27.2 WebSocket事件

每一个WebSocket 可以接收各种事件:

连接时事件

当WebSocket 成功打开的一个指示,

你将接收到一个org.eclipse.jetty.websocket.api.Session对象,指向打开事件的特定会话。

对于通常的WebSocket ,持有住这个会话并与远程端通信是很重要的

对于无状态的WebSockets,会话将被传递到每个发生的会话上,让你只有在众多远程终端中只有1个WebSocket

关闭时事件

当WebSocket 关闭的一个指示

每一个关闭的事件都有一个状态码(和一个可选的关闭原因的消息)

通常的WebSocket关闭会通过通过本地终端和远程终端发送消息来实现连接关闭。

本地WebSocket可以发送结束帧到远程终端,但是远程终端可以持续发送消息,知道发送一个结束帧。这成为半开连接,但是当本地WebSocket发送结束帧以后,将不会写入任何数据。

当异常关闭时,例如连接终端或者连接超时,底层连接将在没有握手的情况下终止连接,这仍然会触发一个关闭事件(很有可能会触发一个错误事件)

错误时事件

当一个错误发生时,并且在实现内,WebSocket 会被事件处理器通知到。

消息时事件

表明一个完整的消息被接收到,并准备进行处理。

可以是(使用UTF-8)文本或者原始的二进制消息

27.3 WebSocket会话

Session对象可以被用来:

判断连接状态(是打开还是关闭):

if(session.isOpen()) {//发送消息

}

判断连接是否安全

if(session.isSecure()) {//使用'wss://'进行连接

}

获得升级后的Request和Response

UpgradeRequest req =session.getUpgradeRequest();

String channelName= req.getParameterMap().get("channelName");

UpgradeRespons resp=session.getUpgradeResponse();

String subprotocol= resp.getAcceptedSubProtocol();

获得远程连接地址

InetSocketAddress remoteAddr = session.getRemoteAddress();

获得和设置超时时间

session.setIdleTimeout(2000); //2 秒超时时间

27.4 将消息发送到远程终端

会话最重要的特性是,通过 org.eclipse.jetty.websocket.api.RemoteEndpoint来发送消息。

通过远程终端,你可以选择发送文本或二进制WebSocket 消息,或者WebSocket 的PING 和PONG终止帧。

27.4.1 同步消息发送

大多数调用本质上都是同步的(堵塞式),在消息发送完毕前不会返回任何内容(或者抛出一个异常)。

RemoteEndpoint remote =session.getRemote();//堵塞式发送二进制消息到远程终端

ByteBuffer buf = ByteBuffer.wrap(new byte[] { 0x11, 0x22, 0x33, 0x44});try{

remote.sendBytes(buf);

}catch(IOException e)

{

e.printStackTrace(System.err);

}

上面例子说明如何发送一个简单的二进制消息到远程终端,这将阻塞住直到消息发送完毕,或者抛出一个IOException异常当无法发送这个消息时。

RemoteEndpoint remote =session.getRemote();//阻塞式发送文件到远程终端

try{

remote.sendString("Hello World");

}catch(IOException e)

{

e.printStackTrace(System.err);

}

上面例子说明如何发送一个文本消息到远程终端,这将阻塞住直到消息发送完毕,或者抛出一个IOException异常当无法发送这个消息时。

27.4.2 发送部分消息

如果你有一个大消息要发送,并且想分部分发送,你可以使用部分消息发送方法到远程终端。只要确保你要完全发送完消息(isLast == true)。

RemoteEndpoint remote =session.getRemote();//阻塞发送二进制到远程终端//部分一

ByteBuffer buf1 = ByteBuffer.wrap(new byte[] { 0x11, 0x22});//部分二 (最后一个部分)

ByteBuffer buf2 = ByteBuffer.wrap(new byte[] { 0x33, 0x44});try{

remote.sendPartialBytes(buf1,false);

remote.sendPartialBytes(buf2,true); //isLast is true

}catch(IOException e)

{

e.printStackTrace(System.err);

}

上面例子说明如何通过两个部分发送二进制消息,使用部分消息发送支持。这将阻塞直到每一部分发送完,如果发送不到会抛出一个IOException 异常。

RemoteEndpoint remote =session.getRemote();//堵塞式发送文本到远程终端

String part1 = "Hello";

String part2= " World";try{

remote.sendPartialString(part1,false);

remote.sendPartialString(part2,true); //最后一个部分

}catch(IOException e)

{

e.printStackTrace(System.err);

}

上面例子说明如何通过两个部分发送文本消息,使用部分消息发送支持。这将阻塞直到每一部分发送完,如果发送不到会抛出一个IOException 异常。

27.4.3 发送PING/PONG控制帧

你也可以使用RemoteEndpoint发送PING和PONG控制帧。

RemoteEndpoint remote =session.getRemote();//阻塞发送PING控制帧

String data = "You There?";

ByteBuffer payload=ByteBuffer.wrap(data.getBytes());try{

remote.sendPing(payload);

}catch(IOException e)

{

e.printStackTrace(System.err);

}

上面例子说明通过"You There?"(到达远程端点作为一个字节数组的负载)文本附带PING控制帧,这将阻塞直到消息发送完成,或许会抛出一个IOException如果ping帧发送不出去的话。

RemoteEndpoint remote =session.getRemote();//阻塞式发送PONG到终端

String data = "Yup, I'm here";

ByteBuffer payload=ByteBuffer.wrap(data.getBytes());try{

remote.sendPong(payload);

}catch(IOException e)

{

e.printStackTrace(System.err);

}

上面例子说明通过"Yup, I'm here?"(到达远程端点作为一个字节数组的负载)文本附带PONG控制帧,这将阻塞直到消息发送完成,或许会抛出一个IOException如果pong帧发送不出去的话。

为了正确使用Pong帧,你应该在接收到PONG帧的时候返回相同的字节数组。

27.4.4 异步消息发送

有两种可用的异步发送消息方法:

RemoteEndpoint.sendBytesByFuture(ByteBuffer message)

RemoteEndpoint.sendStringByFuture(String message)

两种方法都会返回Future,可以使用标准的java.util.concurrent.Future来判断调用成功或失败。

RemoteEndpoint remote =session.getRemote();//异步发送二进制到远程终端

ByteBuffer buf = ByteBuffer.wrap(new byte[] { 0x11, 0x22, 0x33, 0x44});

remote.sendBytesByFuture(buf);

上面例子说明使用RemoteEndpoint发送一个简单的二进制。消息将被放到发送列表中,但是你不会知道发送是否成功。

RemoteEndpoint remote =session.getRemote();//异步发送二进制到远程终端

ByteBuffer buf = ByteBuffer.wrap(new byte[] { 0x11, 0x22, 0x33, 0x44});try{

Future fut =remote.sendBytesByFuture(buf);//等待完成(永久)

fut.get();

}catch (ExecutionException |InterruptedException e)

{//发送失败

e.printStackTrace();

}

上面例子说明使用RemoteEndpoint发送一个简单的二进制,通过追踪Future来判断是否发送成功。

RemoteEndpoint remote =session.getRemote();//异步发送二进制到远程终端

ByteBuffer buf = ByteBuffer.wrap(new byte[] { 0x11, 0x22, 0x33, 0x44});

Future fut = null;try{

fut=remote.sendBytesByFuture(buf);//等待完成(有超时时间)

fut.get(2,TimeUnit.SECONDS);

}catch (ExecutionException |InterruptedException e)

{//发送失败

e.printStackTrace();

}catch(TimeoutException e)

{//发送超时

e.printStackTrace();if (fut != null)

{//取消消息发送

fut.cancel(true);

}

}

上面例子说明使用RemoteEndpoint发送一个简单的二进制,通过追踪Future并等待指定的超时时间来发送消息,到发送超时时,取消消息发送。

RemoteEndpoint remote =session.getRemote();//异步发送二进制到远程终端

remote.sendStringByFuture("Hello World");

上面例子说明使用RemoteEndpoint发送一个文本消息。消息将被放到发送列表中,但是你不会知道发送是否成功。

RemoteEndpoint remote =session.getRemote();//异步发送二进制到远程终端

try{

Future fut = remote.sendStringByFuture("Hello World");//等待完成(永久)

fut.get();

}catch (ExecutionException |InterruptedException e)

{//发送失败

e.printStackTrace();

}

上面例子说明使用RemoteEndpoint发送一个文本,通过追踪Future来判断是否发送成功。

RemoteEndpoint remote =session.getRemote();//异步发送二进制到远程终端

Future fut = null;try{

fut= remote.sendStringByFuture("Hello World");//等待完成(有超时时间)

fut.get(2,TimeUnit.SECONDS);

}catch (ExecutionException |InterruptedException e)

{//发送失败

e.printStackTrace();

}catch(TimeoutException e)

{//发送超时

e.printStackTrace();if (fut != null)

{//取消消息发送

fut.cancel(true);

}

}

上面例子说明使用RemoteEndpoint发送一个文本,通过追踪Future并等待指定的超时时间来发送消息,到发送超时时,取消消息发送。

27.5 使用Websocket 注解

使用WebSocket 最基本的形式是在POJO上使用 Jetty WebSocket API提供的注解。

packageexamples.echo;importorg.eclipse.jetty.websocket.api.Session;importorg.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;importorg.eclipse.jetty.websocket.api.annotations.WebSocket;/*** 使用注解示例*/@WebSocket(maxTextMessageSize= 64 * 1024)public classAnnotatedEchoSocket

{

@OnWebSocketMessagepublic voidonText(Session session, String message)

{if(session.isOpen())

{

System.out.printf("Echoing back message [%s]%n",message);//echo the message back

session.getRemote().sendString(message,null);

}

}

}

上面的例子是一个简单的WebSocket回声终端例子,将接收到的消息发送出去。

这是使用无状态方法来实现的,事先发生时,会话被传递到消息事件中。这将允许你使用单一的实例来处理多个终端。

可用的注解如下:

@WebSocket

一个必需的类级别注解

标记当前类为WebSocket类

这个类必需是非抽象的公共类

@OnWebSocketConnect

一个可选的方法级别的注解

标记类的一个方法用来接收连接事件

方法必需是公共的、非抽象的、void返回值并且只有一个Session参数

@OnWebSocketClose

一个可选的方法级别的注解

标记类的一个方法用来接收关闭事件

方法的参数为:

Session (可选的)

int closeCode (必需的)

String closeReason (必需的)

@OnWebSocketMessage

一个可选的方法级别的注解

标记类的两个方法响应接收到消息事件

可以标记一个文件和一个二进制方法

方法必需是公共的、非抽象的、void返回值

接收文本消息的方法参数为:

Session (可选的)

String text (必需的)

接收二进制消息的方法参数为:

Session (可选的)

byte buf[] (必需的)

int offset (必需的)

int length (必需的)

@OnWebSocketError

一个可选的方法级别的注解

标记类的一个方法用来响应WebSocket 实现类的错误事件

方法必需是公共的、非抽象的、void返回值

方法参数为:

Session (可选的)

Throwable cause (必需的)

@OnWebSocketFrame

一个可选的方法级别的注解

标记类中的一个方法从WebSocket实现类中接收帧事件后表明升级握手过程中处理的任何扩展。

方法必需是公共的、非抽象的、void返回值

方法参数为:

Session (可选的)

Frame (必需的)

接收到的帧将被通知到这个方法上,然后交给Jetty处理,可能导致另一个事件,例如关闭或者接收到消息事件,帧的变化将不会被Jetty获知。

27.6 使用Websocket 监听器

Websocket 实现监听基本形式是通过使用 org.eclipse.jetty.websocket.api.WebSocketListener。

packageexamples.echo;importorg.eclipse.jetty.websocket.api.Session;importorg.eclipse.jetty.websocket.api.WebSocketListener;/*** 使用监听器的示例*/

public class ListenerEchoSocket implementsWebSocketListener

{privateSession outbound;

@Overridepublic void onWebSocketBinary(byte[] payload, int offset, intlen)

{/*只对文本消息感兴趣*/}

@Overridepublic void onWebSocketClose(intstatusCode, String reason)

{this.outbound = null;

}

@Overridepublic voidonWebSocketConnect(Session session)

{this.outbound =session;

}

@Overridepublic voidonWebSocketError(Throwable cause)

{

cause.printStackTrace(System.err);

}

@Overridepublic voidonWebSocketText(String message)

{if ((outbound != null) &&(outbound.isOpen()))

{

System.out.printf("Echoing back message [%s]%n",message);//监听到消息返回

outbound.getRemote().sendString(message,null);

}

}

}

这是到目前为止你能写出来的最基本和表现最好的(速度和存储)WebSocket实现类。如果你觉得这个监听器你有太多的方法要实现,那么你可以使用WebSocketAdapter来替代。

27.7 使用Websocket 适配器

一个在WebSocketListener上管理会话的基本适配器。

packageexamples.echo;importjava.io.IOException;importorg.eclipse.jetty.websocket.api.WebSocketAdapter;/*** 使用适配器的例子*/

public class AdapterEchoSocket extendsWebSocketAdapter

{

@Overridepublic voidonWebSocketText(String message)

{if(isConnected())

{try{

System.out.printf("Echoing back message [%s]%n",message);//将接收到的消息返回

getRemote().sendString(message);

}catch(IOException e)

{

e.printStackTrace(System.err);

}

}

}

}

这是一个方便的类,让你使用WebSocketListener更简单,并提供了一些有用的方法来检查会话的状态。

27.8 Jetty Websocket 服务端API

Jetty提供了WebSocket 终端和Servlet 路径连接通过使用WebSocketServlet 桥接servlet的能力。

在内部,Jetty管理着HTTP升级到WebSocket 和将一个HTTP连接转换为WebSocket 连接。

这个只有在Jetty容器中运行才会起作用(不像过去的Jetty技术,现在你不能获得到在其他容器中运行着的Jetty WebSocket ,例如在JBoss、Tomcat或WebLogic中)。

27.8.1 Jetty的WebSocketServlet

为了通过WebSocketServlet将你的WebSocket 与特殊的路径连接起来,你需要扩展org.eclipse.jetty.websocket.servlet.WebSocketServlet,并且指定哪个WebSocket 对象应该被创建与即将升级后的request。

packageexamples;importjavax.servlet.annotation.WebServlet;importorg.eclipse.jetty.websocket.servlet.WebSocketServlet;importorg.eclipse.jetty.websocket.servlet.WebSocketServletFactory;

@SuppressWarnings("serial")

@WebServlet(name= "MyEcho WebSocket Servlet", urlPatterns = { "/echo"})public class MyEchoServlet extendsWebSocketServlet

{

@Overridepublic voidconfigure(WebSocketServletFactory factory)

{//设置10秒超时时间

factory.getPolicy().setIdleTimeout(10000);//将MyEchoSocket 注册成一个要升级的WebSocket

factory.register(MyEchoSocket.class);

}

}

这个例子通过 @WebServlet创建了一个servlet映射,path为“/echo”(当然你也可以手动在WEB-INF/web.xml中添加),这样当遇到一个请求升级后将创建一个MyEchoSocket 实例。

方法 WebSocketServlet.configure(WebSocketServletFactory factory)是你可以把对WebSocket特殊配置放进去的地方。在这个例子中,我们定义了10秒超时时间,并将MyEchoSocket 注册为默认的WebSocketCreator 用来当请求升级后进行创建。

+提示

当配置websockets时,考虑防火墙和路由器的超时时间是很重要的。确保websocket 的超时时间低于你的防火墙或者路由器。

27.8.2 使用WebSocketCreator

所有WebSocket的创建都是通过你在WebSocketServletFactory中注册的WebSocketCreator。

默认情况下,WebSocketServletFactory 具有创建一个简单WebSocket 的能力。通过WebSocketCreator.register(Class> websocket)这个方法来告诉WebSocketServletFactory 应该创建哪个类(确保它有一个默认的构造方法)。

如果你要创建一个更复杂的场景,你或许希望提供你自己的基于WebSocket 的WebSocketCreator ,它会将创建时的信息将会存在于UpgradeRequest 对象中。

packageexamples;importorg.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;importorg.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;importorg.eclipse.jetty.websocket.servlet.WebSocketCreator;public class MyAdvancedEchoCreator implementsWebSocketCreator

{privateMyBinaryEchoSocket binaryEcho;privateMyEchoSocket textEcho;publicMyAdvancedEchoCreator()

{//创建可重用的套接字

this.binaryEcho = newMyBinaryEchoSocket();this.textEcho = newMyEchoSocket();

}

@OverridepublicObject createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)

{for(String subprotocol : req.getSubProtocols())

{if ("binary".equals(subprotocol))

{

resp.setAcceptedSubProtocol(subprotocol);returnbinaryEcho;

}if ("text".equals(subprotocol))

{

resp.setAcceptedSubProtocol(subprotocol);returntextEcho;

}

}//没有有效的请求,忽略它

return null;

}

}

这里我们展示了一个WebSocketCreator ,它可以通过request中 WebSocket子协议的名称来判断应该创建哪种类型的WebSocket 。

packageexamples;importjavax.servlet.annotation.WebServlet;importorg.eclipse.jetty.websocket.servlet.WebSocketServlet;importorg.eclipse.jetty.websocket.servlet.WebSocketServletFactory;

@SuppressWarnings("serial")

@WebServlet(name= "MyAdvanced Echo WebSocket Servlet", urlPatterns = { "/advecho"})public class MyAdvancedEchoServlet extendsWebSocketServlet

{

@Overridepublic voidconfigure(WebSocketServletFactory factory)

{//设置10秒超时时间

factory.getPolicy().setIdleTimeout(10000);//设置一个普通的创建者

factory.setCreator(newMyAdvancedEchoCreator());

}

}

当你想定制一个WebSocketCreator,使用 WebSocketServletFactory.setCreator(WebSocketCreator creator)这个方法,那么WebSocketServletFactory 将会使用你定义的WebSocketCreator 来对所有到servlet的请求进行升级。

WebSocketCreator的其它用途:

控制WebSocket 子协议的选择

执行任何你认为重要的WebSocket源

获得传入请求的HTTP头信息

获得Servlet HttpSession对象(如果存在的话)

指定response 状态码和原因

如果你不愿接收升级后的,可以简单的返回null值。

27.9 Jetty Websocket 客户端API

Jetty同样提供了一个Jetty客户端WebSocket 的库用来简化编写WebSocket 服务。

为了在你的Java项目中使用这个库,你只需要增加以下坐标:

org.eclipse.jetty.websocket

websocket-client

${project.version}

27.9.1 WebSocketClient介绍

为了使用WebSocketClient ,你需要将一个WebSocket 实例与一个特殊的WebSocket URI进行挂钩。

packageexamples;importjava.net.URI;importjava.util.concurrent.TimeUnit;importorg.eclipse.jetty.websocket.client.ClientUpgradeRequest;importorg.eclipse.jetty.websocket.client.WebSocketClient;/*** 一个简单的客户端例子*/

public classSimpleEchoClient

{public static voidmain(String[] args)

{

String destUri= "ws://echo.websocket.org";if (args.length > 0)

{

destUri= args[0];

}

WebSocketClient client= newWebSocketClient();

SimpleEchoSocket socket= newSimpleEchoSocket();try{

client.start();

URI echoUri= newURI(destUri);

ClientUpgradeRequest request= newClientUpgradeRequest();

client.connect(socket,echoUri,request);

System.out.printf("Connecting to : %s%n",echoUri);//等待关闭的socket 连接

socket.awaitClose(5,TimeUnit.SECONDS);

}catch(Throwable t)

{

t.printStackTrace();

}finally{try{

client.stop();

}catch(Exception e)

{

e.printStackTrace();

}

}

}

}

上面的例子连接了一个远程的WebSocket ,一旦被连接上SimpleEchoSocket 将会处理逻辑、等待socket被注册,直到它关闭。

packageexamples;importjava.util.concurrent.CountDownLatch;importjava.util.concurrent.Future;importjava.util.concurrent.TimeUnit;importorg.eclipse.jetty.websocket.api.Session;importorg.eclipse.jetty.websocket.api.StatusCode;importorg.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;importorg.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;importorg.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;importorg.eclipse.jetty.websocket.api.annotations.WebSocket;/*** Socket的基本例子*/@WebSocket(maxTextMessageSize= 64 * 1024)public classSimpleEchoSocket

{private finalCountDownLatch closeLatch;

@SuppressWarnings("unused")privateSession session;publicSimpleEchoSocket()

{this.closeLatch = new CountDownLatch(1);

}public boolean awaitClose(int duration, TimeUnit unit) throwsInterruptedException

{return this.closeLatch.await(duration,unit);

}

@OnWebSocketClosepublic void onClose(intstatusCode, String reason)

{

System.out.printf("Connection closed: %d - %s%n",statusCode,reason);this.session = null;this.closeLatch.countDown(); //触发位置

}

@OnWebSocketConnectpublic voidonConnect(Session session)

{

System.out.printf("Got connect: %s%n",session);this.session =session;try{

Futurefut;

fut= session.getRemote().sendStringByFuture("Hello");

fut.get(2,TimeUnit.SECONDS); //等待发送完成

fut= session.getRemote().sendStringByFuture("Thanks for the conversation.");

fut.get(2,TimeUnit.SECONDS); //等待发送完成

session.close(StatusCode.NORMAL,"I'm done");

}catch(Throwable t)

{

t.printStackTrace();

}

}

@OnWebSocketMessagepublic voidonMessage(String msg)

{

System.out.printf("Got msg: %s%n",msg);

}

}

当SimpleEchoSocket 连接上,它将发送两个Text文本,并关闭它。

这个onMessage(String msg)方法,接收远程WebSocket 的responses ,并将它们输出到控制台上。

java框架 jetty_Jetty使用教程(四:24-27)—Jetty开发指南相关推荐

  1. 基于java框架的图书分享系统的设计与开发计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署

    基于java框架的图书分享系统的设计与开发计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署 基于java框架的图书分享系统的设计与开发计算机毕业设计源码+系统+lw文档+mysql数据库 ...

  2. Jetty使用教程(四:24-27)—Jetty开发指南

    二十四.处理器(Handler ) 24.1 编写一个常用的Handler Jetty的Handler组件用来处理接收到的请求. 很多使用者不需要编写Jetty的Handler ,而是通过使用Serv ...

  3. Jetty使用教程(四:23)—Jetty开发指南

    二十三.Maven和Jetty 这一章节将说明如何通过Maven管理Jetty和使用Jetty的Maven插件. 23.1 使用Maven Apache Maven是一个款软件项目管理工具.基于项目对 ...

  4. 四、达尔文机器人开发指南(针对中国机器人比赛、华北五省机器人比赛)-----达尔文官方框架解读

    这是我开发过程中,参考的第二个博客,同样为了好理解,这里我在归纳一下.以下是达尔文的各模块图: 一.DARwin 机器人Frame代码简析(一) 1. CM730.cpp 主要定义了一些和控制板CM7 ...

  5. IntelliJ IDEA 12详细开发教程(四) 搭建Android应用开发环境与Android项目创建

    今天我要给大家讲的是使用Intellij Idea开发Android应用开发.自我感觉使用Idea来进行Android开发要比在Eclipse下开发简单很多. (一)打开网站:http://devel ...

  6. java web怎么快速设计网页_Javaweb毕业设计快速开发指南(一)

    在开篇之前,我们先聊一个问题.一个合格的某某管理系统,应该有哪些内容呢? 很多同学在这一步就开始出问题了.拿到了题目,一般是基于Java的会员管理系统.基于SSM的员工管理系统.基于Springboo ...

  7. 情侣积分微信小程序零基础开发教程(附代码及开发指南)

    To Our Loves 本文最新版本 0 情侣积分互动小程序 在idofSunChonggao 的基础上进行开发, 感谢! 且感谢初版(UxxHans)! ⭐ 如果本仓库对您有所帮助,您的fork或 ...

  8. 【IoT】硬件PM系列(四):硬件产品开发指南「构思、设计、工程、验证」

    与软件封闭开发不同,硬件产品会涉及供应链和渠道管理,对成本和时间的限制性要求较高. 硬件产品很难实现类似软件产品的快速迭代,初创硬件公司也往往只有一次机会来交付产品. Eric Ries 推广的精益开 ...

  9. 别再用那些已经淘汰的技术了!2020 年 9 大顶级 Java 框架出炉!!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:Patricia Neil towardsdatascien ...

最新文章

  1. 服务器标记“asp:ScriptManager”不明确。
  2. 公司上网速度极慢(断网)解决思路_网络TS
  3. 解决 Angular 官网下载的库 Schematics 在 windows 环境不支持 .. 的临时解决方案
  4. 【转】什么是CORS
  5. matlab 2014a安装方法以及解决打包失败的问题
  6. 沧小海基于xilinx srio核的学习笔记之第四章 Xilinx SRIO的示例分析(一)
  7. 简单几步开启Mac访问NTFS格式读写
  8. c++程序员书籍推荐
  9. MacOS Ventura 13.0 (22A380) 正式版带 OC 0.8.5 and winPE 双分区原版黑苹果镜像
  10. 怎么将知网论文caj导出word文件
  11. 兵团教师计算机水平考试免考条件,中小学教师等6类人员 职称评审可免考外语...
  12. PriceFromImage\UnCodebase
  13. 计算机名无法开机,电脑无法开机提示0xc00000bb错误
  14. [深度学习][原创]常用ocr框架和技术总结
  15. 手把手教学51单片机 | 第四节 动态数码管,用6位数码管做一个时钟
  16. Python3 - 三天学会微信小程序(Python后端研习)
  17. Java基础综合习题(二)
  18. 金仓数据库KingbaseES数据库中存储过程和函数的区别
  19. 大学四年专业学习规划目标
  20. prim算法适用条件_Prim算法和Kruskal算法介绍

热门文章

  1. 一个vue的日历组件
  2. RGBD-slam和DVO-slam安装及运行
  3. 简易重装win10系统
  4. eml企业通讯录管理系统经典版V5.4.14适配phpstudy8修改版
  5. JavaScript从零开始3
  6. 原来markdown有这用法,用markdown生成目录,惭愧,现在才知道
  7. [量化-018]如何理解券商
  8. 移动短信回执怎么开通_如何取消移动短信回执
  9. PyTorch Geometric安装
  10. STM8驱动LCD段码屏