目标:Netty 开发一个网络应用程序,实现服务端和客户端之间的数据通信。
架构图:

- 导入依赖坐标
- 编写Netty服务端程序:配置线程组,配置自定义业务处理类,绑定端口号,然后启动Server,等待Client连接
- 编写服务端-业务处理类Handler:继承 ChannelInboundHandlerAdapter,并分别重写了三个方法
- 读取事件
- 读取完成事件
- 异常捕获事件
- 编写客户端程序:配置了线程组,配置了自定义的业务处理类,然后启动Client,连接Server。
- 编写客户端-业务处理类:继承 ChannelInboundHandlerAdapter ,并分别重写了2个方法
- 通道就绪事件
- 读取事件
1.导入依赖
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.8.Final</version>
</dependency>
</dependencies>
上述代码在 pom 文件中引入了 netty 的坐标
2.服务端程序Server
public class NettyServer {
public static void main(String[] args) throws Exception{
//1. 创建一个线程组:接收客户端连接
EventLoopGroup bossGroup =new NioEventLoopGroup();
//2. 创建一个线程组:处理网络操作
EventLoopGroup workerGroup =new NioEventLoopGroup();
//3. 创建服务器端启动助手来配置参数
ServerBootstrap serverBootstrap=new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup) //4.设置两个线程组
.channel(NioServerSocketChannel.class) //5.使用NioServerSocketChannel作为服务器端通道的实现
.option(ChannelOption.SO_BACKLOG, 128) //6.设置线程队列中等待连接的个数
.childOption(ChannelOption.SO_KEEPALIVE, true) //7.保持长连接
.childHandler(new ChannelInitializer<SocketChannel>() { //8. 创建一个通道初始化对象
public void initChannel(SocketChannel sc) { //9. 往Pipeline链中添加自定义的handler类
sc.pipeline().addLast(new NettyServerHandler());
}
});
System.out.println("......服务端 启动中 init port:9999 ......");
ChannelFuture cf=serverBootstrap.bind(9999).sync(); //10. 绑定端口 bind方法是异步的 sync方法是同步阻塞的
System.out.println("......服务端 启动成功 ......");
//11. 关闭通道,关闭线程组
cf.channel().closeFuture().sync(); //异步
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
上述代码编写了一个服务端程序,配置了线程组,配置了自定义业务处理类,绑定端口号,然后启动
Server,等待Client连接。
3.服务端-业务处理类ServerHandler
//服务器端的业务处理类
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
//读取数据事件,msg就是客戶端发过来的数据。
public void channelRead(ChannelHandlerContext ctx,Object msg){
//System.out.println("Server:"+ctx);
//用缓冲区接受数据
ByteBuf buf=(ByteBuf) msg;
//转成字符串
//必须编码和解码
// 字符-->字节
// 字节-->字符
System.out.println("client msg:"+buf.toString(CharsetUtil.UTF_8));
}
//数据读取完毕事件,读取完客户端数据后回复客户端
public void channelReadComplete(ChannelHandlerContext ctx){
//Unpooled.copiedBuffer获取到缓冲区
//第一个参数是向客户端传的字符串
ByteBuf byteBuf = Unpooled.copiedBuffer("收到收到!我是服务端!", CharsetUtil.UTF_8);
ctx.writeAndFlush(byteBuf);
}
//异常发生事件
public void exceptionCaught(ChannelHandlerContext ctx,Throwable t){
//异常时关闭ctx,ctx是相关信息的汇总,关闭它其它的也就关闭了。
ctx.close();
}
}
上述代码定义了一个服务端业务处理类,继承 ChannelInboundHandlerAdapter,并分别重写了三个方
法。
4.客户端程序Client
public class NettyClient {
public static void main(String[] args) throws Exception{
//1. 创建一个线程组
EventLoopGroup group=new NioEventLoopGroup();
//2. 创建客户端的启动助手,完成相关配置
Bootstrap b=new Bootstrap();
b.group(group) //3. 设置线程组
.channel(NioSocketChannel.class) //4. 设置客户端通道的实现类
.handler(new ChannelInitializer<SocketChannel>() { //5. 创建一个通道初始化对象
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new NettyClientHandler()); //6.往Pipeline链中添加自定义的handler
}
});
System.out.println("......客户端 准备就绪 msg发射......");
//7.启动客户端去连接服务器端 connect方法是异步的 sync方法是同步阻塞的
ChannelFuture cf=b.connect("127.0.0.1",9999).sync();
//8.关闭连接(异步非阻塞)
cf.channel().closeFuture().sync();
}
}
上述代码编写了一个Client程序,配置了线程组,配置了自定义的业务处理类,然后启动Client,连接
Server。
5.客户端-业务处理类ClientHandler
//客户端业务处理类
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
//通道就绪事件
public void channelActive(ChannelHandlerContext ctx){
//System.out.println("Client:"+ctx);
//编码和解码
ByteBuf byteBuf = Unpooled.copiedBuffer("你好!你好!我是客户端!服务器听到请回复!", CharsetUtil.UTF_8);
ctx.writeAndFlush(byteBuf);
}
//读取数据事件
public void channelRead(ChannelHandlerContext ctx,Object msg){
ByteBuf buf=(ByteBuf) msg;
//字节 --> 字符串
System.out.println("server msg:"+buf.toString(CharsetUtil.UTF_8));
}
}
上述代码自定义了一个Client业务处理类,继承 ChannelInboundHandlerAdapter ,并分别重写了2个
方法
6.启动测试
- 启动服务端:NettyServer,服务器等待客户端消息,控制台打印:

- 启动客户端:NettyClient,客户端连接服务端,收到来自服务端的信息,客户端控制台打印:

- 此时服务端与客户端控制台:

总结:这样就完成客户端与服务端之间的通信。下一步我们再做一个在线聊天室的案例。