在几乎所有Netty教程中都会出现的Netty程序, Echo程序
简单来说就是客户端发送任何内容, 服务端都会返回一样的内容
学习最简单的Echo程序, 主要是为了了解Netty使用的模板代码怎么写
1. 客户端实现
客户端的任务就是, 把消息发送给服务端, 并打印服务端返回的信息
客户端需要做下面几件事
- 建立连接
- 发送数据
- 接收数据
- 打印数据
https://gitee.com/spitman/learnnetty/blob/master/src/main/java/org/zyj/io/example/echo/EchoClient.java
1.1 实现一个发送消息的Handler
当连接建立之后, 立刻发送一条数据
class EchoClientHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelActive(ChannelHandlerContext ctx) {//建立连接之后, 立刻发送一条消息String sendMsg = "这是一条消息!";System.out.println(sendMsg);ByteBuf byteBuf = Unpooled.copiedBuffer(sendMsg, StandardCharsets.UTF_8);ctx.writeAndFlush(byteBuf);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {//读取服务端返回的信息ByteBuf buf = (ByteBuf) msg;if (buf.isReadable()){System.out.println(buf.toString(StandardCharsets.UTF_8));}}}
1.2 客户端启动Netty
public static void main(String[] args) throws InterruptedException {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ch.pipeline().addLast(new EchoClientHandler());}});ChannelFuture f = b.connect("127.0.0.1", 11223).sync();f.channel().closeFuture().sync();//阻塞主线程, 程序不会退出} finally {group.shutdownGracefully();}}
channel(NioSocketChannel.class): 这里指定使用NioChannel的方式建立连接(依赖JDK实现), 不同的SocketChannel实现类是不同的操作系统接口, 会提供不同的特性, 可使用的option参数也有所区别option(ChannelOption._TCP_NODELAY_, true): 不需要等待组装成大TCP包, 这样延迟会比较低ch.pipeline().addLast(new EchoClientHandler()): 添加ChannelHandler服务端实现
回音服务, 就是将客户端发送的信息, 原样返回
要实现这个功能, 服务端要做下面几件事建立连接
- 读取数据
- 发送数据
https://gitee.com/spitman/learnnetty/blob/master/src/main/java/org/zyj/io/example/echo/EchoServer.java
2.1 服务端回音Handler
class EchoServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf receivedMsg = (ByteBuf) msg;System.out.println(receivedMsg.toString(StandardCharsets.UTF_8));//打印一下第一个传进来的msg是什么//ChannelHandlerContext对象提供了多种操作, 是你可以出发各种IO事件和IO操作//ctx.write(msg); //直接将msg写入channel中, 不需要显示的释放ByteBuf, Netty会帮我们释放//一个TCP消息包, 只发送一点点数据, 就很浪费网络, 包头是固定长度//所以ctx.write(msg) 这一步仅仅只是写入缓存中, 并没有真正发送出去//想要立刻发送出去, 需要显示的调用ctx.flush()//ctx.flush();//将缓存中的数据发送出去//或者可以简单的使用一行代码来简洁的表示ctx.writeAndFlush(msg);}}
2.2 服务端启动Netty
public static void main(String[] args) throws Throwable {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<NioSocketChannel>() {@Overridepublic void initChannel(NioSocketChannel ch) {//每次建立连接的时候都会被调用ch.pipeline().addLast(new EchoServerHandler());}}).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);ChannelFuture f = b.bind(11223).sync();f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}
option(ChannelOption._SO_BACKLOG_, 128): 套接字listen函数中的backlog参数, 等待处理连接的队列大小; 如果backlog过小,就可能出现Accept的速度跟不上,A,B队列满了,就会导致客户端无法建立连接childOption(ChannelOption._SO_KEEPALIVE_, true): 当设置为true的时候,TCP会实现监控连接是否有效,当连接处于空闲状态的时候,超过了2个小时,本地的TCP实现会发送一个数据包给远程的 socket,如果远程没有发回响应,TCP会持续尝试11分钟,直到响应为止,如果在12分钟的时候还没响应,TCP尝试关闭socket连接。ch.pipeline().addLast(new EchoServerHandler()): 添加回音Handler3. 运行效果


