编码和解码介绍
-
1)编写网络应用程序时,因为数据在网络中传输的都是二进制字节码,在发送数据时就需要编码,接收数据时就需要解码
-
2)codec(编解码器)的组成:decoder(解码器)和encode(编码器)。encoder负责把业务数据转换成字节码数据,decoder负责把字节码数据转换成业务数据
-
3)Netty自身提供了一些coder(编解码器)
编码器
StringEncoder,对字符串数据进行编码
ObjectEncoder,对java对象进行编码
解码器
StringDecoder,对字符串数据进行解码
ObjectDecoder,对java对象进行解码
-4)Netty本身自带的ObjectEncoder和ObjectDecoder可以用来实现POJO对象或者各种业务对象的编码和解码,底层用的仍然时java序列化技术,而java序列化技术本身效率就不高,存在如下问题
无法跨语言
序列化之后的体积太大,是二进制编码的5倍多
序列化性能太低
引出新的解决方案[google 的 Protobuf ]
Future说明
-
1)表示异步的执行结果,可以通过他提供的方法来检测执行是否完成,比如检索计算等等
-
2)ChannelFuture是一个接口:public interface ChannelFuture extends
Future 我们可以添加监听器,当监听的事件发生时,会通知。 -
3)工作原理示意图
-
说明:
在使用Netty进行编程时,拦截操作和转换出入栈数据只需要您提供,callback或利用future即可。这使得链式操作、简单、高效,并有利于编码可重用的、通用的代码 -
4.Future -Listener机制
- 当Future对象刚刚创建时,处于非完成状态,调用者可以通过返回channelFuture来获取操作执行的状态,注册监听函数来执行完成后的操作
常见的操作有:
通过isDone方法来判断当前的操作是否完成
通过isSuccess方法来判断当前的操作是否成功
通过getCause方法来判断当前的操作失败的原因
通过isCancelled方法来判断已完成的当前操作是否被取消
通过addListener方法来注册监听器,当操作已完成(isDone方法返回完成),将会通知指定的监听器;如果Future对象已完成,则通知指定的监听器
- 当Future对象刚刚创建时,处于非完成状态,调用者可以通过返回channelFuture来获取操作执行的状态,注册监听函数来执行完成后的操作
举例说明
- 演示:绑定端口是异步操作,当绑定操作处理完成后,将会调用响应的监听器处理逻辑
//给cf注册监听器,监控我们关心的事件
cf.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (cf.isSuccess()){
System.out.println("监听端口6668,成功");
}else{
System.out.println("监听失败");
}
}
});
快速入门实列-HTTP服务
- 1)Netty服务器在6668端口监听,浏览器发送请求http://localhost:6668/
- 2)服务器可以回复消息给客户端“hello 我是服务器”,并对特定请求资源进行过滤
- 3)目的:Netty可以做HTTP服务开发,并理解handler实例和客户端及其请求的关系
- 4)代码如下
package com.dd.netty.http;
import com.dd.netty.simple.NettyServerHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class TestServer {
public static void main(String[] args) {
//创建bossGroup 和 WorkerGroup
//bossGroup只处理连接请求,真正的客户端业务处理,会交给workerGroup完成
//两个都是无限循环
//bossgroup和workergroup含有的子线程(NIOeventloop)的个数 默认为cpu核数乘以2
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup woekerGroup = new NioEventLoopGroup();
try {
//创建服务器端启动的对象
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, woekerGroup)//设置两个线程组
.channel(NioServerSocketChannel.class)//使用NIOsocketChannel实现服务器通道
.option(ChannelOption.SO_BACKLOG, 128)//设置线程队列得到连接的个数
.childOption(ChannelOption.SO_KEEPALIVE, true)//设置保持活动连接状态
.childHandler(new TestServerInitializer());//给WorkerGoop的 EventLoop 对应的管道设置处理器
//绑定一个端口并且同步处理,生成一个ChannelFuture,并启动服务器
ChannelFuture cf = bootstrap.bind(16669).sync();
//给cf注册监听器,监控我们关心的事件
cf.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (cf.isSuccess()){
System.out.println("监听端口16669,成功");
}else{
System.out.println("监听失败");
}
}
});
//对关闭通道进行监听
cf.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
woekerGroup.shutdownGracefully();
}
}
}
package com.dd.netty.http;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
public class TestServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//向管道加入处理器
//得到管道
ChannelPipeline pipeline = socketChannel.pipeline();
//加入Netty提供的HttpServerCodec codec=>[code decoder]
pipeline.addLast("myHttpServerCodec",new HttpServerCodec());
//增加自定义的handler
pipeline.addLast("myTestHttpServerHandler",new TestHttpServerHandler());
}
}
package com.dd.netty.http;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import java.net.URI;
/*
说明:他是ChannelInboundHandlerAdapter的子类
HttpObject表示客户端和服务器端相互通讯的数据被封装成HttpObject
*/
public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
/*
当有读取事件会触发这个方法 读取客户端数据
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
//判断msg是不是http请求
if (msg instanceof HttpRequest){
System.out.println("pipleline hashcode"+ctx.pipeline().hashCode()+"testHttpServerHandler hashcode"+this.hashCode());
System.out.println("msg类型"+msg.getClass());
System.out.println("客户端地址"+ctx.channel().remoteAddress());
//获取请求,或滤掉网站图标访问
HttpRequest httpRequest = (HttpRequest)msg;
URI uri = new URI(httpRequest.uri());
if ("/favicon.ico".equals(uri.getPath())){
System.out.println("请求了 favicon.ico ,不做响应");
return;
}
//回复信息给浏览器 [http协议]
ByteBuf content = Unpooled.copiedBuffer("hello,我是服务器",CharsetUtil.UTF_8);
//构造一个http协议,即httpResponse
FullHttpResponse reponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
reponse.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain;charset=UTF-8");
reponse.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());
//将构建好的response返回
ctx.writeAndFlush(reponse);
}
}
}
- 5)发现访问一次地址,会发送两次请求
//获取请求,或滤掉网站图标访问
HttpRequest httpRequest = (HttpRequest)msg;
URI uri = new URI(httpRequest.uri());
if ("/favicon.ico".equals(uri.getPath())){
System.out.println("请求了 favicon.ico ,不做响应");
return;
}
- 注意:每个浏览器请求该地址,都会分别生成自己对应的piple,handler。由于为HTTP协议,它不是长连接,刷新浏览器会产生新的piple,handler。