xiwenAndlejian / my-blog

Java基础学习练习题
1 stars 0 forks source link

Netty 学习笔记(一)EchoServer #15

Open xiwenAndlejian opened 5 years ago

xiwenAndlejian commented 5 years ago

Netty 学习笔记(一)EchoServer

本文内容:创建一个 EchoServer(回声服务),并使用 telnet 测试。

EchoServer:将接受的数据不做任何处理,直接返回给客户端

源码


public class EchoServerDemo {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(group)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<Channel>() {
                        @Override
                        protected void initChannel(Channel ch) throws Exception {
                            ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                                @Override
                                public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                    // 将从客户端接收到的消息回写给客户端
                                    ctx.writeAndFlush(msg);
                                }
                            });
                        }
                    });
            // 绑定(侦听)8000端口
            ChannelFuture future = bootstrap.bind(8000).sync();
            future.channel().closeFuture().sync();
        } finally {
            // 释放资源
            group.shutdownGracefully();
        }
    }
}

进行测试

1. 启动服务端

启动结果 image

2. 使用 telnet 连接

终端中执行命令telnet 127.0.0.1 8000 如下所示表示已连接上服务端:

$telnet 127.0.0.1 8000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

此时输入内容,并按下回车发送消息给服务端:

hello # telnet 发送的消息
hello # server 回复的消息
echo  # telnet 发送的消息
echo  # server 回复的消息

注:也可以使用多个 telnet 进程连接客户端,测试消息回传是单播还是广播。

源码拆分

这大概是最简单的服务端代码了。

业务代码

服务端主要功能:服务端不对上传的数据进行任何处理,直接将上传数据回写给客户端。 这段主要功能对应的代码:


new ChannelInboundHandlerAdapter(){
    @Override
    public void channelRead(ChannelHandlerContext ctx,Object msg)throws Exception{
        // 将从客户端接受到的消息回写给客户端
        ctx.writeAndFlush(msg);
    }
}

channelRead :将在 channel 有数据可读时触发。 ctx.writeAndFlush():将消息写入并且立即刷新缓存,此处类似于 JDK 中的 I/O 流,消息并不会立即发送,而是存放在缓存区,刷新后才立即写入。 这段匿名内部类实际是创建一个处理回写业务的 ChannelHandler,当有数据可读时,立即回写该数据。

端口&资源的释放


NioEventLoopGroup group = new NioEventLoopGroup();
try {
    ...
    // 绑定(侦听)8000端口
    ChannelFuture future = bootstrap.bind(8000).sync();
    future.channel().closeFuture().sync();
} finally {
    // 释放资源
    group.shutdownGracefully();
}

暂时只需要明白这一段代码做了几件事:

  1. 创建了 EventLoopGroup
  2. 设置服务端绑定(侦听)端口
  3. 等待 Channel 关闭
  4. 释放资源

服务端引导器

源码中只剩最后这一段了:


ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(group)
        .channel(NioServerSocketChannel.class)
        .childHandler(...)

这一段大致的作用:

  1. 创建服务端启动器
  2. 设置上文创建的 EventLoopGroup
  3. 设置 Channel 类型 NioServerSocketChannel
  4. 设置业务处理链,此处只有一个业务:回写

EchoServer 总结

EchoServer 与 Client 关系图: image

目前 EchoServer 侦听 8000 端口。每与一个 Client (本文中使用的 telnet)建立连接,Server 便会创建一个 channel。并且与一个 EventLoop 绑定,而 EventLoop 本身绑定一个线程,在整个 channel 生命周期中,都会是在该 EventLoop/线程 中处理。 Server 中,与多个 Client 建立的 channel 本身是互不相干的,对其中一个 channel 写入消息并不会影响其他 channel。

注意:写入数据之后需要刷新缓存区才能发送消息。write() 和 flush(),或者直接调用简写方法 writeAndFlush()。