簡述:
Springboot項目的web服務後臺,web服務運行在9100端口。
後臺使用netty實現了TCP服務,運行在8000端口。
啓動截圖如下:
pom依賴
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.25.Final</version>
</dependency>
netty服務代碼
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
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;
/**
* netty服務器,主要用於與客戶端通訊
*/
public class NettyServer {
private final int port; //監聽端口
public NettyServer(int port) {
this.port = port;
}
//編寫run方法,處理客戶端的請求
public void run() throws Exception {
//創建兩個線程組
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(); //8個NioEventLoop
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//獲取到pipeline
ChannelPipeline pipeline = ch.pipeline();
//向pipeline加入解碼器
pipeline.addLast("decoder", new StringDecoder());
//向pipeline加入編碼器
pipeline.addLast("encoder", new StringEncoder());
//加入自己的業務處理handler
pipeline.addLast(new NettyServerHandler());
}
});
System.out.println("netty服務器啓動成功(port:" + port + ")......");
ChannelFuture channelFuture = b.bind(port).sync();
//監聽關閉
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
netty業務處理handler
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class NettyServerHandler extends SimpleChannelInboundHandler<String> {
//定義一個channle 組,管理所有的channel
//GlobalEventExecutor.INSTANCE) 是全局的事件執行器,是一個單例
public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
/**
* 有客戶端與服務器發生連接時執行此方法
* 1.打印提示信息
* 2.將客戶端保存到 channelGroup 中
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
System.err.println("有新的客戶端與服務器發生連接。客戶端地址:" + channel.remoteAddress());
channelGroup.add(channel);
}
/**
* 當有客戶端與服務器斷開連接時執行此方法,此時會自動將此客戶端從 channelGroup 中移除
* 1.打印提示信息
*/
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
System.err.println("有客戶端與服務器斷開連接。客戶端地址:" + channel.remoteAddress());
}
/**
* 表示channel 處於活動狀態
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println(ctx.channel().remoteAddress() + " 處於活動狀態");
}
/**
* 表示channel 處於不活動狀態
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println(ctx.channel().remoteAddress() + " 處於不活動狀態");
}
/**
* 讀取到客戶端發來的數據數據
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
//獲取到當前channel
Channel channel = ctx.channel();
System.err.println("有客戶端發來的數據。地址:" + channel.remoteAddress() + " 內容:" + msg);
}
/**
* 處理異常
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
log.error("發生異常。異常信息:{}", cause.getMessage());
//關閉通道
ctx.close();
}
}
Springboot項目中啓動netty服務
啓動類修改:
測試結果截圖
服務啓動,及客戶端連接
客戶端給服務器發送數據
服務器給客戶端發送數據
服務器查看當前所有連接的客戶端
服務器獲取到所有客戶單的ip地址及端口號後,即可通過其給指定客戶端發送數據