一. ChannelHandler: 开发中需要自定义一个
Handler 类去实现 ChannelHandle接口或其子接口或其实现类
1.
public void channelActive(ChannelHandlerContext ctx)
,通道就绪事件 就是客服端和服务端成功建立连接
2.
public void channelRead(ChannelHandlerContext ctx, Object msg)
,通道读取数据事件 就是在这个方法中可以获取服务端发送过来的消息
3.
public void channelReadComplete(ChannelHandlerContext ctx)
,数据读取完毕事件 在这里相当于消息读取完了, 我们可以进行回复, 通道是可读可写的。
4.
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
,通道发生异常事
件
二. ChannelPipeline:它是handler的一个集合, 它负责处理和拦截 inbound 或者 outbound 的事件和操作,相当于一个贯穿 Netty 的责任链
比如说: 1. 我们可以往pipeline中加编解码handler 2. 我们可以让channel支持HTTP协议 3. 我们可以让post请求
request line / request header / message body 转换为一个request/response
3. 将http协议(1.1)生成WebSocket全双工协议
加载顺序:
InboundHandler
是按照
Pipleline
的加载顺序的顺序执行
, OutboundHandler
是按照
Pipeline
的加载顺序,逆序执行
三. ChannelHandlerContext:
实际上就是一个事件处理器上下文对象, Pipeline中实际处理节点, 每个处理节点context都包含一个具体的handler, 同时绑定了Pipeline和channel信息:
我们可以 1. 关闭通道 ChannelFuture close() 2. 刷新ChannelOutboundInvoker flush()
3.
将数据写到 ChannelPipeline 中当前ChannelHandler
的下一个
ChannelHandler 开始处理(出站)ChannelFuture writeAndFlush(Object msg)
四. ChannelOption:
Netty
在创建
Channel
实例后
,
一般都需要设置
ChannelOption
参数。
ChannelOption
是
Socket
的标 准参数,而非 Netty
独创的。常用的参数配置有:
1. ChannelOption.SO_BACKLOG: 指定连接队列的大小
2. ChannelOption.SO_KEEPALIVE, TCP探测通道是否活跃
五. ChannelFuture:
采用future模式, 表示 Channel 中异步 I/O 操作的结果, 调用者并不能立刻得到结果, 但是能得到IO处理的状态:
Channel channel()
,返回当前正在进行
IO
操作的通道
ChannelFuture sync()
,等待异步操作执行完毕
,
将异步改为同步
六. EventLoopGroup
和实现类
NioEventLoopGroup
之前讲到, netty有俩个组, 一个处理连接事件, 一个处理IO事件。 EventLoopGroup是一组EventLoop的抽象, 每个EventLoop维护者一个Selector实例。
EventLoopGroup 提供 next
接口,可以从组里面按照一定规则获取其中一个
EventLoop
来处理任 务。在 Netty
服务器端编程中,我们一般都需要提供两个
EventLoopGroup
,例如:
BossEventLoopGroup
和
WorkerEventLoopGroup
。 通常一个服务端口即一个
ServerSocketChannel 对应一个Selector
和一个
EventLoop
线程。
BossEventLoop
负责接收客户端的连接并将 SocketChannel 交给
WorkerEventLoopGroup
来进 行
IO
处理
七.
ServerBootstrap
和
Bootstrap
这是netty启动类, 提供group(), channel(),
option():
ServerChannel
添加配置 childOption()给接收到的通道添加配置
childHandler() bind() connect()
八.
Unpooled
这是
Netty
提供的一个专门用来操作缓冲区的工具类:
public static ByteBuf copiedBuffer(CharSequence string, Charset charset),通过给定的数据 和 字符编码返回一个 ByteBuf
对象(类似于
NIO
中的
ByteBuffer
对象)
NettyServer:
public static void main(String[] args) throws InterruptedException {
// 1. 创建bossGroup线程组: 处理网络事件--连接事件
EventLoopGroup boosGroup = new NioEventLoopGroup(1); // 默认线程数 是 处理器*2
// 2. 创建workerGroup线程组: 处理网络事件--读写事件
EventLoopGroup workGroup = new NioEventLoopGroup();
// 3. 创建服务端启动助手
ServerBootstrap bootstrap = new ServerBootstrap();
// 4. 设置bossGroup线程组和workerGroup线程组
bootstrap.group(boosGroup, workGroup)
.channel(NioServerSocketChannel.class) // 5. 设置服务端通道实现为NIO
.option(ChannelOption.SO_BACKLOG, 128) // 6. 设置参数
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() { // 7. 创建一个通道初始化对象
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
// 8. 向pipeline中添加自定义业务处理handler
socketChannel.pipeline().addLast(new EnCoder());//添加编码器
socketChannel.pipeline().addLast(new Decoder());//添加解码器
socketChannel.pipeline().addLast(new ServerHandler()) ;
}
}) ;
// 9. 启动服务端并绑定端口,同时将异步改为同步 为什么改为同步, 如果是异步,程序会向下执行,使代码出现问题
ChannelFuture future = bootstrap.bind(9999).sync(); // 必须通道连接后才能干事
future.channel().closeFuture().sync() ;
boosGroup.shutdownGracefully() ;
workGroup.shutdownGracefully() ;
}
Nettyclient:
public static void main(String[] args) throws InterruptedException {
// 1. 创建线程组
EventLoopGroup group = new NioEventLoopGroup();
// 2. 创建客户端启动助手
Bootstrap bootstrap = new Bootstrap();
// 3. 设置线程组
bootstrap.group(group)
.channel(NioSocketChannel.class) // 4. 设置客户端通道实现为NIO
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new EnCoder());//添加编码器
socketChannel.pipeline().addLast(new Decoder());//添加解码器
//6. 向pipeline中添加自定义业务处理handler
socketChannel.pipeline().addLast(new ClientHandle()) ;
}
}) ;
//7. 启动客户端,等待连接服务端,同时将异步改为同步
ChannelFuture future = bootstrap.connect("127.0.0.1", 9999).sync();
//8. 关闭通道和关闭连接池
future.channel().closeFuture().sync() ;
group.shutdownGracefully() ;
}
基于Netty的Http服务器开发:
1. NettyServer只需要向pipeline中添加HttpServerCodec
2. 添加自定义dandler
public class ServerHandler extends SimpleChannelInboundHandler<HttpObject> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
// 1. 判断msg是否来自http
if(msg instanceof HttpRequest) {
// 2. 包装响应信息
DefaultHttpRequest request = (DefaultHttpRequest) msg;
System.out.println("浏览器请求路径: " + request.uri());
// 给浏览器响应
ByteBuf buf = Unpooled.copiedBuffer("你好! 我是netty服务器!".getBytes(StandardCharsets.UTF_8));
// 得到response对象并且封装起始行
DefaultFullHttpResponse response =
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf);
// 封装headers
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=utf-8") ;
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, buf.readableBytes()) ;
ctx.writeAndFlush(response) ;
}
}
}