Netty 服务器在 6667 端口监听,客户端上线后发送消息给服务器,服务器接收并回复消息给客户端。总的来说就是一来一回。
public class NettyServer {
private static final int PORT = 6667;
public static void main(String[] args) {
//1.创建两个线程组 BossGroup、WorkerGroup
EventLoopGroup bossGroup = new NioEventLoopGroup(1); //只处理连接请求
EventLoopGroup workerGroup = new NioEventLoopGroup(); //处理与客户端的IO传输以及业务处理
try {
//2.创建服务器启动类
ServerBootstrap bootstrap = new ServerBootstrap();
//3.为服务器启动类并绑定Channel、父子线程组
bootstrap.group(bossGroup, workerGroup) //设置父子线程组
.channel(NioServerSocketChannel.class) //设置channel:客户端与服务端通信的nio双向通道
.option(ChannelOption.SO_BACKLOG, 128) //设置连接队列的长度(半连接队列+全连接队列之和)
.childOption(ChannelOption.SO_KEEPALIVE, true) //设置保持活动连接状态
//4.给子线程组的pipeline流水线设置处理器
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new NettyServerHandler());
}
});
//5.绑定端口号,启动服务器(同步方式)
ChannelFuture channelFuture = bootstrap.bind(PORT).sync();
//监听通道关闭
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
//6.优雅关闭 EventLoopGroup
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
/**
* 读取数据
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Channel channel = ctx.channel();
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println("收到客户端" + channel.remoteAddress() + "发送的数据是:" + byteBuf.toString(CharsetUtil.UTF_8));
}
/**
* 读取数据完毕后的处理
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//将数据写入到缓存,并刷新
ctx.writeAndFlush(Unpooled.copiedBuffer("hello,客户端", CharsetUtil.UTF_8));
}
/**
* 异常处理
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
public class NettyClient {
public static void main(String[] args) {
//创建一个事件循环组
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
//创建客户端启动类
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyClientHandler());
}
});
//启动客户端连接服务器
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6667).sync();
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
eventLoopGroup.shutdownGracefully();
}
}
}
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
/**
* 当通道就绪会触发该方法
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("hello server", CharsetUtil.UTF_8));
}
/**
* 当通道有读取事件时触发
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println("服务端" + ctx.channel().remoteAddress() +
"发来消息:" + byteBuf.toString(CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
编写一个 Netty 群聊系统,实现服务器端和客户端之间的非阻塞通讯,实现多人群聊。
public class GroupChatServer {
private static final int PORT = 6667;
public void run() {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO)) //日志处理器
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
//添加心跳检测处理器
pipeline.addLast(new IdleStateHandler(3, 5, 7, TimeUnit.SECONDS));
pipeline.addLast(new GroupChatServerHandler());
}
});
System.out.println("netty服务端启动...");
ChannelFuture channelFuture = serverBootstrap.bind(PORT).sync();
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
new GroupChatServer().run();
}
}
public class GroupChatServerHandler extends SimpleChannelInboundHandler<String> {
private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
//心跳检测:空闲事件触发
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
String eventType = null;
switch (event.state()) {
case READER_IDLE:
eventType = "读空闲";
break;
case WRITER_IDLE:
eventType = "写空闲";
break;
case ALL_IDLE:
eventType = "读写空闲";
break;
}
System.out.println(ctx.channel().remoteAddress() + eventType);
ctx.channel().close(); //如果发生空闲,则关闭通道(根据业务决定)
}
}
/**
* 连接建立时第一个执行
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
//将客户端加入群聊的消息推送给其他在线的客户端
channelGroup.writeAndFlush("[客户端] " + channel.remoteAddress() + " 加入聊天 —— " + sdf.format(new Date()));
channelGroup.add(channel);
}
/**
* 断开连接,并推送给其他客户端
*/
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
channelGroup.writeAndFlush("[客户端] " + channel.remoteAddress() + " 离开了");
//自动remove
}
/**
* 表示channel处于活动状态
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println(ctx.channel().remoteAddress() + " 上线了~");
}
/**
* 表示channel处于不活动状态
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println(ctx.channel().remoteAddress() + " 离线了~");
}
/**
* 读取数据
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
Channel channel = ctx.channel();
channelGroup.forEach(ch -> {
if (ch != channel) {
ch.writeAndFlush("[客户端] " + channel.remoteAddress() + " 发送了消息: " + msg);
} else {
ch.writeAndFlush("自己发送了: " + msg);
}
});
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
public class GroupChatClient {
private static final String HOST = "127.0.0.1";
private static final int PORT = 6667;
public void run() {
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast(new GroupChatClientHandler());
}
});
ChannelFuture channelFuture = bootstrap.connect(HOST, PORT).sync();
Channel channel = channelFuture.channel();
System.out.println("------" + channel.remoteAddress() + "------");
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String readLine;
while ((readLine = reader.readLine()) != null) {
channel.writeAndFlush(readLine);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
eventLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
new GroupChatClient().run();
}
}
public class GroupChatClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println(msg.trim());
}
}