使用的Nettyjar包:netty-all-4.1.27.Final.jar
项目结构:
服务端代码
package com.nio.netty.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
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 NettyNioServer {
public void bind(int port)throws Exception{
// 配置服务端的NIO线程组,NioEventLoopGroup是个线程组,包含了一组NIO线程
// 专门用于网路时间的处理,实际上它们就是Reactor线程组
NioEventLoopGroup groupParent = new NioEventLoopGroup();
NioEventLoopGroup groupChild = new NioEventLoopGroup();
try {
// 创建ServerBootstrap对象,它是Netty用于启动Nio服务的辅助类启动器
// 目的是降低服务端的开发复杂度
ServerBootstrap bootstrap = new ServerBootstrap();
// 将两个线程组当参数传递到ServerBootstrap中
// 设置创建的channel为NioServerSocketChannel
// 配置NioServerSocketChannel的TCP参数,此处的backlog设置为1024
// 绑定IO事件的处理类ChildChannelHandler,用于处理网络IO事件,例如记录日志,对消息进行编码等
bootstrap.group(groupParent,groupChild)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChildChannelHandler());
// 调用bind方法绑定端口,调用同步阻塞方法sync等待绑定操作成功
// 返回值主要用于异步操作的通知回调
ChannelFuture future = bootstrap.bind(port).sync();
// 等待服务端监听端口关闭
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
// 退出,释放系统资源
groupParent.shutdownGracefully();
groupChild.shutdownGracefully();
}
}
private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new NettyNioServerHandler());
}
}
public static void main(String[] args)throws Exception {
int port = 8089;
if (args != null && args.length > 0){
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
new NettyNioServer().bind(port);
}
}
package com.nio.netty.server;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.util.Date;
public class NettyNioServerHandler extends ChannelInboundHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 将msg转换成Netty的ByteBuf对象
ByteBuf buf = (ByteBuf) msg;
// buf.readableBytes()获取缓冲区可读的字节数
// 根据可读的字节数创建新的数组
byte[] req = new byte[buf.readableBytes()];
// 将缓冲区的字节数组复制到新建的字节byte数组
buf.readBytes(req);
// 对这个字节数组进编码
String body = new String(req, "utf-8");
System.out.println("服务端收到客户端发来的的消息是: " + body);
// 判断客户端发来的消息和服务端预设值的消息是否相同
// 如果相同就返回给客户端当前的时间
String str = "NOW TIME";
String nowTime = "NOTE RIGHT";
if (str.trim().equals(body.trim())){
nowTime = new Date().toString();
}
ByteBuf resp = Unpooled.copiedBuffer(nowTime.getBytes());
// 异步发送应答消息给客户端
ctx.write(resp);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
// 将消息发送队列中的消息写入到socketChannel中发送给对方
/**
* 为了防止频繁的唤醒selector进行消息发送,Netty的write方法并不直接将消息写入socketChannel中
* 调用write方法只是把待发送的消息放到发送缓冲区数组,
* 在通过调用flush方法,将缓冲区中的消息全部写到socketChannel中中
*/
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// 当发生异常的时候,关闭ChannelHandlerContext,释放和ChannelHandlerContext相关联的句柄等资源
ctx.close();
}
}
客户端代码
package com.nio.netty.client;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
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.NioSocketChannel;
public class NettyNioClient {
public void connect(int port, String host)throws Exception{
// 配置服务端的NIO线程组,NioEventLoopGroup是个线程组,包含了一组NIO线程
// 专门用于网路时间的处理,实际上它们就是Reactor线程组
NioEventLoopGroup group = new NioEventLoopGroup();
try {
// 创建ServerBootstrap对象,它是Netty用于启动Nio服务的辅助类启动器
// 目的是降低服务端的开发复杂度
Bootstrap bootstrap = new Bootstrap();
// 于服务端不同channel(NioSocketChannel.class)
/**
* handler,创建匿名内部类,实现initChannel方法,
* 作用是当创建NioSocketChannel成功之后
* 在进行初始化时,将它的channelHandler设置到ChannelPipeline中,
* 用于处理网络IO事件
*/
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new NettyClientHandler());
}
});
// 发起异步连接操作,调用同步阻塞方法等待连接成功
ChannelFuture sync = bootstrap.connect(host, port).sync();
// 等待客户端链路关闭
sync.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
// 退出,释放资源
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception{
int port = 8089;
if (args != null && args.length > 0){
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
new NettyNioClient().connect(port,"localhost");
}
}
package com.nio.netty.client;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.util.logging.Logger;
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
/**
* 日志
*/
private static final Logger logger = Logger.getLogger(NettyClientHandler.class.getName());
private final ByteBuf firstMessage;
public NettyClientHandler() {
byte[] req = "NOW TIME".getBytes();
firstMessage = Unpooled.buffer(req.length);
firstMessage.writeBytes(req);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// 调用ChannelHandlerContext的writeAndFlush方法将请求消息发送给服务端
ctx.writeAndFlush(firstMessage);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
// buf.readableBytes()获取缓冲区可读的字节数
// 根据可读的字节数创建新的数组
byte[] req = new byte[buf.readableBytes()];
// 将缓冲区的字节数组复制到新建的字节byte数组
buf.readBytes(req);
// 编码
String body = new String(req, "utf-8");
// 打印服务端返回的消息
System.out.println("客户端收到服务端返回的消息是: " + body);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// 释放资源
logger.warning("不期而遇的异常:" + cause.getMessage());
ctx.close();
}
}