Netty4.0学习笔记系列之四:混合使用coder和handler
来源:互联网 发布:订演唱会门票软件 编辑:程序博客网 时间:2024/04/30 20:17
Handler如何使用在前面的例子中已经有了示范,那么同样是扩展自ChannelHandler的Encoder和Decoder,与Handler混合后又是如何使用的?本文将通过一个实际的小例子来展示它们的用法。
该例子模拟一个Server和Client,两者之间通过http协议进行通讯,在Server内部通过一个自定义的StringDecoder把httprequest转换成String。Server端处理完成后,通过StringEncoder把String转换成httpresponse,发送给客户端。具体的处理流程如图所示:
其中红色框中的Decoder、Encoder及request都是Netty框架自带的,灰色框中的三个类是我自己实现的。
Server端的类有:Server StringDecoder BusinessHandler StringEncoder四个类。
1、Server 启动netty服务,并注册handler、coder,注意注册的顺序:
package com.guowl.testmulticoderandhandler;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelOption;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.codec.http.HttpRequestDecoder;import io.netty.handler.codec.http.HttpResponseEncoder;// 测试coder 和 handler 的混合使用public class Server {public void start(int port) throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Overridepublic void initChannel(SocketChannel ch) throws Exception {// 都属于ChannelOutboundHandler,逆序执行ch.pipeline().addLast(new HttpResponseEncoder());ch.pipeline().addLast(new StringEncoder());// 都属于ChannelIntboundHandler,按照顺序执行ch.pipeline().addLast(new HttpRequestDecoder());ch.pipeline().addLast(new StringDecoder());ch.pipeline().addLast(new BusinessHandler());}}).option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}public static void main(String[] args) throws Exception {Server server = new Server();server.start(8000);}}2、StringDecoder 把httpRequest转换成String,其中ByteBufToBytes是一个工具类,负责对ByteBuf中的数据进行读取
package com.guowl.testmulticoderandhandler;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;import io.netty.handler.codec.http.HttpContent;import io.netty.handler.codec.http.HttpHeaders;import io.netty.handler.codec.http.HttpRequest;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import com.guowl.utils.ByteBufToBytes;public class StringDecoder extends ChannelInboundHandlerAdapter {private static Loggerlogger= LoggerFactory.getLogger(StringDecoder.class);private ByteBufToBytesreader;@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {logger.info("StringDecoder : msg's type is " + msg.getClass());if (msg instanceof HttpRequest) {HttpRequest request = (HttpRequest) msg;reader = new ByteBufToBytes((int) HttpHeaders.getContentLength(request));}if (msg instanceof HttpContent) {HttpContent content = (HttpContent) msg;reader.reading(content.content());if (reader.isEnd()) {byte[] clientMsg = reader.readFull();logger.info("StringDecoder : change httpcontent to string ");ctx.fireChannelRead(new String(clientMsg));}}}}3、BusinessHandler 具体处理业务的类,把客户端的请求打印出来,并向客户端发送信息
package com.guowl.testmulticoderandhandler;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class BusinessHandler extends ChannelInboundHandlerAdapter {private Loggerlogger= LoggerFactory.getLogger(BusinessHandler.class);@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {String clientMsg = "client said : " + (String) msg;logger.info("BusinessHandler read msg from client :" + clientMsg);ctx.write("I am very OK!");}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.flush();}}
4、StringEncoder 把字符串转换成HttpResponse
package com.guowl.testmulticoderandhandler;import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;import static io.netty.handler.codec.http.HttpResponseStatus.OK;import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelOutboundHandlerAdapter;import io.netty.channel.ChannelPromise;import io.netty.handler.codec.http.DefaultFullHttpResponse;import io.netty.handler.codec.http.FullHttpResponse;import io.netty.handler.codec.http.HttpHeaders.Values;// 把String转换成httpResponsepublic class StringEncoder extends ChannelOutboundHandlerAdapter {private Loggerlogger= LoggerFactory.getLogger(StringEncoder.class);@Overridepublic void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {logger.info("StringEncoder response to client.");String serverMsg = (String) msg;FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(serverMsg.getBytes()));response.headers().set(CONTENT_TYPE, "text/plain");response.headers().set(CONTENT_LENGTH, response.content().readableBytes());response.headers().set(CONNECTION, Values.KEEP_ALIVE);ctx.write(response);ctx.flush();}}Client端有两个类:Client ClientInitHandler
1、Client 与Server端建立连接,并向Server端发送HttpRequest请求。
package com.guowl.testmulticoderandhandler;import io.netty.bootstrap.Bootstrap;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelOption;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioSocketChannel;import io.netty.handler.codec.http.DefaultFullHttpRequest;import io.netty.handler.codec.http.HttpHeaders;import io.netty.handler.codec.http.HttpMethod;import io.netty.handler.codec.http.HttpRequestEncoder;import io.netty.handler.codec.http.HttpResponseDecoder;import io.netty.handler.codec.http.HttpVersion;import java.net.URI;public class Client {public void connect(String host, int port) throws Exception {EventLoopGroup workerGroup = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap(); b.group(workerGroup); b.channel(NioSocketChannel.class); b.option(ChannelOption.SO_KEEPALIVE, true); b.handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new HttpResponseDecoder());ch.pipeline().addLast(new HttpRequestEncoder());ch.pipeline().addLast(new ClientInitHandler());}});// Start the client.ChannelFuture f = b.connect(host, port).sync();URI uri = new URI("http://127.0.0.1:8000");String msg = "Are you ok?";DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST,uri.toASCIIString(), Unpooled.wrappedBuffer(msg.getBytes()));request.headers().set(HttpHeaders.Names.HOST, host);request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);request.headers().set(HttpHeaders.Names.CONTENT_LENGTH, request.content().readableBytes());f.channel().write(request);f.channel().flush();f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();}}public static void main(String[] args) throws Exception {Client client = new Client();client.connect("127.0.0.1", 8000);}}2、ClientInitHandler 从Server端读取响应信息
package com.guowl.testmulticoderandhandler;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;import io.netty.handler.codec.http.HttpContent;import io.netty.handler.codec.http.HttpHeaders;import io.netty.handler.codec.http.HttpResponse;import com.guowl.utils.ByteBufToBytes;public class ClientInitHandler extends ChannelInboundHandlerAdapter {private ByteBufToBytesreader;@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {if (msg instanceof HttpResponse) {HttpResponse response = (HttpResponse) msg;if (HttpHeaders.isContentLengthSet(response)) {reader = new ByteBufToBytes((int) HttpHeaders.getContentLength(response));}}if (msg instanceof HttpContent) {HttpContent httpContent = (HttpContent) msg;ByteBuf content = httpContent.content();reader.reading(content);content.release();if (reader.isEnd()) {String resultStr = new String(reader.readFull());System.out.println("Server said:" + resultStr);}}}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.close();}}工具类:ByteBufToBytes 对ByteBuf的数据进行读取,支持流式读取(reading 和 readFull方法结合使用)
ByteBufToBytes
package com.guowl.utils;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;public class ByteBufToBytes {private ByteBuftemp;private booleanend= true;public ByteBufToBytes() {}public ByteBufToBytes(int length) {temp = Unpooled.buffer(length);}public void reading(ByteBuf datas) {datas.readBytes(temp, datas.readableBytes());if (this.temp.writableBytes() != 0) {end = false;} else {end = true;}}public boolean isEnd() {return end;}public byte[] readFull() {if (end) {byte[] contentByte = new byte[this.temp.readableBytes()];this.temp.readBytes(contentByte);this.temp.release();return contentByte;} else {return null;}}public byte[] read(ByteBuf datas) {byte[] bytes = new byte[datas.readableBytes()];datas.readBytes(bytes);return bytes;}}运行结果:
2014-03-19 23:50:48 StringDecoder : msg's type is class io.netty.handler.codec.http.DefaultHttpRequest
2014-03-19 23:50:48 StringDecoder : msg's type is class io.netty.handler.codec.http.DefaultLastHttpContent
2014-03-19 23:50:48 StringDecoder : change httpcontent to string
2014-03-19 23:50:48 BusinessHandler read msg from client :client said : Are you ok?
2014-03-19 23:50:48 StringEncoder response to client.
可以看到执行顺序为:StringDecoder BusinessHandler StringEncoder ,其它的都是Netty自身的,没有打印。
通过该实例证明,Encoder、Decoder的本质也是Handler,它们的执行顺序、使用方法与Handler保持一致。
执行顺序是:Encoder 先注册的后执行,与OutboundHandler一致;Decoder是先注册的先执行,与InboundHandler一致。
- Netty4.0学习笔记系列之四:混合使用coder和handler
- Netty4.0学习笔记系列之四:混合使用coder和handler
- Netty4.0学习笔记系列之四:混合使用coder和handler
- Netty4.0学习笔记系列之四:混合使用coder和handler
- Netty4.0学习笔记系列之四:混合使用coder和handler
- Netty4.0学习笔记系列之四:混合使用coder和handler
- Netty4.0学习笔记系列之四:混合使用coder和handler
- netty 学习 (4)混合使用coder和handler
- Netty4.0学习笔记系列之二:Handler的执行顺序
- Netty4.0学习笔记系列之二:Handler的执行顺序
- Netty4.0学习笔记系列之二:Handler的执行顺序
- Netty4.0学习笔记系列之二:Handler的执行顺序
- Netty4.0学习笔记系列之二:Handler的执行顺序
- Netty4.0学习笔记系列之二:Handler的执行顺序
- Netty4.0学习笔记系列之二:Handler的执行顺序
- Netty4.0学习笔记系列之二:Handler的执行顺序
- Netty4.0学习笔记系列之五:自定义通讯协议
- Netty4.0学习笔记系列之五:自定义通讯协议
- Convert Sorted List to Binary Search Tree
- 我的Android学习体系
- C#调用WebService实现天气预报
- python 中使用BeautifulSoup
- 第二周~
- Netty4.0学习笔记系列之四:混合使用coder和handler
- 利用VS2012对文本文件的单词频率统计,并分析结果
- 我的android(第十一天)
- function overload 2
- 获取本机ip和外网ip地址
- [android] viewpager获取当前view报空指针的解决方法
- (中国剩余定理) hdu 1573 X问题
- Oracle LISTENER 主机名修改为IP地址后LISTENER无法监听到实例 oracle监听错误与hosts文件配置
- 使用nginx搭建媒体点播服务器