Netty5入门(4)

这个类实现SimpleChannelInboundHandler,SimpleChannelInboundHandler是一个抽象类,实现了中定义的channelRead方法,但同时定义了一个抽象的messageReceived方法,因此我们在MyWebSocketServerHandler类中,不需要实现channelRead方法,但需要实现messageReceived方法。当然,我们还需要覆盖ChannelHandlerAdapter的channelActive方法和channelInactive方法,因为我们想在客户端连接和关闭时做一些事情。

1、channelActive和channelInactive方法

这两个方法分别在新的客户端连接到服务端时触发,我们仅仅是在这两个方法中进行Channel的添加和移除操作,并输出一些内容到控制台而已。

2、messageReceived方法

真正的业务逻辑在这个方法里。在这个方法中,我们针对客户端的请求类型进行处理——因为我们不知道客户端到底会是什么样子以及会以何种方式请求服务端。如果客户端是以WebSocket的方式(即ws://)请求的,我们调用handlerWebSocketFrame进行处理,否则调用handleHttpRequest方法。

3、handleWebSocketFrame方法

这个方法用于处理WebSocket请求,即WebSocket握手完成后(即handshaker已经初始化)的消息。

首先需要判断WebSocket请求(即WebSocketFrame类)的具体类型,以进行不同的操作。

这里需要介绍一下WebSocket 数据传输格式中的Opcode定义。Opcode是一个4位操作码,定义有效负载数据,如果收到了一个未知的操作码,连接也必须断掉,以下是定义的操作码:

*  %x0 表示连续消息片断

*  %x1 表示文本消息片断

*  %x2 表示二进制消息片断

*  %x3-7 为将来的非控制消息片断保留的操作码

*  %x8 表示连接关闭

*  %x9 表示心跳检查的ping

*  %xA 表示心跳检查的pong

*  %xB-F 为将来的控制消息片断的保留操作码

根据这个,我们可以对frame的类型进行判断:

首先判断是不是WebSocket关闭操作:

if (frame instanceof CloseWebSocketFrame) {

handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());

return;

}

如果是,关闭handshaker,即WebSocket会话。

然后判断是不是Ping消息,如果是,则发送一个Pong消息给客户端:

if (frame instanceof PingWebSocketFrame) {

ctx.channel().write(

new PongWebSocketFrame(frame.content().retain()));

return;

}

WebSocket中,Ping、Pong消息用于心跳检查。客户端使用的叫做Ping,服务端对之进行的响应叫做Pong。

注意,凡是引用到frame时,都要retain一下。因为netty所有io操作都是异步的,这样做是防止frame在还没有用完的时候就被释放掉了。

然后判断消息的类型是否是文本消息:

if (!(frame instanceof TextWebSocketFrame)) {

System.out.println("本例程仅支持文本消息,不支持二进制消息");

throw newUnsupportedOperationException(String.format(

"%s frame types notsupported", frame.getClass().getName()));

}

如果不是,抛出UpsupportedOperationException错误。

如果前面三种情况都不是,则frame应该是一个合法的WebSocket文本消息了,我们进行接下来的处理:

String request = ((TextWebSocketFrame) frame).text();

System.out.println("服务端收到:" + request);

if (logger.isLoggable(Level.FINE)) {

logger.fine(String.format("%s received %s", ctx.channel(),request));

}

TextWebSocketFrame tws = new TextWebSocketFrame(new Date().toString()+ctx.channel().id() + ":" + request);

// 群发

Global.group.writeAndFlush(tws);

打印收到的内容,记录日志,然后最后一句实现群发。

3、handleHttpRequest方法

这个方法处理HTTP请求。一个WebSocket会话的开始其实是由一个HTTP请求开始的。根据HTTP1.1的定义,这个HTTP请求的头信息中必须包含一个Upgrade:websocket的key-value。如果不包含则表明这不是一个标准的WebSocket会话的开始,我们可以调用sendHttpResponse输出一个Bad Request错误:

if (!req.getDecoderResult().isSuccess()

||(!"websocket".equals(req.headers().get("Upgrade")))) {

sendHttpResponse(ctx, req, new DefaultFullHttpResponse(

HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));

return;

}

接下来开始进行WebSocket连接,这是通过创建一个WebSocket的handshaker对象来完成的:

WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("ws://localhost:7397/websocket", null, false);

handshaker = wsFactory.newHandshaker(req);

如果handshaker创建失败,发送错误消息,否则开始进行握手动作:

if (handshaker == null) {

WebSocketServerHandshakerFactory

.sendUnsupportedWebSocketVersionResponse(ctx.channel());

} else {

handshaker.handshake(ctx.channel(), req);

}

4、sendHttpResponse方法

这个方法用于向客户端输出一些HTTP消息。

首先它判断服务器要输出的是不是HTTP200状态(准备就绪),如果不是,表明出错了,将HTTP状态码输出给客户端:
if (res.getStatus().code() != 200) {

ByteBufbuf = Unpooled.copiedBuffer(res.getStatus().toString(),CharsetUtil.UTF_8);

res.content().writeBytes(buf);

buf.release();

}

ChannelFuture f =ctx.channel().writeAndFlush(res);

然后关闭连接。当然要判断一下keep alive 和 HTTP 200标志:

if (!isKeepAlive(req) || res.getStatus().code() != 200) {

f.addListener(ChannelFutureListener.CLOSE);

}

根据HTTP1.1协议的规定,

五、NettyServer.java

这个类代表了服务端主线程。在run方法中,我们将所有类串联在一起。对于netty客户端,需要用到两个group:

EventLoopGroup bossGroup = new NioEventLoopGroup();

EventLoopGroup workGroup = new NioEventLoopGroup();

其中bossGroup用于所有channel,workGroup则应用于某个channel。

然后是规定动作ServerBootstrap,group,channel以及handler等等,这些代码都非常模式化,恐怕不需要再多做说明了:

ServerBootstrap b = new ServerBootstrap();

b.group(bossGroup, workGroup);

b.channel(NioServerSocketChannel.class);

b.childHandler(newChildChannelHandler());

System.out.println("服务端开启等待客户端连接 ... ...");

Channel ch = b.bind(7397).sync().channel();

ch.closeFuture().sync();

六、index.html

这个就是所谓的客户端了,是跑在浏览器里的东东。这个文件随便你放在那里(不需要放到web服务器上),反正用浏览器一打开,其中的js脚本就会生成一个WebSocket客户端:

<!DOCTYPE html PUBLIC "-//W3C//DTDXHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

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

<head>

<metahttp-equiv="Content-Type" content="text/html;charset=utf-8" />

<title>无标题文档</title>

</head>

</head>

<script type="text/javascript">

varsocket;

if(!window.WebSocket){

window.WebSocket = window.MozWebSocket;

}

if(window.WebSocket){

socket = newWebSocket("ws://localhost:7397/websocket");

socket.onmessage = function(event){

varta = document.getElementById(‘responseText‘);

ta.value+= event.data+"\r\n";

};

socket.onopen = function(event){

varta = document.getElementById(‘responseText‘);

ta.value= "打开WebSoket 服务正常,浏览器支持WebSoket!"+"\r\n";

};

socket.onclose = function(event){

varta = document.getElementById(‘responseText‘);

ta.value= "";

ta.value= "WebSocket 关闭"+"\r\n";

};

}else{

alert("您的浏览器不支持WebSocket协议!");

}

function send(message){

if(!window.WebSocket){return;}

if(socket.readyState== WebSocket.OPEN){

socket.send(message);

}else{

alert("WebSocket连接没有建立成功!");

}

}

</script>

<body>

<form onSubmit="return false;">

<input type = "text"name="message" value="Netty The Sinper"/>

<br/><br/>

<input type="button"value="发送 WebSocket 请求消息" onClick="send(this.form.message.value)"/>

<hr color="blue"/>

<h3>服务端返回的应答消息</h3>

<textarea id="responseText"style="width: 1024px;height: 300px;"></textarea>

</form>

</body>

</html>

function send(message){

if(!window.WebSocket){return;}

if(socket.readyState== WebSocket.OPEN){

socket.send(message);

}else{

alert("WebSocket连接没有建立成功!");

}

}

时间: 2024-10-10 21:29:00

Netty5入门(4)的相关文章

Netty5入门(2)

四.Time协议 继续测试<netty5用户指南>中的Time协议. 1.一个封装时间的特殊的POJO类 首先实现UnixTime类: package com.ydtf; import java.util.Date; public class UnixTime { private final int value; public UnixTime() { this((int) (System.currentTimeMillis() / 1000L+ 2208988800L)); } public

Netty5入门(1)

一.     搭建开发环境 本教程使用的开发环境为MacOSX 10.10+JDK1.8+Eclipse Java EE IDE4.4.1+Maven3.2.5.以下是开发环境的搭建过程. 1. 安装JDK 1.8 本教程使用的JDK版本为JDK1.8.你可以在http://java.com下载到这个最新的JDK. JDK的安装配置就不用多说了,你明白的. 2. 安装Eclipse Java EE 本教程使用Eclipse为Eclipse Java EE 4.4.1,你可以在http://www

Netty5入门(3)

一.示例介绍 示例取自<基于Netty5.0高级案例一之NettyWebsocket>,和<Netty inAction>中11章的例子一样,这个例子通过WebSocket实现了一个聊天室的群发功能.但后者的例子我没本事跑通. 新建一个Maven项目,项目名称叫NettyWebSocket,具体过程请参考前一贴.别忘了在pom.xml中加入netty5.0的依赖. 在项目中新建4个class: 4个类的代码你可以从后面的内容中找到,这里先不考虑代码的问题.用"复制>

Netty5入门学习笔记001

Netty官网:http://netty.io/ 本例程使用最新的netty5.x版本编写 服务器端: TimeServer 时间服务器 服务端接收客户端的连接请求和查询当前时间的指令,判断指令正确后响应返回当前服务器的校准时间. package c1; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializ

netty5入门教程0

1.Netty是什么? 本质:JBoss做的一个Jar包 目的:快速开发高性能.高可靠性的网络服务器和客户端程序 优点:提供异步的.事件驱动的网络应用程序框架和工具 通俗的说:一个好使的处理Socket的东东 2.Netty的异步事件驱动模型主要涉及到下面几个核心的概念   Channel:表示一个与socket关联的通道  ChannelPipeline: 管道,一个Channel拥有一个ChannelPipeline,ChannelPipeline维护着一个处理链(严格的说是两 个:upst

Netty5入门学习笔记004-使用Netty传输POJO对象(上)

使用Netty传输POJO对象,重点在于对象的序列化,序列化后的对象可以通过TCP流进行网络传输,结合Netty提供的对象编解码器,可以做到远程传输对象. 下面我们来看一个例子:模拟订票 首先Java序列化的POJO对象需要实现java.io.Serializable接口. 火车车次和余票量POJO: package bookticket; import java.io.Serializable; /**  * 火车pojo对象  * @author xwalker  */ public cla

Netty5入门学习笔记003-TCP粘包/拆包问题的解决之道(下)

TCP网络通信时候会发生粘包/拆包的问题,上节使用定长解码器解码,本次使用Netty提供的特殊分隔符解码器 还是用上节中的代码例子,但是只需要修改一下发送的消息和配置一下解码器就可以了 客户端发送消息中添加分隔符做为指令的结束符,模拟多条指令粘包发出 服务器配置分隔符解码器使用&符号拆包 运行结果: 服务器使用分隔符解码器成功拆包. 当然还有更复杂的自定义协议处理TCP粘包/拆包问题,后续深入学习后在进行讨论. 史上最高性价比PS教程-敬伟Photoshop经典教程

JAVA通信系列三:Netty入门总结

一.Netty学习资料 书籍<Netty In Action中文版> 对于Netty的十一个疑问http://news.cnblogs.com/n/205413/ 深入浅出Nettyhttp://wenku.baidu.com/view/7765bc2db4daa58da0114a4c.html Netty了解与小试 http://www.cnblogs.com/xd502djj/archive/2012/06/25/2561318.html Netty系列之Netty高性能之道[精彩]htt

OSChina 技术周刊第十五期——每周技术精粹集锦

每周技术抢先看,总有你想要的! 移动开发 [软件]移动基站数据分析 SnoopSnitch [博客]android自动连接wifi--WifiManager [资讯]OSC 安卓客户端全面改版 -- 新界面新体验 [资讯][email protected] 项目推荐 -- Android 日历控件 前端开发 [翻译]JavaScript 应用框架 Bearcat [博客]基于HT的CSG功能构建HTML5的3D书架 [资讯][email protected] 项目推荐 -- AngularJS