Netty5入门(3)

一、示例介绍

示例取自《基于Netty5.0高级案例一之NettyWebsocket》,和《Netty inAction》中11章的例子一样,这个例子通过WebSocket实现了一个聊天室的群发功能。但后者的例子我没本事跑通。

新建一个Maven项目,项目名称叫NettyWebSocket,具体过程请参考前一贴。别忘了在pom.xml中加入netty5.0的依赖。

在项目中新建4个class:

4个类的代码你可以从后面的内容中找到,这里先不考虑代码的问题。用“复制>>粘贴”将代码原样拷贝到这4个源文件中再说。

注意,如果源代码中缺少import语句,请自行fixed一下。

在NettyServe.java上右键,Run As >> Java Application,运行服务端代码。此时控制台将输出:

服务端开启等待客户端连接 ... ...

然后在磁盘上创建一个index.html文件,文件内容你也可以在后面的内容中找到。在Finder中双击index.html文件,用Safari打开。

在Dock栏上右击Safari图标,选择“新建窗口”,打开另一个Safari窗口。然后将第一个Safari窗口中的地址复制到第二个Safari窗口地址栏中,回车。

将两个窗口并列,你可以看到在一个窗口中输入的聊天消息,在另一个窗口中会实时得到刷新,显然是服务端通过WebSocket同时向所有连接的客户端进行了推送:

在服务端控制台中也会有输出:

测试完毕,我们下面再来介绍代码。

注意,如果使用Safari测试,当你关闭Safari,服务端会输出“客户端与服务端连接关闭”。如果使用Chrome测试,当你关闭Chrome时,服务端会抛出一个“UnsupportedOperationException”异常。

二、Global.java

这个类很简单,就是定义了一个全局变量ChannelGroup  group,这样在后面的其它类(主要是MyWebSocketServerHandler)中就不用定义group了,直接使用就行了。

源代码(如果你已经复制/粘贴过源代码了,请跳过):

public class Global {

public staticChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

}

三、ChildChannelHandler.java

这个类实现了ChannelInitializer,即Channel与ChannelHandler的绑定,也就是向pipeline中填入各个ChannelHandler。

源代码(如果你已经复制/粘贴过源代码了,请跳过):

public classChildChannelHandler extends ChannelInitializer<SocketChannel>{

@Override

protected void initChannel(SocketChannel e) throws Exception {

e.pipeline().addLast("http-codec",new HttpServerCodec());

e.pipeline().addLast("aggregator",new HttpObjectAggregator(65536));

e.pipeline().addLast("http-chunked",new ChunkedWriteHandler());

e.pipeline().addLast("handler",newMyWebSocketServerHandler());

}

}

四、MyWebSocketServerHandler.java

服务器所有的业务逻辑被放到这里,当然也包括我们的WebSocket群发。

源代码(如果你已经复制/粘贴过源代码了,请跳过):

public class MyWebSocketServerHandlerextends

SimpleChannelInboundHandler<Object>{

private static final Logger logger = Logger

.getLogger(WebSocketServerHandshaker.class.getName());

private WebSocketServerHandshaker handshaker;

@Override

public voidchannelActive(ChannelHandlerContext ctx) throws Exception {

// 添加

Global.group.add(ctx.channel());

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

}

@Override

public voidchannelInactive(ChannelHandlerContext ctx) throws Exception {

// 移除

Global.group.remove(ctx.channel());

System.out.println("客户端与服务端连接关闭");

}

@Override

protected void messageReceived(ChannelHandlerContext ctx, Object msg)

throws Exception {

if (msg instanceof FullHttpRequest) {

handleHttpRequest(ctx, ((FullHttpRequest) msg));

} else if (msg instanceofWebSocketFrame) {

handlerWebSocketFrame(ctx, (WebSocketFrame) msg);

}

}

@Override

public voidchannelReadComplete(ChannelHandlerContext ctx) throws Exception {

ctx.flush();

}

private voidhandlerWebSocketFrame(ChannelHandlerContext ctx,

WebSocketFrameframe) {

// 判断是否关闭链路的指令

if (frame instanceof CloseWebSocketFrame) {

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

.retain());

return;

}

// 判断是否ping消息

else if (frame instanceofPingWebSocketFrame) {

ctx.channel().write(

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

return;

}

// 本例程仅支持文本消息,不支持二进制消息

else if (!(frame instanceofTextWebSocketFrame)) {

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

throw newUnsupportedOperationException(String.format(

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

}

// 返回应答消息

Stringrequest = ((TextWebSocketFrame) frame).text();

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

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

logger

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

request));

}

TextWebSocketFrametws = new TextWebSocketFrame(new Date().toString()

+ctx.channel().id() + ":" + request);

// 群发

Global.group.writeAndFlush(tws);

// 返回【谁发的发给谁】

// ctx.channel().writeAndFlush(tws);

}

private voidhandleHttpRequest(ChannelHandlerContext ctx,

FullHttpRequestreq) {

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

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

sendHttpResponse(ctx, req, new DefaultFullHttpResponse(

HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));

return;

}

WebSocketServerHandshakerFactorywsFactory = new WebSocketServerHandshakerFactory(

"ws://localhost:7397/websocket", null, false);

handshaker = wsFactory.newHandshaker(req);

if (handshaker == null) {

WebSocketServerHandshakerFactory

.sendUnsupportedWebSocketVersionResponse(ctx.channel());

} else {

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

}

}

private static void sendHttpResponse(ChannelHandlerContext ctx,

FullHttpRequestreq, DefaultFullHttpResponse res) {

// 返回应答给客户端

if (res.getStatus().code()!= 200) {

ByteBufbuf = Unpooled.copiedBuffer(res.getStatus().toString(),

CharsetUtil.UTF_8);

res.content().writeBytes(buf);

buf.release();

}

// 如果是非Keep-Alive,关闭连接

ChannelFuturef = ctx.channel().writeAndFlush(res);

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

f.addListener(ChannelFutureListener.CLOSE);

}

}

private static boolean isKeepAlive(FullHttpRequest req) {

return false;

}

@Override

public voidexceptionCaught(ChannelHandlerContext ctx, Throwable cause)

throws Exception {

cause.printStackTrace();

ctx.close();

}

}

时间: 2024-08-02 21:04:22

Netty5入门(3)的相关文章

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入门学习笔记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经典教程

Netty5入门(4)

这个类实现SimpleChannelInboundHandler,SimpleChannelInboundHandler是一个抽象类,实现了中定义的channelRead方法,但同时定义了一个抽象的messageReceived方法,因此我们在MyWebSocketServerHandler类中,不需要实现channelRead方法,但需要实现messageReceived方法.当然,我们还需要覆盖ChannelHandlerAdapter的channelActive方法和channelInac

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