服务端

public class Server {
private final int port;

public Server(int port) {
this.port = port;
}

public void run() {
//创建两个事件循环组,bossGroup只处理连接请求,workGroup处理客户端业务处理,交给bossGroup
//两个都是无线循环
//默认CPU核*2
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();

try {
//创建服务端启动对象,配置参数
ServerBootstrap bootstrap = new ServerBootstrap();

//用链式编程进行设置
bootstrap.group(bossGroup, workGroup)//设置两个线程组
.channel(NioServerSocketChannel.class)//使用NioSocketChannel作为服务器通道实现
.option(ChannelOption.SO_BACKLOG, 128)//设置线程队列个数
.childOption(ChannelOption.SO_KEEPALIVE, true)//设置保持活动连接状态
.childHandler(new ServerInitializer());//给workGroup的EventLoop对应的管道设置处理器

System.out.println("服务器启动");

//启动服务器,绑定端口,生成ChannelFuture对象
ChannelFuture future = bootstrap.bind(port).sync();

//给ChannelFuture注册监听器,监控关心的事件
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (future.isSuccess()) {
System.out.println("监听端口成功");
} else {
System.out.println("监听端口失败");
}
}
});

//对关闭通道进行监听(非阻塞监听,异步模型)
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}

public static void main(String[] args) throws InterruptedException {
new Server(666).run();
}
}

服务端Initializer类

public class ServerInitializer extends ChannelInitializer<SocketChannel> {
//给pipeline设置处理器
@Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline()
.addLast("decoder",new StringDecoder())//解码器
.addLast("encoder",new StringEncoder())//编码器
.addLast("ServerHandler", new ServerHandler());//添加自定义Handler
}
}

服务端Handler类

//SimpleChannelInboundHandler继承HandlerAdapter
// 客户端与服务端相互通讯数据封装成String
public class ServerHandler extends SimpleChannelInboundHandler<String> {
//定义一个Channel组,管理所有Channel
private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

//建立连接,触发此事件
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
//获取当前channel
Channel channel = ctx.channel();
//将加入信息推送给其他客户端。该方法会将channelGroup中所有channel遍历并发送
channelGroup.writeAndFlush(Unpooled.copiedBuffer(date.format(new Date()) + channel.remoteAddress() + " 加入聊天", CharsetUtil.UTF_8));
//将当前channel加入到channelGroup
channelGroup.add(channel);
}

//断开连接,触发此事件,将离开信息推送给在线客户
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
//将当前channel加入到channelGroup
Channel channel = ctx.channel();
//将加入信息推送给其他客户端。该方法会将channelGroup中所有channel遍历并发送
channelGroup.writeAndFlush(Unpooled.copiedBuffer(date.format(new Date()) + channel.remoteAddress() + " 离开聊天", CharsetUtil.UTF_8));
}

//当通道活动时,触发此事件
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println(date.format(new Date()) + " " + ctx.channel().remoteAddress() + " 上线了");
}

//当通道不活动,触发此事件
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println(date.format(new Date()) + " " + ctx.channel().remoteAddress() + " 离线了");
}

@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
//获取当前channel
Channel channel = ctx.channel();
//遍历channelGroup
channelGroup.forEach(ch -> {
//判断不是当前channel,转发消息
if (channel != ch) {
ch.writeAndFlush(Unpooled.copiedBuffer(date.format(new Date()) + channel.remoteAddress() + ":" + msg, CharsetUtil.UTF_8));
} else {
//ch.writeAndFlush(Unpooled.copiedBuffer(date.format(new Date()) + channel.remoteAddress() + ":" + msg, CharsetUtil.UTF_8));
}
});
}

//处理异常,关闭通道
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}

客户端

public class Client {
private final String host;
private final int port;

public Client(String host, int port) {
this.host = host;
this.port = port;
}

public void run() {
//客户端只需要一个事件循环组
EventLoopGroup group = new NioEventLoopGroup();

try {
//创建客户端启动对象,配置参数
Bootstrap bootstrap = new Bootstrap();

//用链式编程进行设置
bootstrap.group(group)//设置事件循环组
.channel(NioSocketChannel.class)//设置客户端通道实现
.handler(new ClientInitializer());//给workGroup的EventLoop对应的管道设置处理器

//启动客户端,绑定端口,连接服务端
ChannelFuture future = bootstrap.connect(host, port).sync();

//得到channel
Channel channel = future.channel();
System.out.println("----------" + channel.localAddress() + "---------");
//客户端需要输入信息,创建扫描器
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String msg = scanner.nextLine();
channel.writeAndFlush(Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));
}
//对关闭通道进行监听(非阻塞监听,异步模型)
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}

public static void main(String[] args) throws InterruptedException {
new Client("127.0.0.1", 666).run();
}
}

客户端Initializer类

public class ClientInitializer extends ChannelInitializer<SocketChannel> {
//给pipeline设置处理器
@Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline()
.addLast("decoder",new StringDecoder())//解码器
.addLast("encoder",new StringEncoder())//编码器
.addLast("ClientHandler", new ClientHandler());//添加自定义Handler
}
}

客户端Handler类

//SimpleChannelInboundHandler继承HandlerAdapter
// 客户端与服务端相互通讯数据封装成String
public class ClientHandler extends SimpleChannelInboundHandler<String> {
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println(msg);
}

//处理异常,关闭通道
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}