Multicast na interface Wi‑Fi/cabo preferida para presença na LAN.
Evita anunciar heartbeat por interfaces Docker/WSL; TTL 1 e checklist de descoberta em dois PCs. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
6c1ce9a6ec
commit
39c1e504bb
3 files changed
+124
-11
No files matched your search
@@ -29,10 +29,18 @@ Executar o Pitiupi no WSL e peers no Windows (ou vice-versa) pode falhar em mult
|
|||||||
- todos os peers no mesmo ambiente (Linux nativo ou VMs), ou
|
- todos os peers no mesmo ambiente (Linux nativo ou VMs), ou
|
||||||
- dois PCs físicos na mesma sub-rede.
|
- dois PCs físicos na mesma sub-rede.
|
||||||
|
|
||||||
|
## Checklist — dois PCs na mesma Wi‑Fi
|
||||||
|
|
||||||
|
1. **Mesma porta no login** (ex.: `5000` nos dois). Portas diferentes isolam a sala.
|
||||||
|
2. **Configurações → Sua conexão:** IPs na mesma sub-rede (ex. ambos `192.168.0.x` ou `192.168.1.x`). Se um for `172.x` (WSL/Docker) e outro `192.168.x`, o multicast da aula não cruza.
|
||||||
|
3. **Firewall:** liberar **UDP entrada e saída** na porta do login (Windows Defender / `ufw`).
|
||||||
|
4. **Roteador:** desativar *isolamento de clientes* / *AP isolation* / Wi‑Fi de visitantes.
|
||||||
|
5. Aguardar até **~15 s** após abrir os dois apps (heartbeat a cada 5 s).
|
||||||
|
|
||||||
## Sintomas
|
## Sintomas
|
||||||
|
|
||||||
| Sintoma | Verificar |
|
| Sintoma | Verificar |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Lista de usuários vazia | Porta igual entre peers? Mesma sub-rede? Firewall? |
|
| Lista só com **Todos**, sem peers | Porta igual? Mesma sub-rede? Firewall UDP? AP isolation? WSL num dos PCs? |
|
||||||
| Plugin não recebe | Desserialização / SPI / JAR em `plugins/` |
|
| Plugin não recebe | Desserialização / SPI / JAR em `PITIUPI_HOME/plugins` |
|
||||||
| TCP falha | Peer ativo no heartbeat? IP correto? |
|
| TCP falha | Peer visível na lista? IP correto? |
|
||||||
@@ -13,6 +13,7 @@ import java.net.*;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import pitiupi.presentation.shell.LocalNetworkInfo;
|
||||||
import pitiupi.control.ConnectionManager;
|
import pitiupi.control.ConnectionManager;
|
||||||
import pitiupi.control.plugins.PluginManager;
|
import pitiupi.control.plugins.PluginManager;
|
||||||
import pitiupi.models.Messages.GoodbyeMessage;
|
import pitiupi.models.Messages.GoodbyeMessage;
|
||||||
@@ -39,6 +40,7 @@ public class SocketUDP extends Thread {
|
|||||||
private MulticastSocket multicastSocket;
|
private MulticastSocket multicastSocket;
|
||||||
|
|
||||||
private InetAddress address;
|
private InetAddress address;
|
||||||
|
private NetworkInterface multicastInterface;
|
||||||
|
|
||||||
private final ConnectionManager connectionManager;
|
private final ConnectionManager connectionManager;
|
||||||
private final PluginManager pluginManager;
|
private final PluginManager pluginManager;
|
||||||
@@ -62,12 +64,20 @@ public class SocketUDP extends Thread {
|
|||||||
try {
|
try {
|
||||||
this.address = InetAddress.getByName(SocketUDP.INET_ADDR);
|
this.address = InetAddress.getByName(SocketUDP.INET_ADDR);
|
||||||
|
|
||||||
multicastSocket = new MulticastSocket(connectionManager.getPort());
|
int port = connectionManager.getPort();
|
||||||
|
multicastSocket = new MulticastSocket(port);
|
||||||
multicastSocket.setSendBufferSize(256000);
|
multicastSocket.setSendBufferSize(256000);
|
||||||
multicastSocket.setReceiveBufferSize(256000);
|
multicastSocket.setReceiveBufferSize(256000);
|
||||||
multicastSocket.setReuseAddress(true);
|
multicastSocket.setReuseAddress(true);
|
||||||
|
multicastSocket.setTimeToLive(1);
|
||||||
|
multicastInterface = LocalNetworkInfo.preferredNetworkInterface();
|
||||||
|
if (multicastInterface != null) {
|
||||||
|
multicastSocket.setNetworkInterface(multicastInterface);
|
||||||
|
multicastSocket.joinGroup(new InetSocketAddress(address, port), multicastInterface);
|
||||||
|
} else {
|
||||||
multicastSocket.joinGroup(address);
|
multicastSocket.joinGroup(address);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (UnknownHostException ex) {
|
catch (UnknownHostException ex) {
|
||||||
System.getLogger(SocketUDP.class.getName()).log(System.Logger.Level.ERROR, (String) null, ex);
|
System.getLogger(SocketUDP.class.getName()).log(System.Logger.Level.ERROR, (String) null, ex);
|
||||||
}
|
}
|
||||||
@@ -118,7 +128,12 @@ public class SocketUDP extends Thread {
|
|||||||
running = false;
|
running = false;
|
||||||
if (multicastSocket != null) {
|
if (multicastSocket != null) {
|
||||||
try {
|
try {
|
||||||
|
int port = connectionManager.getPort();
|
||||||
|
if (multicastInterface != null) {
|
||||||
|
multicastSocket.leaveGroup(new InetSocketAddress(address, port), multicastInterface);
|
||||||
|
} else {
|
||||||
multicastSocket.leaveGroup(address);
|
multicastSocket.leaveGroup(address);
|
||||||
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
System.out.println(ex);
|
System.out.println(ex);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import java.net.Inet4Address;
|
|||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.NetworkInterface;
|
import java.net.NetworkInterface;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Endereço IPv4 local preferencial para compartilhar com peers.
|
* Endereço IPv4 local preferencial para compartilhar com peers.
|
||||||
@@ -13,20 +14,49 @@ public final class LocalNetworkInfo {
|
|||||||
private LocalNetworkInfo() {
|
private LocalNetworkInfo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String bestLocalIpv4() {
|
/**
|
||||||
|
* Interface preferida para LAN (Wi‑Fi/cabo), ignorando bridges Docker/WSL/VPN virtuais quando possível.
|
||||||
|
*/
|
||||||
|
public static NetworkInterface preferredNetworkInterface() {
|
||||||
try {
|
try {
|
||||||
|
NetworkInterface fallback = null;
|
||||||
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
|
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
|
||||||
while (interfaces.hasMoreElements()) {
|
while (interfaces.hasMoreElements()) {
|
||||||
NetworkInterface ni = interfaces.nextElement();
|
NetworkInterface ni = interfaces.nextElement();
|
||||||
if (!ni.isUp() || ni.isLoopback() || ni.isVirtual()) {
|
if (!isCandidateInterface(ni)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Enumeration<InetAddress> addresses = ni.getInetAddresses();
|
if (isPreferredLanInterfaceName(ni.getName())) {
|
||||||
while (addresses.hasMoreElements()) {
|
return ni;
|
||||||
InetAddress addr = addresses.nextElement();
|
|
||||||
if (addr instanceof Inet4Address && !addr.isLoopbackAddress()) {
|
|
||||||
return addr.getHostAddress();
|
|
||||||
}
|
}
|
||||||
|
if (fallback == null) {
|
||||||
|
fallback = ni;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fallback;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String bestLocalIpv4() {
|
||||||
|
try {
|
||||||
|
NetworkInterface preferred = preferredNetworkInterface();
|
||||||
|
if (preferred != null) {
|
||||||
|
String fromPreferred = firstIpv4OnInterface(preferred);
|
||||||
|
if (fromPreferred != null) {
|
||||||
|
return fromPreferred;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
|
||||||
|
while (interfaces.hasMoreElements()) {
|
||||||
|
NetworkInterface ni = interfaces.nextElement();
|
||||||
|
if (!isCandidateInterface(ni)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String ip = firstIpv4OnInterface(ni);
|
||||||
|
if (ip != null) {
|
||||||
|
return ip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InetAddress local = InetAddress.getLocalHost();
|
InetAddress local = InetAddress.getLocalHost();
|
||||||
@@ -36,6 +66,66 @@ public final class LocalNetworkInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isCandidateInterface(NetworkInterface ni) {
|
||||||
|
if (ni == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (!ni.isUp() || ni.isLoopback()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (java.net.SocketException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ni.isVirtual() || isVirtualInterfaceName(ni.getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return firstIpv4OnInterface(ni) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isVirtualInterfaceName(String name) {
|
||||||
|
if (name == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
String n = name.toLowerCase(Locale.ROOT);
|
||||||
|
return n.equals("lo")
|
||||||
|
|| n.startsWith("docker")
|
||||||
|
|| n.startsWith("br-")
|
||||||
|
|| n.startsWith("veth")
|
||||||
|
|| n.startsWith("vir")
|
||||||
|
|| n.contains("wsl")
|
||||||
|
|| n.startsWith("tailscale");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isPreferredLanInterfaceName(String name) {
|
||||||
|
if (name == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String n = name.toLowerCase(Locale.ROOT);
|
||||||
|
return n.startsWith("wlan")
|
||||||
|
|| n.startsWith("wlp")
|
||||||
|
|| n.startsWith("wl")
|
||||||
|
|| n.startsWith("eth")
|
||||||
|
|| n.startsWith("eno")
|
||||||
|
|| n.startsWith("enp")
|
||||||
|
|| n.startsWith("ens");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String firstIpv4OnInterface(NetworkInterface ni) {
|
||||||
|
try {
|
||||||
|
Enumeration<InetAddress> addresses = ni.getInetAddresses();
|
||||||
|
while (addresses.hasMoreElements()) {
|
||||||
|
InetAddress addr = addresses.nextElement();
|
||||||
|
if (addr instanceof Inet4Address && !addr.isLoopbackAddress()) {
|
||||||
|
return addr.getHostAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code true} se o IP informado é deste host (inclui loopback e interfaces ativas).
|
* {@code true} se o IP informado é deste host (inclui loopback e interfaces ativas).
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in new issue
Block a user