Netty记录

本文详细介绍Netty框架的高性能原理、线程模型及其实现细节,通过具体代码示例展示如何使用Netty搭建服务器与客户端进行双向通信,包括长连接的维护与心跳机制。

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

  • Netty 系列之 Netty 高性能之道:https://www.infoq.cn/article/netty-high-performance
  • Netty 系列之 Netty 线程模型:https://www.infoq.cn/article/netty-threading-model
  • Netty 和 RPC 框架线程模型分析:https://www.infoq.cn/article/9Ib3hbKSgQaALj02-90y

  • Netty源码解析-概述篇:https://segmentfault.com/a/1190000018248509
  • 源码之下无秘密 ── 做最好的 Netty 源码分析教程
  • 新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析:https://www.jianshu.com/p/f16698aa8be2?utm_source=oschina-app

  • NIO框架入门(一):服务端基于Netty4的UDP双向通信Demo演示 [附件下载]:http://www.52im.net/forum.php?mod=viewthread&tid=367&highlight=netty

Netty简单使用

原文链接:Netty——简单创建服务器、客户端通讯:https://www.cnblogs.com/lemon-flm/p/7813484.html

类HelloWorldServer

import java.net.InetAddress;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
 * Netty4 服务端代码
 *
 */
public class HelloWorldServer {
    public static void main(String[] args) {
        
        // EventLoop 代替原来的 ChannelFactory
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap(); // 创建 一个netty 服务器
            
            // server端采用简洁的连写方式,client端才用分段普通写法。
            serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // 指定channel[通道]类型
            .childHandler(new ChannelInitializer < SocketChannel > () { // 指定Handler [操纵者]
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    // 以("\0")为结尾分割的 解码器
                    ch.pipeline().addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.nulDelimiter()));
                    // 字符串 解码 和 编码 默认的 StringDecoder 字符串形式输出
                    ch.pipeline().addLast("decoder", new StringDecoder());
                    ch.pipeline().addLast("encoder", new StringEncoder());
                    ch.pipeline().addLast(new HelloServerHandler()); // 添加自己的对 上传数据的处理
                }
            }).option(ChannelOption.SO_KEEPALIVE, true);
            ChannelFuture f = serverBootstrap.bind(8000).sync(); // 绑定 8000 端口
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            
        } finally {
            workerGroup.shutdownGracefully(); // 销毁 netty
            bossGroup.shutdownGracefully();
        }
    }
    /**
     * 自己对 处理数据
     *
     * @author flm
     * 2017年11月10日
     */
    private static class HelloServerHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            // 收到消息直接打印输出
            System.out.println(ctx.channel().remoteAddress() + " Say : " + msg);
            // 返回客户端消息 - 我已经接收到了你的消息
            ctx.writeAndFlush("server Received your message !");
        }
        /**
         *
                * 覆盖 channelActive 方法 在channel被启用的时候触发 (在建立连接的时候)
         *
         * channelActive 和 channelInActive 在后面的内容中讲述,这里先不做详细的描述
         */
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println("RamoteAddress : " + ctx.channel().remoteAddress() + " active !");
            ctx.writeAndFlush("Welcome to " + InetAddress.getLocalHost().getHostName() + " service!\n"); //回复
            super.channelActive(ctx);
        }
    }
}

类HelloWorldClient

import java.net.InetSocketAddress;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

/**
 * Netty4 客户端代码
 * 
 */
public class HelloWorldClient {

    public static void main(String args[]) {

        // Bootstrap,且构造函数变化很大,这里用无参构造。
        Bootstrap bootstrap = new Bootstrap();
        // 指定channel[通道]类型
        bootstrap.channel(NioSocketChannel.class);
        // 指定Handler [操纵者]
        bootstrap.handler(new ChannelInitializer<Channel>() {

            @Override
            protected void initChannel(Channel ch) throws Exception {
                ChannelPipeline pipeline = ch.pipeline();
                /*
                 * 这个地方的 必须和服务端对应上。否则无法正常解码和编码
                 * 
                 */
                pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.nulDelimiter()));
                pipeline.addLast("decoder", new StringDecoder());
                pipeline.addLast("encoder", new StringEncoder());

                // 客户端的逻辑,自己对数据处理
                pipeline.addLast(new HelloClientHandler());

            }
        });
        // 指定EventLoopGroup [事件 组]
        bootstrap.group(new NioEventLoopGroup());
        
        //设置TCP协议的属性
        bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
        bootstrap.option(ChannelOption.TCP_NODELAY, true);
        bootstrap.option(ChannelOption.SO_TIMEOUT, 5000);

        // 连接到本地的8000端口的服务端
        bootstrap.connect(new InetSocketAddress("192.168.50.177", 4545));

    }

    private static class HelloClientHandler extends ChannelInboundHandlerAdapter {
        /*
         * 监听 服务器 发送来的数据
         */
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

            if("0002".equals(msg.toString().substring(4, 8))){
                // 握手成功,主动发送心跳消息
            } else if ("9999".equals(msg.toString().substring(4, 8))) {
                String keepAlive = "00209999000         " + "\0";
                Thread.sleep(10000);
                ctx.writeAndFlush(keepAlive);
            } else if("0061".equals(msg.toString().substring(4, 8))){
                System.out.println("发送给所有客户端");
            }
            System.out.println("say:" + msg);
        }

        /*
         * 启动客户端 时触发
         */
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println("Client active ");
            String start = "00200001003         ";
            String subscribe02  = "002000600031  02    ";
            String subscribe03  = "002000600031  03    ";
            ctx.writeAndFlush(start + "\0");
            ctx.writeAndFlush(subscribe02 + "\0");
            ctx.writeAndFlush(subscribe03 + "\0");
            super.channelActive(ctx);
        }

        /*
         * 关闭 客户端 触发
         */
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            System.out.println("Client close ");
            super.channelInactive(ctx);
        }
    }
    
}

写一个长连接的client

类TorqueGunClient

import java.net.InetSocketAddress;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

@Component
public class TorqueGunClient {
    private static Logger logger = LoggerFactory.getLogger(TorqueGunClient.class);
    
    @Autowired
    TorqueGunConfig torqueGunConfig;
    
    public void start() {
        logger.debug("开始,扭力枪通信");
        // Bootstrap,且构造函数变化很大,这里用无参构造。
        Bootstrap bootstrap = new Bootstrap();
        // 指定channel[通道]类型
        bootstrap.channel(NioSocketChannel.class);
        // 指定Handler [操纵者]
        bootstrap.handler(new ChannelInitializer<Channel>() {

            @Override
            protected void initChannel(Channel ch) throws Exception {
                ChannelPipeline pipeline = ch.pipeline();

                /*
                 * 这个地方的 必须和服务端对应上。否则无法正常解码和编码
                 * 
                 */
                pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.nulDelimiter()));
                pipeline.addLast("decoder", new StringDecoder());
                pipeline.addLast("encoder", new StringEncoder());

                // 客户端的逻辑,自己对数据处理
                pipeline.addLast(new TorqueGunClientHandler());
            }
        });
        // 指定EventLoopGroup [事件组]
        bootstrap.group(new NioEventLoopGroup());

        // 连接到本地的8000端口的服务端
        bootstrap.connect(new InetSocketAddress(torqueGunConfig.getIp(), torqueGunConfig.getPort()));
    }
}

类TorqueGunClientHandler

import java.util.Map;
import java.util.Set;

import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.com.tcb.uavpcs.datacenter.comm.desktop.process.WorkplaceClientInvotor;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class TorqueGunClientHandler extends ChannelInboundHandlerAdapter {
    private static Logger logger = LoggerFactory.getLogger(TorqueGunClientHandler.class);
    private String keepAlive = "00209999000         " + "\0";
    private String start = "00200001003         " + "\0";
    private String subscribe02 = "002000600031  02    " + "\0";
    private String subscribe03 = "002000600031  03    " + "\0";

    /*
     * 监听 服务器 发送来的数据
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        logger.debug("say:" + msg);
        if ("0002".equals(msg.toString().substring(4, 8))) {
            // 握手成功,主动发送一次心跳消息
            ctx.writeAndFlush(keepAlive);
        } else if ("9999".equals(msg.toString().substring(4, 8))) {
            Thread.sleep(10000);
            ctx.writeAndFlush(keepAlive);
        } else if ("0061".equals(msg.toString().substring(4, 8))) {
            logger.debug(msg.toString());
            WorkplaceClientInvotor workplaceClientInvotor = new WorkplaceClientInvotor();
            Map<String, IoSession> map = WorkplaceClientInvotor.STATION_SESSIONS;
            Set<String> set = map.keySet();
            for (String key : set) {
                workplaceClientInvotor.reportTorqueData(msg.toString(), key);
            }
        }
    }

    /*
     * 启动客户端 时触发
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        logger.debug("Client active ");
        ctx.writeAndFlush(start);
        ctx.writeAndFlush(subscribe02);
        ctx.writeAndFlush(subscribe03);
        super.channelActive(ctx);
    }

    /*
     * 关闭 客户端 触发
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        logger.debug("Client close ");
        super.channelInactive(ctx);
    }
}

类TorqueGunConfig

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource(value = "classpath:define.properties")
@ConfigurationProperties(prefix = "torquegun.socketserver")
public class TorqueGunConfig {

    private String ip;
    private int port;

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }
}

转载于:https://www.cnblogs.com/ioufev/p/10715827.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值