说明
Netty 的一个练习,使用 Netty 连通 服务端 和 客户端,进行基本的通信。
需求
Client
- 连接服务端后,给服务端发送消息
HelloServer
Server
- 客户端连接成功后,打印
连接成功
- 读取到客户端的消息后,打印到控制台,并回复消息
HelloClient
写法总结
- 对于
服务端和客户端的启动
代码基本不变,可能会根据需要修改一些配置参数 业务逻辑都写在各种 Handler 里
,根据规范,需要继承自已有的 Netty 类
实现
- 导包
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.35.Final</version>
</dependency>
- 创建
NettyServer
,用于启动服务端
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class NettyServer {
public static void main(String[] args) throws Exception {
// 创建 boss 线程组,处理 连接请求,个数代表有 几主,每个 主 都需要配置一个单独的监听端口
NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
// 创建 worker 线程组,处理 具体业务,个数代表有
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
// 创建 Server 启动器,配置必要参数:bossGroup, workerGroup, channel, handler
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
// 对workerGroup的SocketChannel设置 个性化业务Handler
socketChannel.pipeline().addLast(new NettyServerHandler());
}
});
System.out.println("Netty Server start ...");
ChannelFuture channelFuture = serverBootstrap.bind(9000).sync();
// 给 channelFuture 添加监听器,监听是否启动成功
/*channelFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (channelFuture.isSuccess()) {
System.out.println("启动成功");
} else {
System.out.println("启动失败");
}
}
});*/
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
- 创建
NettyServerHandler
,实现具体业务
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
/**
* 自定义Handler需要继承netty规定好的某个HandlerAdapter (规范)
* 业务逻辑:
* 连接成功后,打印 连接成功
* 收到客户端的消息时,打印,并回复消息
*
* @author liuhuan
*/
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
// 当客户端连接服务器完成就会触发该方法
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端建立连接成功");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端断开连接");
}
// 读取客户端发送的数据
@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 {
ByteBuf buf = Unpooled.copiedBuffer("HelloClient".getBytes(CharsetUtil.UTF_8));
ctx.writeAndFlush(buf);
}
// 处理异常, 一般是需要关闭通道
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
- 创建
NettyClient
,启动客户端
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
public class NettyClient {
public static void main(String[] args) throws InterruptedException {
NioEventLoopGroup group = new NioEventLoopGroup();
try {
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 NettyClientHandler());
}
});
System.out.println("Netty Client start ...");
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9000).sync();
channelFuture.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
}
- 创建
NettyClientHandler
,实现客户端业务逻辑
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
/**
* 自定义Handler需要继承netty规定好的某个HandlerAdapter (规范)
* 业务逻辑:连接成功后,给服务端发送一条消息
*
* @author liuhuan
*/
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
// 当客户端连接服务器完成就会触发该方法
@Override
public void channelActive(ChannelHandlerContext ctx) {
ByteBuf buf = Unpooled.copiedBuffer("HelloServer".getBytes(CharsetUtil.UTF_8));
ctx.writeAndFlush(buf);
}
//当通道有读取事件时会触发,即服务端发送数据给客户端
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf buf = (ByteBuf) msg;
System.out.println("收到服务端的消息:" + buf.toString(CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
- 分别执行
server
的main
方法,和client
的main
方法