Commit 141d1b58 authored by wuyuhang's avatar wuyuhang

更新网卡自动选择功能

parent 28909bda
...@@ -10,33 +10,33 @@ import org.springframework.stereotype.Component; ...@@ -10,33 +10,33 @@ import org.springframework.stereotype.Component;
public class AppProperties { public class AppProperties {
/** 监控软件信源地址 SID(十六进制,如 11651101) */ /** 监控软件信源地址 SID(十六进制,如 11651101) */
// private String sid = "11651101"; private String sid = "11651101";
//
// /** 数传软件信宿地址 DID(十六进制,如 26000004) */ //** 数传软件信宿地址 DID(十六进制,如 26000004) *//*
// private String did = "26000004"; private String did = "26000004";
//
// /** 命令发送目标组播地址 */ //** 命令发送目标组播地址 *//*
// private String sendHost = "238.1.0.103"; private String sendHost = "238.1.0.103";
//
// /** 命令发送目标端口 */ //** 命令发送目标端口 *//*
// private int sendPort = 8889; private int sendPort = 8889;
//
// /** 上报接收监听组播组 */ //** 上报接收监听组播组 *//*
// private String recvGroup = "238.1.0.3"; private String recvGroup = "238.1.0.3";
//
// /** 上报接收监听端口 */ //** 上报接收监听端口 *//*
// private int recvPort = 8888; private int recvPort = 8888;
// 244 // 244
private String sid = "26125010"; /* private String sid = "26125010";
private String did = "26045002"; private String did = "26045002";
private String sendHost = "232.18.185.21"; private String sendHost = "232.18.185.21";
private int sendPort = 8888; private int sendPort = 8888;
private String recvGroup = "232.18.185.11"; private String recvGroup = "232.18.185.11";
private int recvPort = 8888; private int recvPort = 8888;*/
/** 网络接口名称,留空则自动选择第一个支持组播的接口 ens192*/ /** 网络接口名称,留空则按系统组播路由自动选择;也可手动指定。 */
private String networkInterface = "wlan1"; private String networkInterface = "";
/** 状态查询间隔(秒),0=不自动查询 */ /** 状态查询间隔(秒),0=不自动查询 */
private int queryInterval = 10; private int queryInterval = 10;
...@@ -50,4 +50,4 @@ public class AppProperties { ...@@ -50,4 +50,4 @@ public class AppProperties {
public long parseDid() { public long parseDid() {
return Long.parseUnsignedLong(did, 16); return Long.parseUnsignedLong(did, 16);
} }
} }
\ No newline at end of file
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; ...@@ -14,7 +14,6 @@ import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import java.net.*; import java.net.*;
import java.util.Enumeration;
/** /**
* UDP 组播接收端 * UDP 组播接收端
...@@ -30,12 +29,15 @@ public class UdpReceiver { ...@@ -30,12 +29,15 @@ public class UdpReceiver {
@Autowired @Autowired
private ReportInboundHandler reportHandler; private ReportInboundHandler reportHandler;
@Autowired
private NetworkInterfaceSelector interfaceSelector;
private NioEventLoopGroup group; private NioEventLoopGroup group;
private Channel channel; private Channel channel;
@PostConstruct @PostConstruct
public void start() throws Exception { public void start() throws Exception {
NetworkInterface ni = resolveNetworkInterface(); NetworkInterface ni = interfaceSelector.resolve();
InetAddress groupAddr = InetAddress.getByName(props.getRecvGroup()); InetAddress groupAddr = InetAddress.getByName(props.getRecvGroup());
group = new NioEventLoopGroup(1); group = new NioEventLoopGroup(1);
...@@ -56,34 +58,6 @@ public class UdpReceiver { ...@@ -56,34 +58,6 @@ public class UdpReceiver {
props.getRecvGroup(), props.getRecvPort(), ni.getName()); 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 @PreDestroy
public void stop() { public void stop() {
if (channel != null) channel.close(); if (channel != null) channel.close();
......
...@@ -15,7 +15,7 @@ import org.springframework.stereotype.Component; ...@@ -15,7 +15,7 @@ import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.NetworkInterface; import io.netty.channel.socket.InternetProtocolFamily;
/** /**
* UDP 组播发送端 * UDP 组播发送端
...@@ -28,6 +28,9 @@ public class UdpSender { ...@@ -28,6 +28,9 @@ public class UdpSender {
@Autowired @Autowired
private AppProperties props; private AppProperties props;
@Autowired
private NetworkInterfaceSelector interfaceSelector;
private NioEventLoopGroup group; private NioEventLoopGroup group;
private Channel channel; private Channel channel;
private InetSocketAddress targetAddress; private InetSocketAddress targetAddress;
...@@ -35,11 +38,12 @@ public class UdpSender { ...@@ -35,11 +38,12 @@ public class UdpSender {
@PostConstruct @PostConstruct
public void start() throws Exception { public void start() throws Exception {
targetAddress = new InetSocketAddress(props.getSendHost(), props.getSendPort()); targetAddress = new InetSocketAddress(props.getSendHost(), props.getSendPort());
java.net.NetworkInterface ni = interfaceSelector.resolve();
group = new NioEventLoopGroup(1); group = new NioEventLoopGroup(1);
Bootstrap b = new Bootstrap(); Bootstrap b = new Bootstrap();
b.group(group) b.group(group)
.channel(NioDatagramChannel.class) .channelFactory(() -> new NioDatagramChannel(InternetProtocolFamily.IPv4))
.option(ChannelOption.IP_MULTICAST_IF, NetworkInterface.getByName(props.getNetworkInterface())) .option(ChannelOption.IP_MULTICAST_IF, ni)
.option(ChannelOption.SO_REUSEADDR, true) .option(ChannelOption.SO_REUSEADDR, true)
.handler(new SimpleChannelInboundHandler<Object>() { .handler(new SimpleChannelInboundHandler<Object>() {
@Override @Override
......
...@@ -108,23 +108,23 @@ public class ApiController { ...@@ -108,23 +108,23 @@ public class ApiController {
private int num = 0; private int num = 0;
// ──定时器────────────────────────────────────────────────────────── // ──定时器──────────────────────────────────────────────────────────
@Scheduled(cron = "1 */30 * * * *") // @Scheduled(cron = "1 */1 * * * *")
public void autoSend5422() { // public void autoSend5422() {
String localIP = "127.0.0.1"; // String localIP = "127.0.0.1";
String remoteIP = "127.0.0.1"; // String remoteIP = "127.0.0.1";
int concurrency = 4; // int concurrency = 1;
try { // try {
for (int i = 0; i < concurrency; i++) { // for (int i = 0; i < concurrency; i++) {
int count = num * concurrency + i; // int count = num * concurrency + i;
ObjectMapper mapper = new ObjectMapper(); // 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}]}]}"; // 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); // Map<String, Object> parm = mapper.readValue(json, Map.class);
sendCommand("5422",parm); // sendCommand("5422",parm);
} // }
log.info("执行次数: {}", num++); // log.info("执行次数: {}", num++);
} catch (IOException e) { // } catch (IOException e) {
e.printStackTrace(); // 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