首页 > 其他分享 >Netty实践 -- echo

Netty实践 -- echo

时间:2023-10-19 21:23:46浏览次数:35  
标签:Netty -- void ctx echo new public 服务端 客户端

Netty实践

学习netty,可以从netty源码的 netty-example 模块开始。

netty-example 有一个例子 echo,非常适合入门学习。

这里稍微改造一下,用作示例学习。

引入依赖包:

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.29.Final</version>
        </dependency>

服务端

服务端收到客户端的消息后,会进行响应。

  • EchoServer:
/**
 * 服务端收到客户端的消息后,会进行响应。
 */
public final class EchoServer {
    /**
     * 端口
     */
    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

    public static void main(String[] args) throws Exception {
        // 配置 EventLoopGroup
        // 主从 Reactor 多线程模式,bossGroup是 主 Reactor,workerGroup是 从Reactor
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //初始化服务器的引导类 ServerBootstrap
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //指定 EventLoopGroup
            serverBootstrap.group(bossGroup, workerGroup)
                //指定 channel
             .channel(NioServerSocketChannel.class)
             .option(ChannelOption.SO_BACKLOG, 100)
                //指定 ChannelHandler,用于处理 channel
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     //ChannelPipeline,基于责任链模式,可以添加多个 ChannelHandler
                     ChannelPipeline channelPipeline = ch.pipeline();
                     //channelPipeline.addLast(new LoggingHandler(LogLevel.INFO));
                     //ChannelHandler,用于处理 channel,实现对接收的数据的处理,实现业务逻辑。
                     channelPipeline.addLast(new EchoServerHandler());
                 }
             });

            // 开启服务器,将服务器绑定到它要监听连接请求的端口上
            ChannelFuture channelFuture = serverBootstrap.bind(PORT).sync();

            // 等待直到服务器socket关闭
            channelFuture.channel().closeFuture().sync();
        } finally {
            //关闭所有 eventLoop,终止线程
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

  • EchoServerHandler:

ChannelHandler,用于处理 channel,实现业务逻辑。

/**
 * 服务端的 ChannelHandler.
 *
 * ChannelHandler,用于处理 channel,实现对接收的数据的处理,实现业务逻辑。
 * 继承 ChannelInboundHandlerAdapter,用来定义响应入站事件的方法。
 *
 */
@Sharable
@Slf4j
public class EchoServerHandler extends ChannelInboundHandlerAdapter {

    /**
     * channelRead() :读取 channel 传入的消息
     *
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ByteBuf buf= (ByteBuf) msg;
        log.info("客户端发来的消息:"+ buf.toString(CharsetUtil.UTF_8) +"\n");
    }

    /**
     * channelReadComplete() :表示当前 ChannelHandler 读取完毕.
     * 执行后会自动跳转到 ChannelPipeline 中的下一个 ChannelHandler.
     *
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        //向客户端返回数据,writeAndFlush() 也可以拆分成 write(msg) 和 flush()
        ctx.writeAndFlush(Unpooled.copiedBuffer("见到你,我也很高兴^_^",CharsetUtil.UTF_8));
    }

    /**
     * exceptionCaught(): 在读取操作期间,有异常抛出时会调用。
     *
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // 发生异常时关闭连接
        cause.printStackTrace();
        ctx.close();
    }
}

客户端

在以下示例中,客户端会向服务端发送消息。

  • EchoClient:
/**
 * 客户端。发送数据给服务端,并接收服务端的响应。
 *
 */
public final class EchoClient {

    static final String HOST = System.getProperty("host", "127.0.0.1");
    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
    static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));

    public static void main(String[] args) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
             .channel(NioSocketChannel.class)
             .option(ChannelOption.TCP_NODELAY, true)
             .handler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel socketChannel) throws Exception {
                     ChannelPipeline channelPipeline = socketChannel.pipeline();
                     //channelPipeline.addLast(new LoggingHandler(LogLevel.INFO));
                     //ChannelHandler,用于处理 channel,实现对接收的数据的处理,实现业务逻辑。
                     channelPipeline.addLast(new EchoClientHandler());
                 }
             });

            // 开启客户端,连接服务端的端口
            ChannelFuture channelFuture = bootstrap.connect(HOST, PORT).sync();

            channelFuture.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}

  • EchoClientHandler:
/**
 * 客户端 的 ChannelHandler.
 */
@Slf4j
public class EchoClientHandler extends ChannelInboundHandlerAdapter {


    /**
     * channelActive() 客户端跟服务器的连接建立之后将被调用.
     *
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        ByteBuf firstMessage = Unpooled.buffer(EchoClient.SIZE);
        byte[] bytes = "见到你很高兴^_^\n".getBytes(CharsetUtil.UTF_8);
        firstMessage.writeBytes(bytes);
        //向服务器发送数据
        ctx.writeAndFlush(firstMessage);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ByteBuf buf = (ByteBuf) msg;
        log.info("服务器发来的消息:" + buf.toString(CharsetUtil.UTF_8));
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

执行:

先启动服务端,然后再启动客户端。

服务端收到客户端的信息:

11:25:03.081 [nioEventLoopGroup-3-1] INFO com.example.demo.netty.echo.EchoServerHandler - 客户端发来的消息:见到你很高兴^_^

客户端收到服务端的回复:

11:25:03.091 [nioEventLoopGroup-2-1] INFO com.example.demo.netty.echo.EchoClientHandler - 服务器发来的消息:见到你,我也很高兴^_^

Netty常用类

以上的这个示例,用到了 Netty常用的类,

详情见:https://blog.csdn.net/sinat_32502451/article/details/133934402

参考资料:

https://zhuanlan.zhihu.com/p/415450910

标签:Netty,--,void,ctx,echo,new,public,服务端,客户端
From: https://www.cnblogs.com/expiator/p/17775670.html

相关文章

  • 类与对象
    实验任务1#include<iostream>#include<string>#include<vector>#include<array>template<typenameT>voidoutput1(constT&obj){for(autoi:obj)std::cout<<i<<",";std::cout<<"\b\......
  • L1-011记录
    关于这道题目,今天其实有自己的一些思路 L1-011A-B分数20全屏浏览题目切换布局作者 陈越单位 浙江大学本题要求你计算A−B。不过麻烦的是,A和B都是字符串——即从字符串A中把字符串B所包含的字符全删掉,剩下的字符组成的就是字符串A−B。输入......
  • GIL全局解释器锁、互斥锁、线程队列、进程池和线程池的使用、多线程爬取网页、协程理
    进程和线程的比较进程的开销比线程的开销大很多进程之间的数据是隔离的,但是,线程之间的数据不隔离多个进程之间的线程数据不共享----->还是让进程通信(IPC)------->进程下的线程也通信了---->队列GIL全局解释器锁(重要理论)Python在设计之初就考虑到要在主循环中,同时只有一......
  • 21.1 Python 使用PEfile分析PE文件
    PeFile模块是Python中一个强大的便携式第三方PE格式分析工具,用于解析和处理Windows可执行文件。该模块提供了一系列的API接口,使得用户可以通过Python脚本来读取和分析PE文件的结构,包括文件头、节表、导入表、导出表、资源表、重定位表等等。此外,PEfile模块还可以帮助用户进行一些......
  • 普通平衡树
    很久以前,我很抗拒使用平衡树。但是这现在是8级算法,我不得不学之。FHQ-Treap核心只有两个操作split和merge。考虑像Treap一样每个点打上rand,使得其满足是rnd的小根堆。然后考虑分裂。考虑每次对于值归类<=的归在\(lt\),其他归在\(rt\)。考虑每次归入\(lt\)一......
  • redis一主二从三哨兵
    节点规划准备三台虚拟机,使用CentOS-7-x86_64-DVD-2009.iso镜像节点IPmaster192.168.108.91slave1192.168.108.92slave2192.168.108.93手动安装redis1.解压tar-zxvfredis-6.0.8.tar.gz2.yum安装gcc(注意版本,5.3以上)gcc-v##查看......
  • 231019校内赛
    T1机器人题解傻逼题,但是有人\(90\)分一开始十分想直接暴力\(2^n\)判断每一步选不选求出所有可能性但是会发现它有制约关系有些步走了之后,有些就必须走了所以需要用一个数组记录当前位置走没走过,或者是不是障碍注意走没走过不能直接赋值\(1,0\)因为回溯时会直接将前......
  • Spring 拦截器和过滤器
    目录Filter使Spring管理Filter方式一:@Component+@Order方式二:通过JavaConfig配置方式三:@WebFilter+@ServletComponentScan对比应用场景InterceptorpreHandlepostHandlerafterCompletion应用场景在SpringMVC中,Interceprtor与Filter两者的应用场景好像差不多,最大的......
  • gcc对构造函数的调用生成
    identifierC++的前端对identifier做了扩展,在每个identifier中还包含了两个cxx_binding字段:namespace_bindings和bindings。当通过字符串找到一个identifier的时候,同时顺带获得了两个binding信息。/*Language-dependentcontentsofanidentifier.*/structGTY(())lang_id......
  • 解决:Exception: URL fetch failure on https://storage.googleapis.com/tensorflow/tf
    首次装载IMDB数据集时可能会出现的错误。解决方案:1、先将数据集单独下载下来:datasets/imdb.npz·黄健/keras-datasets-Gitee.com2、将其复制到 ~/.keras/dataset目录下:cpimdb.npz ~/.keras/dataset ......