netty 做服务端监听多个端口推送数据并区分接收每个端口响应回来的数据:
@Sharable:被注解的ChannelHandle对应的同意实例可以被加入到一个或者多个ChannelPipelines一次或者多次,而不存在竞争条件。
* 如果在创建服务端后 clien重连服务端 报错 is not a@Sharable hanler 解决方法: 往Handler类上加注解即可。
参考:https://blog.csdn.net/haoziwlh/article/details/77684938
笔记分了三种:
1.0监听单端口,并实现在网页上动态开启或关闭(给线程测试连接)
2.0监听多端口,由于站点较多、采集时间也在三秒内,加了闭锁无法保证效率
3.0使用广播模式,统一向连接上的端口发信息,接收并处理每个端口对应站点的值
1.0:
在网页上改端口,netty服务端只监听输入的端口。
在有客户端连接上时,就将与客户端连接的ChannelHandlerContext 存到缓存集合中,当在网页上输入要更改的端口时,
控制层收到传送过来的值后,遍历缓存集合,逐个关闭channel 与两个WordGroup 。
注意: channelHandlerContext.channel().close(); //关闭端口
channelHandlerContext.close()则是关闭当前的端口的管道,但是端口还在监听。
System.out.println("修改的端口:" + port);
Map channelMap = TCPServer.channelMap;
for (String key : channelMap.keySet()) {
ChannelHandlerContext channelHandlerContext = channelMap.get(key);
channelHandlerContext.channel().close(); //关闭
//也要关闭两个wordGroup !!! 这里没贴 关一个都没用
}
try {
new TCPServer().bind(Integer.parseInt(port));
return Result.Success("绑定端口"+port+"成功*****************");
} catch (Exception e) {
e.printStackTrace();
return new Result("绑定端口失败");
}
2.0:
开启多个服务端口,监听端口,当有客户端连接上时,定时向这些连接上的管道发送数据,并等待回应(一发一回)
思路1. 使用BootStrap 绑定多个端口,当有客户端连接就将连接的ChannelHandlerContext存到缓存集合
但是这样存在两个问题 1).1无法判断发送回来的信息是通过哪个端口发送的。
2).要在几秒内发送信息并等待回应,需要一一遍历缓存集合,逐个发送并等待回应。 而客户端有三十来个 一次读完大概要30秒左右。
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import org.springframework.stereotype.Component;
import java.net.InetSocketAddress;
/**
* 测试多端口监听服务端
* @author peng
*/
@Component
public class Server {
private ChannelFuture [] ChannelFutures = null;
private int end;
private int start;
public Server(){}
public Server(int start,int end){
this.start = start;
this.end = end;
}
public static void main(String[] args){
Server sever = new Server(20001,20032);
sever.start();
}
private void start() {
EventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup);
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.childOption(ChannelOption.SO_REUSEADDR,true);
serverBootstrap.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new CountHandler());
}
});
if (ChannelFutures == null){
ChannelFutures = new ChannelFuture[end - start +1];
}
//多端口绑定
for (int i = start;i <= end ;i++){
final int port = i;
ChannelFuture channelFuture = serverBootstrap.bind(port);
ChannelFutures[i-start] = channelFuture;
channelFuture.addListener(future -> {
if (future.isSuccess()){
System.out.println("start success");
}else{
System.out.println("start failed");
}
});
}
}
@ChannelHandler.Sharable
class CountHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
InetSocketAddress inteSocket = (InetSocketAddress) ctx.channel().remoteAddress();
System.out.println("端口 已有客户端连接***"+inteSocket.getPort());
super.channelActive(ctx);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
super.channelRead(ctx, msg);
System.out.println("接受到.."+msg);
}
}
}
3.0 使用广播模式:
ChannelGroup可以管理当前的channel,当有连接上时就将连接上的channel组添加到ChannelGroup里:
public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
/**
* 客户端连接上
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
FxmelsecApplication.channelGroup.add(ctx.channel()); // 将这个Channel添加到ChannelGroup
String s = analyLocalAddress(ctx.channel().localAddress());
Global.channelMap.put(s, ctx);
super.channelActive(ctx);
//连接上信息存数据库
RecordEntity re = new RecordEntity();
re.setState(1);
re.setContent("连接成功");
re.setSitesNum(s);
re.setDate(DateUtils.getDate());
writeSQLInConnectionOperation(re);
System.out.println(recordServiceImpl);
ctx.fireChannelActive();
}
连接断开:
/***
* 客户端离线
* 判断管道id 删除
* @param ctx
* @throws Exception
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Map channelMap = Global.channelMap;
for (String key : channelMap.keySet()
) {
if (channelMap.get(key) == ctx){
Global.channelMap.remove(key);
//将断开连接的写入数据库 在页面开一个框记录连接跟断开
RecordEntity re = new RecordEntity();
re.setState(0);
re.setContent("断开连接");
re.setSitesNum(key);
re.setDate(DateUtils.getDate());
super.channelInactive(ctx);
writeSQLInConnectionOperation(re);
return;
}
}
}
Server:
public void bind(int port) {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
// 服务器辅助启动类配置
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
//设置nio的双向通道
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ServerChannelInitializer())
// 设置tcp缓冲区
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(ChannelOption.SO_KEEPALIVE, true);
// 绑定端口 同步等待绑定成功
ChannelFuture f = b.bind(port);
f.addListener(future -> {
if (future.isSuccess()) {
System.out.printf("开启端口%s成功", port);
} else {
System.out.printf("开启端口%s失败", port);
}
});
}
/**
* 网络事件处理器
*/
private class ServerChannelInitializer extends ChannelInitializer {
@Override
protected void initChannel(SocketChannel ch) {
// 添加自定义协议的编解码工具
ch.pipeline().addLast("decoder", new StringDecoder());
ch.pipeline().addLast("encoder", new StringEncoder());
// 处理网络IO
ch.pipeline().addLast(new ServerHandler());
}
}