案例使用Netty实现简单地服务端到客户端的数据发送和读取
Netty 是由 JBOSS 提供的一个 Java 开源框架,所以在使用得时候首先得导入Netty的maven坐标。
<dependency>
<groupId>io.nettygroupId>
<artifactId>netty-allartifactId>
<version>4.1.69.Finalversion>
dependency>
NettyServerDemo : 服务端Demo依赖 NettyServerHandlerDemo
NettyServerHandlerDemo:服务端的一个ChannelHandler的实现类,用来承接业务逻辑
public class NettyServerDemo {
/**
* 服务端Demo实现目标
* 1.创建BossGroup线程组,处理网络连接事件
* 2.创建workerGroup线程组 处理网络读写事件
* 3.创建服务端启动助手,serverBootStrap
* 4.设置服务端通道实现方式为NIO
* 5.设置服务端options
* 6.创建通道初始化对象
* 8.向pipeline中添加自定义业务初处理逻辑handler
* 9.启动服务端并绑定端口,将异步改为同步
* 10.关闭通道和连接池
*/
public static void main(String[] args) throws InterruptedException {
//1.创建BossGroup线程组,处理网络连接事件,默认线程数量与电脑处理器相关,处理器线程数*2
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
//2.创建workerGroup线程组 处理网络读写事件
EventLoopGroup wrokerGroup = new NioEventLoopGroup();
//3.创建服务端启动助手,serverBootStrap
ServerBootstrap serverBootstrap = new ServerBootstrap();
//配置线程组
serverBootstrap.group(bossGroup, wrokerGroup)
.channel(NioServerSocketChannel.class)//配置Channel的实现,这里使用NIO的TCP服务端Channel
.option(ChannelOption.SO_BACKLOG, 128)//对于阻塞的连接队列大小的配置
.childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)//开启keep-alive 会根据操作系统的设定保持长连接并检测连接可用性
.childHandler(new ChannelInitializer<SocketChannel>() {
//通过一个特殊的ChannelInboundHandler 来初始化注册到EventLoop的Channel
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new NettyServerHandlerDemo());
}
});
//启动服务并绑定端口,并且将异步改成同步
ChannelFuture channelFuture = serverBootstrap.bind(9999).sync();
System.out.println("服务器启动成功。。。");
//关闭通道(停止接受新的连接)监听通道关闭的状态,这里改为同步后通道关闭才会返回
channelFuture.channel().closeFuture().sync();
bossGroup.shutdownGracefully();
wrokerGroup.shutdownGracefully();
}
}
/**
* 自定义处理Handler
*/
public class NettyServerHandlerDemo implements ChannelInboundHandler {
/**
* 通道读取事件
*
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println("客户端发送过来的消息:" + byteBuf.toString(CharsetUtil.UTF_8));
}
/**
* 通道读取完毕事件
*
* @param ctx
* @throws Exception
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("你好.我是Netty服务端",
CharsetUtil.UTF_8));//消息出站
}
/**
* 通道异常事件
*
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
}
@Override
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
}
}
NettyClientDemo 客户端启动器
NettyClientHandlerDemo 承载客户端业务代码的 channelhandler
public class NettyClientDemo {
/**
* 1.创建线程组
* 2.设置线程组启动助手 bootstrap
* 3.设置客户端通道为NIO
* 4.创建通道初始化对象
* 5.向pipeline中添加自定义业务处理的handler
* 6.启动客户端,等待链接服务端,同时将异步改为同步
* 8.关闭通道和连接池
*/
public static void main(String[] args) throws InterruptedException {
//1.创建线程组
EventLoopGroup group=new NioEventLoopGroup();
//2.创建bootstrap
Bootstrap bootstrap=new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyClientHandlerDemo());
}
});
//启动客户端
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9999).sync();
//关闭通道和连接池
//监听通道关闭的状态事件
channelFuture.channel().closeFuture().sync();
group.shutdownGracefully();
}
}
public class NettyClientHandlerDemo implements ChannelInboundHandler {
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("Hello! 我是Netty客户端!", CharsetUtil.UTF_8));
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println("服务端发来消息:" + byteBuf.toString(CharsetUtil.UTF_8));
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
}
@Override
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
}
}
JAVA中原本的Future参考:异步Future模式
表示异步的执行结果, 可以通过它提供的方法来检测执行是否完成,ChannelFuture 是他的一个子接口. ChannelFuture 是一个接口 ,可以添加监听器,当监听的事件发生时,就会通知到监听器当 Future 对象刚刚创建时,处于非完成状态,调用者可以通过返回的 ChannelFuture 来获取操作执行的状态, 注册监听函数来执行完成后的操作。
ChannelFuture future = bootstrap.bind(9999);
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
System.out.println("端口绑定成功!");
} else {
System.out.println("端口绑定失败!");
}
}
});
ChannelFuture channelFuture = ctx.writeAndFlush(Unpooled.copiedBuffer("你好
呀,我是Netty客户端", CharsetUtil.UTF_8));
channelFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
System.out.println("数据发送成功.");
} else {
System.out.println("数据发送失败.");
}
}
});