目录
1、添加 Netty 依赖
2、服务器端
3、客户端
4、运行步骤
io.netty
netty-all
4.1.68.Final
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public void start() throws Exception {
// 1、创建bossGroup线程组,处理连接请求,线程数默认:2*处理器线程数
EventLoopGroup bossGroup = new NioEventLoopGroup();
// 2、创建workerGroup线程组,处理业务(读写事件),线程数默认:2*处理器线程数
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 3、创建服务端启动助手
ServerBootstrap b = new ServerBootstrap();
// 4、设置线程组
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // 5、设置服务端通道实现,使用NIO传输
.option(ChannelOption.SO_BACKLOG, 128) // 6、设置连接队列大小
.childOption(ChannelOption.SO_KEEPALIVE, true); // 7、保持长连接
.childHandler(new ChannelInitializer() {// 8、创建一个通道初始化对象
@Override
public void initChannel(SocketChannel ch) throws Exception {
// 9、向pipeline中添加自定义业务处理handler
ch.pipeline().addLast(new EchoServerHandler());
}
})
// 10、绑定端口并开始接收连接,同时将异步改为同步
ChannelFuture f = b.bind(port).sync();
System.out.println("EchoServer started and listen on " + f.channel().localAddress());
// 11、等待服务器socket关闭
f.channel().closeFuture().sync();
} finally {
// 12、关闭通道和连接池
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
new EchoServer(port).start();
}
}
解说:
1> ChannelFuture f = b.bind(port).sync();sync()的意义和作用:
阻塞当前线程:
.sync()
会阻塞调用它的线程(通常是主线程),直到异步操作(这里是bind(port)
)完成没有
.sync()
时,方法会立即返回,而绑定操作可能在后台继续执行同步等待结果:
确保在
.sync()
之后的代码执行时,绑定操作已经完成(成功或失败)例如后面的
System.out.println("聊天服务器已启动")
一定是在绑定完成后执行异常传播:
如果绑定失败(如端口被占用),
.sync()
会抛出异常没有
.sync()
时,异常会被静默处理,需要通过监听器捕获2> future.channel().closeFuture().sync();
future.channel().closeFuture()
获取一个特殊的ChannelFuture,它仅在服务器的Channel关闭时才会完成
.sync()
会阻塞当前线程(通常是main线程),直到这个关闭事件发生服务器会一直运行,直到有人主动触发关闭操作,例如:
调用
channel.close()
方法程序收到终止信号(如Ctrl+C)
通过管理接口发送关闭命令
发生不可恢复的错误
3>
finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); }
当
closeFuture().sync()
返回(即服务器Channel已关闭)后程序会进入 finally 块
优雅关闭两个线程组:
bossGroup
:接收新连接的线程组
workerGroup
:处理I/O操作的线程组
shutdownGracefully()
会:
停止接收新连接
等待现有任务完成
释放所有关联资源
服务器端处理器:
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
/**
* 通道读取事件
*
* @param ctx 通道上下文对象
* @param msg 消息
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf in = (ByteBuf) msg;
System.out.println("Server received: " + in.toString(io.netty.util.CharsetUtil.UTF_8));
ctx.write(in); // 将接收到的消息回写给发送者,而不冲刷出站消息
}
/**
* 读取完毕事件
*
* @param ctx
* @throws Exception
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
//ctx.writeAndFlush(Unpooled.copiedBuffer("你好,我是Netty服务端.", CharsetUtil.UTF_8));
ctx.flush(); // 将未决消息冲刷到远程节点,并关闭该Channel
}
/**
* 异常发生事件
*
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close(); // 关闭该Channel
}
}
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
public class EchoClient {
private final String host;
private final int port;
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws Exception {
// 1、创建线程组
EventLoopGroup group = new NioEventLoopGroup();
try {
// 2、创建客户端启动助手
Bootstrap b = new Bootstrap();
// 3、设置线程组
b.group(group)
.channel(NioSocketChannel.class) //4、设置服务端通道实现为NIO
.handler(new ChannelInitializer() { //5、创建一个通道初始化对象
@Override
public void initChannel(SocketChannel ch) throws Exception {
//6、向pipeline中添加自定义业务处理handler
ch.pipeline().addLast(new EchoClientHandler());
}
});
// 7、连接到服务器,将异步改为同步
ChannelFuture f = b.connect(host, port).sync();
System.out.println("Connected to server");
// 8、发送消息
String message = "Hello, Netty!";
ByteBuf buf = Unpooled.copiedBuffer(message.getBytes());
f.channel().writeAndFlush(buf);
// 9、等待连接关闭
f.channel().closeFuture().sync();
} finally {
// 10、关闭连接池
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new EchoClient("localhost", 8080).start();
}
}
客户端处理器:
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class EchoClientHandler extends ChannelInboundHandlerAdapter {
/**
* 通道就绪事件
*
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("你好呀,我是Netty客户端", CharsetUtil.UTF_8));
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf in = (ByteBuf) msg;
System.out.println("Client received: " + in.toString(io.netty.util.CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
首先启动 EchoServer
,它将监听 8080 端口
然后启动 EchoClient
,它将连接到服务器并发送一条消息
服务器会将接收到的消息回传给客户端
将在客户端控制台看到服务器返回的消息