Commit 141d1b58 authored by wuyuhang's avatar wuyuhang

更新网卡自动选择功能

parent 28909bda
......@@ -10,33 +10,33 @@ import org.springframework.stereotype.Component;
public class AppProperties {
/** 监控软件信源地址 SID(十六进制,如 11651101) */
// private String sid = "11651101";
//
// /** 数传软件信宿地址 DID(十六进制,如 26000004) */
// private String did = "26000004";
//
// /** 命令发送目标组播地址 */
// private String sendHost = "238.1.0.103";
//
// /** 命令发送目标端口 */
// private int sendPort = 8889;
//
// /** 上报接收监听组播组 */
// private String recvGroup = "238.1.0.3";
//
// /** 上报接收监听端口 */
// private int recvPort = 8888;
private String sid = "11651101";
//** 数传软件信宿地址 DID(十六进制,如 26000004) *//*
private String did = "26000004";
//** 命令发送目标组播地址 *//*
private String sendHost = "238.1.0.103";
//** 命令发送目标端口 *//*
private int sendPort = 8889;
//** 上报接收监听组播组 *//*
private String recvGroup = "238.1.0.3";
//** 上报接收监听端口 *//*
private int recvPort = 8888;
// 244
private String sid = "26125010";
/* private String sid = "26125010";
private String did = "26045002";
private String sendHost = "232.18.185.21";
private int sendPort = 8888;
private String recvGroup = "232.18.185.11";
private int recvPort = 8888;
private int recvPort = 8888;*/
/** 网络接口名称,留空则自动选择第一个支持组播的接口 ens192*/
private String networkInterface = "wlan1";
/** 网络接口名称,留空则按系统组播路由自动选择;也可手动指定。 */
private String networkInterface = "";
/** 状态查询间隔(秒),0=不自动查询 */
private int queryInterval = 10;
......
package com.monitor.d2d.netty;
import com.monitor.d2d.config.AppProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.*;
import java.util.*;
/** 收发共用一次选择结果,避免分别枚举时选到不同接口。 */
@Slf4j
@Component
public class NetworkInterfaceSelector {
@Autowired
private AppProperties props;
private NetworkInterface selected;
public synchronized NetworkInterface resolve() throws IOException {
if (selected != null) return selected;
String name = props.getNetworkInterface();
if (name != null && !name.trim().isEmpty()) {
NetworkInterface configured = NetworkInterface.getByName(name.trim());
if (usable(configured)) return remember(configured, "手动配置");
log.warn("配置的网络接口 [{}] 不存在或不支持已启用的 IPv4 组播,尝试自动选择", name);
}
// UDP connect 只查询系统路由,不发送数据包。
try (DatagramSocket socket = new DatagramSocket(null)) {
socket.bind(new InetSocketAddress("0.0.0.0", 0));
socket.connect(InetAddress.getByName(props.getSendHost()), props.getSendPort());
NetworkInterface routed = NetworkInterface.getByInetAddress(socket.getLocalAddress());
if (usable(routed) && !routed.isPointToPoint()) return remember(routed, "系统目标路由");
} catch (IOException ex) {
log.warn("无法按目标路由选择网卡,尝试枚举可用接口: {}", ex.getMessage());
}
List<NetworkInterface> candidates = new ArrayList<>();
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces != null && interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
if (usable(ni) && !ni.isPointToPoint()) candidates.add(ni);
}
candidates.sort(Comparator.comparing(NetworkInterface::isVirtual)
.thenComparingInt(NetworkInterface::getIndex));
if (candidates.isEmpty()) throw new SocketException("未找到已启用且支持 IPv4 组播的非回环网卡");
if (candidates.size() > 1) log.warn("存在多个可用网卡且无法确定目标路由,采用候选列表首项: {}", candidates);
return remember(candidates.get(0), "可用接口回退");
}
private boolean usable(NetworkInterface ni) throws SocketException {
if (ni == null || !ni.isUp() || ni.isLoopback() || !ni.supportsMulticast()) return false;
return Collections.list(ni.getInetAddresses()).stream().anyMatch(a -> a instanceof Inet4Address);
}
private NetworkInterface remember(NetworkInterface ni, String reason) {
selected = ni;
log.info("组播网络接口: {} ({}),地址: {},选择依据: {}", ni.getName(),
ni.getDisplayName(), Collections.list(ni.getInetAddresses()), reason);
return ni;
}
}
......@@ -14,7 +14,6 @@ import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.net.*;
import java.util.Enumeration;
/**
* UDP 组播接收端
......@@ -30,12 +29,15 @@ public class UdpReceiver {
@Autowired
private ReportInboundHandler reportHandler;
@Autowired
private NetworkInterfaceSelector interfaceSelector;
private NioEventLoopGroup group;
private Channel channel;
@PostConstruct
public void start() throws Exception {
NetworkInterface ni = resolveNetworkInterface();
NetworkInterface ni = interfaceSelector.resolve();
InetAddress groupAddr = InetAddress.getByName(props.getRecvGroup());
group = new NioEventLoopGroup(1);
......@@ -56,34 +58,6 @@ public class UdpReceiver {
props.getRecvGroup(), props.getRecvPort(), ni.getName());
}
/**
* 自动或按配置选择网络接口
*/
private NetworkInterface resolveNetworkInterface() throws SocketException {
String ifName = props.getNetworkInterface();
if (ifName != null && !ifName.isEmpty()) {
NetworkInterface ni = NetworkInterface.getByName(ifName);
if (ni != null) return ni;
log.warn("配置的网络接口 [{}] 不存在,尝试自动选择", ifName);
}
// 自动选择:第一个支持组播且已启动的非回环接口
Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
while (nis.hasMoreElements()) {
NetworkInterface ni = nis.nextElement();
if (ni.isUp() && ni.supportsMulticast() && !ni.isLoopback()) {
log.info("自动选择网络接口: {}", ni.getName());
return ni;
}
}
// 回退到回环(仅用于本地测试)
NetworkInterface lo = NetworkInterface.getByName("lo");
if (lo != null) {
log.warn("未找到可用组播接口,回退到回环接口(仅限本地测试)");
return lo;
}
throw new IllegalStateException("无法找到可用的网络接口");
}
@PreDestroy
public void stop() {
if (channel != null) channel.close();
......
......@@ -15,7 +15,7 @@ import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import io.netty.channel.socket.InternetProtocolFamily;
/**
* UDP 组播发送端
......@@ -28,6 +28,9 @@ public class UdpSender {
@Autowired
private AppProperties props;
@Autowired
private NetworkInterfaceSelector interfaceSelector;
private NioEventLoopGroup group;
private Channel channel;
private InetSocketAddress targetAddress;
......@@ -35,11 +38,12 @@ public class UdpSender {
@PostConstruct
public void start() throws Exception {
targetAddress = new InetSocketAddress(props.getSendHost(), props.getSendPort());
java.net.NetworkInterface ni = interfaceSelector.resolve();
group = new NioEventLoopGroup(1);
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioDatagramChannel.class)
.option(ChannelOption.IP_MULTICAST_IF, NetworkInterface.getByName(props.getNetworkInterface()))
.channelFactory(() -> new NioDatagramChannel(InternetProtocolFamily.IPv4))
.option(ChannelOption.IP_MULTICAST_IF, ni)
.option(ChannelOption.SO_REUSEADDR, true)
.handler(new SimpleChannelInboundHandler<Object>() {
@Override
......
......@@ -108,23 +108,23 @@ public class ApiController {
private int num = 0;
// ──定时器──────────────────────────────────────────────────────────
@Scheduled(cron = "1 */30 * * * *")
public void autoSend5422() {
String localIP = "127.0.0.1";
String remoteIP = "127.0.0.1";
int concurrency = 4;
try {
for (int i = 0; i < concurrency; i++) {
int count = num * concurrency + i;
ObjectMapper mapper = new ObjectMapper();
String json = "{\"taskControl\":2,\"primaryFlag\":15,\"planNo\":\""+String.format("%09d", count)+"\",\"terminalStation\":\"TEST44621\",\"relayTaskCode\":16671,\"relayAntenna\":15,\"spacecraft\":\"TEST01\",\"orbitNo\":\""+String.format("%05d", count)+"\",\"transferNo\":1,\"captureStartTime\":\"20260701154300\",\"mode\":1,\"terminalAppProtocol\":5,\"terminalTransProtocol\":144,\"linkId\":\"LINK0\",\"initialRecvFrames\":0,\"initialRecvBytes\":0,\"terminalLinks\":[{\"primaryFlag\":15,\"routePlane\":0,\"terminalIp\":\""+remoteIP+"\",\"terminalPort\":27586,\"dataTransIp\":\""+localIP+"\",\"dataTransPort\":1001"+i+"}],\"outerEndpoints\":[{\"centerId\":\"TESTZX\",\"appProtocol\":0,\"initialSendFrames\":0,\"initialSendBytes\":0,\"transportProtocol\":144,\"links\":[{\"dataTransIp\":\""+localIP+"\",\"dataTransPort\":444"+i+",\"outerIp\":\""+remoteIP+"\",\"outerPort\":24584}]}]}";
Map<String, Object> parm = mapper.readValue(json, Map.class);
sendCommand("5422",parm);
}
log.info("执行次数: {}", num++);
} catch (IOException e) {
e.printStackTrace();
}
}
// @Scheduled(cron = "1 */1 * * * *")
// public void autoSend5422() {
// String localIP = "127.0.0.1";
// String remoteIP = "127.0.0.1";
// int concurrency = 1;
// try {
// for (int i = 0; i < concurrency; i++) {
// int count = num * concurrency + i;
// ObjectMapper mapper = new ObjectMapper();
// String json = "{\"taskControl\":2,\"primaryFlag\":15,\"planNo\":\""+String.format("%09d", count)+"\",\"terminalStation\":\"TEST44621\",\"relayTaskCode\":16671,\"relayAntenna\":15,\"spacecraft\":\"TEST01\",\"orbitNo\":\""+String.format("%05d", count)+"\",\"transferNo\":1,\"captureStartTime\":\"20260701154300\",\"mode\":1,\"terminalAppProtocol\":5,\"terminalTransProtocol\":144,\"linkId\":\"LINK0\",\"initialRecvFrames\":0,\"initialRecvBytes\":0,\"terminalLinks\":[{\"primaryFlag\":15,\"routePlane\":0,\"terminalIp\":\""+remoteIP+"\",\"terminalPort\":27586,\"dataTransIp\":\""+localIP+"\",\"dataTransPort\":1001"+i+"}],\"outerEndpoints\":[{\"centerId\":\"TESTZX\",\"appProtocol\":0,\"initialSendFrames\":0,\"initialSendBytes\":0,\"transportProtocol\":144,\"links\":[{\"dataTransIp\":\""+localIP+"\",\"dataTransPort\":444"+i+",\"outerIp\":\""+remoteIP+"\",\"outerPort\":24584}]}]}";
// Map<String, Object> parm = mapper.readValue(json, Map.class);
// sendCommand("5422",parm);
// }
// log.info("执行次数: {}", num++);
// } catch (IOException e) {
// e.printStackTrace();
// }
//
// }
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment