Netty学习之客户端创建
一、客户端开发时序图
图片来源:Netty权威指南(第2版)
二、Netty客户端开发步骤
使用Netty进行客户端开发主要有以下几个步骤:
1、用户线程创建Bootstrap
Bootstrap b = new Bootstrap();
Bootstrap是Socket客户端创建工具类,通过API设置创建客户端相关的参数,异步发起客户端连接。
2、创建处理客户端连接、IO读写的Reactor线程组NioEventLoopGroup
EventLoopGroup group = new NioEventLoopGroup();
3、通过Bootstrap的ChannelFactory和用户指定的Channel类型创建用于客户端连接的NioSocketChannel
b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
此处的NioSocketChannel类似于Java NIO提供的SocketChannel。
4、创建默认的channel Handler pipeline
b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new HelloClientHandler()); } });
用于调度和执行网络事件。
5、异步发起TCP连接
// 发起异步连接操作 ChannelFuture f = b.connect(host, port).sync();
SocketChannel执行connect()操作后有以下三种结果:
- 连接成功,然会true;
- 暂时没有连接上,服务器端没有返回ACK应答,连接结果不确定,返回false。此种结果下,需要将NioSocketChannel中的selectionKey设置为OP_CONNECT,监听连接结果;
- 接连失败,直接抛出I/O异常
6、由多路复用器在I/O中轮询个Channel,处理连接结果
7、如果连接成功,设置Future结果,发送连接成功事件,触发ChannelPipeline执行
8、由ChannelPipeline调度执行系统和用户的ChannelHandler,执行业务逻辑
三、Netty客户端开发示例代码
需求:客户端端实现,连接服务器端,并向服务器端发送hello Netty。(注:本代码使用的netty是netty-all-5.0.0.Alpha1-sources.jar版本)
服务器端代码见Netty学习之服务器端创建
客户端代码:
import io.netty.bootstrap.Bootstrap; 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; public class HelloClient { public void connect(int port, String host) throws Exception { // 配置客户端NIO线程组 EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new HelloClientHandler()); } }); // 发起异步连接操作 ChannelFuture f = b.connect(host, port).sync(); // 等待客户端链路关闭 f.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } public static void main(String[] args) throws Exception { int port = 8080; new HelloClient().connect(port, "127.0.0.1"); } }
import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; public class HelloClientHandler extends ChannelHandlerAdapter { private final ByteBuf message; public HelloClientHandler() { byte[] req="hello Netty".getBytes(); message=Unpooled.buffer(req.length); message.writeBytes(req); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush(message); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
程序运行结果: