第一步:引入maven netty-all jar包

<!--netty-socketio-->
<dependency>
	<groupId>io.netty</groupId>
	<artifactId>netty-all</artifactId>
	<version>4.1.36.Final</version>
</dependency>

第二步: 自定义解码器

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.extern.slf4j.Slf4j;

/**
 * 自定义解码器
 */
@Slf4j
public class DecoderHandler extends ByteToMessageDecoder {

    private static Map<ChannelHandlerContext, String> msgBufMap = new ConcurrentHashMap<>();

    private static Map<String,ChannelHandlerContext> channelMap = new ConcurrentHashMap<>();

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        byte[] data = new byte[in.readableBytes()];
        in.readBytes(data);
        log.info("接收到的消息:【{}】",data);
    }
 }

继承 ChannelInboundHandlerAdapter 实现连接相关方法

import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
@ChannelHandler.Sharable
public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    PooledByteBufAllocator pool = PooledByteBufAllocator.DEFAULT;

    private static Map<String,String> antIdMap = new ConcurrentHashMap<>();

    /**
     * 在与客户端的连接已经建立之后将被调用
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("netty客户端与服务端连接开始...客户端地址【{}】",ctx.channel().remoteAddress());
    }

    /**
     * 当从客户端接收到一个消息时被调用
     * msg 就是硬件传送过来的数据信息
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        log.info("当从客户端接收到一个消息时被调用...{}",msg.toString());
    }

    /**
     * 客户端与服务端断开连接时调用
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("netty客户端与服务端连接关闭...客户端地址【{}】",ctx.channel().remoteAddress());
    }

    /**
     * 在处理过程中引发异常时被调用
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
        System.out.println("异常信息:rn " + cause.getMessage());
    }
    /**
     * 服务端接收客户端发送过来的数据结束之后调用
     * @param ctx
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
    	ctx.writeAndFlush("我收到消息了!");
        ctx.flush();
    	System.out.println("服务端接收客户端发送过来的数据结束之后调用 ");
    }
 }

@ChannelHandler.Sharable 作用与多个客户端连接时使用 否则当第二个连接时会抛异常

第三步:创建配置类 配置socket端口

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * 读取YML中的服务配置
 *
 */
@Configuration
@ConfigurationProperties(prefix = ServerProperties.PREFIX)
@Data
public class ServerProperties {

    public static final String PREFIX = "netty.server";

    /**
     * 服务器端口
     */
    private Integer port;

}

yml配置:

netty:
  server:
    port: 4001

第四步:创建启动server

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.nio.charset.Charset;

/**
 * 启动 Server
 */
@Component
@Slf4j
public class NettySocketServer {

    @Autowired
    private NettyServerHandler nettyServerHandler;

    @Autowired
    private ServerProperties serverProperties;

    public static ServerSocketChannel serverSocketChannel;

    /**
     * 初始化
     */
    public void start() throws Exception {
        log.info("初始化 NettySocketServer  ...");
        // 连接处理group
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 事件处理group
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap bootstrap = new ServerBootstrap();//1.创建ServerBootStrap实例
        // 绑定处理group
        bootstrap.group(bossGroup, workerGroup)//2.设置并绑定Reactor线程池:EventLoopGroup,EventLoop就是处理所有注册到本线程的Selector上面的Channel
                .channel(NioServerSocketChannel.class)//3.设置并绑定服务端的channel
                // 保持连接数
                .option(ChannelOption.SO_BACKLOG, 1024)
                // 有数据立即发送
                .option(ChannelOption.TCP_NODELAY, true)
                // 保持连接
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                // 处理新连接
                .childHandler(new ChannelInitializer<SocketChannel>() {//设置了客户端连接socket属性。
                    @Override
                    protected void initChannel(SocketChannel sc) throws Exception {
                        // 增加任务处理
                        ChannelPipeline p = sc.pipeline();
                        p.addLast(new DecoderHandler(), // 自定义解码器
                                //默认的编码器
                                new StringEncoder(Charset.forName("utf-8")),
                                new StringDecoder(Charset.forName("utf-8")),
                                // 自定义的处理器
                                // new ServerHandler()
                                nettyServerHandler);
                    }
                });

        // 绑定端口,同步等待成功
        ChannelFuture future;
        try {
            log.info("netty服务器在[{}]端口启动监听",serverProperties.getPort());
            future = bootstrap.bind(serverProperties.getPort()).sync();//真正让netty跑起来的重点
            if (future.isSuccess()) {
                serverSocketChannel = (ServerSocketChannel) future.channel();
                log.info("netty服务开启成功");
            } else {
                log.info("netty服务开启失败");
            }
            // 等待服务监听端口关闭,就是由于这里会将线程阻塞,导致无法发送信息,所以我这里开了线程
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 优雅地退出,释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

第五步:配置启动类

import com.cloud.nettysocket.service.NettySocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;

/**
 * @author zhangbl
 * @version 1.0.0
 * @Description 消息通讯模块
 * @Date : 2022/5/25 14:17
 **/
@SpringCloudApplication
public class NettySocketApplication implements CommandLineRunner {

	@Autowired
	private NettySocketServer nettySocketServer;

	public static void main(String[] args) {
		SpringApplication.run(NettySocketApplication.class, args);
	}

	@Override
	public void run(String... args) throws Exception {
		nettySocketServer.start();
	}
}

启动项目 使用网络调试工具连接 127.0.0.1 4001 端口 注意!!! 这个端口不是项目端口 时配置的socket端口