第一步:引入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端口