Netty TCP 客户端/服务端 通用模板

本文介绍了一种基于Netty的TCP客户端和服务端通用模板。该模板分为单通道和多通道客户端,并提供了一种服务端模板。通过具体的代码示例展示了如何使用这些模板快速搭建TCP通信应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  个人参与过多个基于netty的项目, 小伙伴们对原生Netty组件的使用方式五花八门,风格迥异, 各种曲线救国,各种妥协, 各种凌乱, 并不是说这样不正确, 只是如果无法建立统一的使用范式, 将会浪费很多沟通,协调,学习, 百度的时间, 为此Nettyx提供了基础的TCP通信模板, 并分成客户端(client)和服务端(server), 意欲为netty的使用建立一种范式,有范式总比没有好, 说不定没有各位大佬手中的模板好,手动狗头保命

直接上依赖:

请从maven中央仓获取{lastest.version},最新版本号
<dependency>
    <groupId>io.github.fbbzl</groupId>
    <artifactId>nettyx</artifactId>
    <version>{lastest.version}</version>
</dependency>

一. 客户端模板

  Nettyx根据channel载量提供了两种tcp客户端,一种是单channel的客户端, 一种是多channel的客户端. 单channel顾名思义, 一个client组件中只保存了一个channel, 而多channel客户端通常是用来保存一组相同功能的channel.

1. SingleTcpChannelClientTemplate

  Nettyx提供的单通道客户端基类SingleTcpChannelClientTemplate , 提供了一些通用的抽象方法, 通过继承SingleTcpChannelClientTemplate 可以创建一个单Channel的TCP客户端, 以下展示了一个简单的单通道客户端实现


 @Slf4j
 // 此例中的DemoSingleTcpClient 仅作展示用, 实际使用请根据业务自行命名
public class DemoSingleTcpClient extends SingleTcpChannelClientTemplate {

   // 为了演示, 此处定义了一个定时器 , 用来在连接成功之后发送消息给对端
    static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
    
		// 保证初始化远端地址即可
    public DemoSingleTcpClient (InetSocketAddress address) {
        super(address);
    }
    	
    // channel初始化器, 各位根据自己业务自行创建 
   @Override
    protected ChannelInitializer<NioSocketChannel> channelInitializer() {
        return new TestChannelInitializer<>();
    }
    
    // 简单写个main进行测试,实际应作为spring组件使用,并使用spring的生命器钩子来初始化连接
    public static void main(String[] args) {
       // 先创建当前单通道客户端的实例, 并在构造函数中指定要server的地址
        DemoSingleTcpClient demoClient = new DemoSingleTcpClient (new InetSocketAddress("3.11.134.88", 9888));
		// 后续会详细介绍Nettyx对监听器的扩展, 这里先看下如何使用
        ChannelFutureListener listener = new ActionChannelFutureListener()
                .whenSuccess((ls, cf) -> {
                    executor.scheduleAtFixedRate(() -> {
                        byte[] msg = new byte[300];
                        Arrays.fill(msg, (byte) 1);
                        demoClient.writeAndFlush(Unpooled.wrappedBuffer(msg));
                    }, 2, 30, TimeUnit.MILLISECONDS);

                    System.err.println(cf.channel().localAddress() + ": ok");
                })
                .whenCancel((ls, cf) -> System.err.println("cancel"))
                // redo为ActionChannelFutureListener中的静态方法, 直接静态引入即可, 用来重试的方法
                .whenFailure(redo(testClient::connect, 2, SECONDS))
                .whenDone((ls, cf) -> System.err.println("done"));
		// 单通道客户端的connect方法返回channelfuture, 我们这里添加监听器即可
        demoClient.connect().addListener(listener);
    }
}

  以上便实现了一个单通道的客户端, 调用connect方法初始化连接后, 即可开始使用此客户端的write系列方法发送消息. 此客户端亦可成为spring的bean, 单通道客户端的实现是线程安全的, 当成类似于HttpTemplate, RestTemplate一样去使用即可

2. MultiTcpChannelClientTemplate

  讲完单通道, 接下来讲多通道客户端, 部分业务场景需要你保存一组功能相同的channel, 针对此场景Nettyx提供了MultiTcpChannelTemplate 泛型为key的类型, 此类所有方法都需要你带上相应的Key,以便内部检索出对应的channel来使用,来看一个简单的MultiTcpChannelClientTemplate实现

// 此例中的DemoMultiTcpClient 仅作展示, 实际使用请根据自己业务来进行命名
public class DemoMultiTcpClient extends MultiTcpChannelClientTemplate<String> {

    static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

    protected DemoMultiTcpClient(Map<String, InetSocketAddress> inetSocketAddressMap) {
        super(inetSocketAddressMap);
    }

   // 因为设置为同一组相同功能的channel, channel初始化器并没有根据key去进行特例化设置
    @Override
    protected ChannelInitializer<NioSocketChannel> channelInitializer() {
        return new TestChannelInitializer<>();
    }

   // 对channel进行设置 
    @Override
    protected void doChannelConfig(String key, SocketChannelConfig channelConfig) {
        // 进行设置, 可以根据key进行独有的设置 
        super.doChannelConfig(key, channelConfig);
    }

	// 也是简单写个main测试下
    public static void main(String[] args) {
        Map<String, InetSocketAddress> map = new HashMap<>(2);
		// 在构造DemoMultiTcpClient时需要你传入, key和远端地址的映射
        map.put("a", new InetSocketAddress(9888));
        map.put("b", new InetSocketAddress(9887));

        DemoMultiTcpClient demoMultiTcp = new DemoMultiTcpClient (map);
        ChannelFutureListener listener = new ActionChannelFutureListener()
                .whenSuccess((l, cf) -> {
                    executor.scheduleAtFixedRate(() -> {
                        byte[] msg = new byte[300];
                        Arrays.fill(msg, (byte) 1);
                        cf.channel().writeAndFlush(Unpooled.wrappedBuffer(msg));
                    }, 2, 30, TimeUnit.MILLISECONDS);

                    System.err.println(cf.channel().localAddress() + ": ok");
                })
                .whenCancel((l, cf) -> System.err.println("cancel"))
                .whenFailure(redo(cf -> demoMultiTcp.connect(channelKey(cf)), 2, SECONDS))
                .whenDone((l, cf) -> System.err.println("done"));

        demoMultiTcp.connectAll().values().forEach(c -> c.addListener(listener));
    }
}

  至此我们完成了单通道和多通道tcp客户端的创建


二. 服务端模板

  相较于客户端, 服务端因为自身的特性, 导致它并没有multiserver此类的封装, Nettyx暂时只提供了TcpServerTemplate一种模板

1. TcpServerTemplate

   如下展示了一个简单的TCP服务端, 它继承自Nettyx中的模板TcpServerTemplate 实现指定方法后即可使用

//  
public class DemoServer extends TcpServerTemplate {

    public DemoServer (SocketAddress bindAddress) {
        super(bindAddress);
    }

    public TestServer(int bindPort) {
        super(bindPort);
    }

    public static void main(String[] args) {
        DemoServer demoServer = new DemoServer (9888);
        ChannelFuture bindFuture = demoServer.bind();
        bindFuture.addListener(cf -> System.err.println("binding state:" + cf.isSuccess()));
        bindFuture.channel().closeFuture().addListener(cf -> {
            System.err.println("关闭了");
            testServer.shutdownGracefully();
        });

    }

    @Override
    protected ChannelInitializer<SocketChannel> childChannelInitializer() {
        return new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel channel) {
                InboundAdvice inboundAdvice = new InboundAdvice(channel)
                        .whenExceptionCaught((c, t) -> log.error("in error: [" + t + "]"));
                OutboundAdvice outboundAdvice = new OutboundAdvice(channel)
                        .whenExceptionCaught((c, t) -> log.error("out error: [" + t + "]"));

                channel.pipeline().addLast(
                        outboundAdvice
                        , new StartEndFlagFrameCodec(320, true, wrappedBuffer(new byte[]{(byte) 0x7e}))
                        , new ChannelInboundHandlerAdapter() {
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object in) throws Exception {
                                byte[] msg = new byte[300];
                                Arrays.fill(msg, (byte) 1);
                                ctx.channel().writeAndFlush(Unpooled.wrappedBuffer(msg));
                                super.channelRead(ctx, in);
                            }
                        }
                        , new UserCodec()

                        , inboundAdvice);
            }
        };
    }
}

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值