@cappitolian/network-discovery 0.0.13 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -240
- package/android/src/main/java/com/cappitolian/plugins/networkdiscovery/NetworkDiscovery.java +123 -17
- package/android/src/main/java/com/cappitolian/plugins/networkdiscovery/NetworkDiscoveryPlugin.java +24 -13
- package/dist/docs.json +44 -30
- package/dist/esm/definitions.d.ts +47 -9
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +3 -3
- package/dist/esm/web.js +6 -5
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +7 -6
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +7 -6
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/NetworkDiscoveryPlugin/NetworkDiscovery.swift +138 -25
- package/ios/Sources/NetworkDiscoveryPlugin/NetworkDiscoveryPlugin.swift +10 -32
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,11 +6,12 @@ A Capacitor plugin for network service discovery using mDNS/Bonjour. Allows auto
|
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
- **
|
|
9
|
+
- **Publish services** on the local network (server mode)
|
|
10
10
|
- **Discover services** automatically (client mode)
|
|
11
|
-
- Pass custom
|
|
12
|
-
-
|
|
13
|
-
- Works on **iOS** (Bonjour) and **Android** (NSD)
|
|
11
|
+
- Pass custom metadata via TXT records (like HTTP port, version, etc.)
|
|
12
|
+
- Anti-ghosting IP resolution logic
|
|
13
|
+
- Works on **iOS** (Bonjour/NWListener) and **Android** (NSD)
|
|
14
|
+
- Cross-platform compatible: iOS ↔ Android in both directions
|
|
14
15
|
- Tested with **Capacitor 7** and **Ionic 8**
|
|
15
16
|
|
|
16
17
|
---
|
|
@@ -30,133 +31,48 @@ npx cap sync
|
|
|
30
31
|
import { NetworkDiscovery } from '@cappitolian/network-discovery';
|
|
31
32
|
```
|
|
32
33
|
|
|
33
|
-
### Server Mode -
|
|
34
|
+
### Server Mode - Publish Your Service
|
|
34
35
|
```typescript
|
|
35
|
-
// Start
|
|
36
|
-
await NetworkDiscovery.
|
|
37
|
-
serviceName: '
|
|
38
|
-
serviceType: '
|
|
39
|
-
port:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
// Start publishing your server
|
|
37
|
+
await NetworkDiscovery.startServer({
|
|
38
|
+
serviceName: 'SSSPOSServer',
|
|
39
|
+
serviceType: '_ssspos._tcp',
|
|
40
|
+
port: 8081, // Discovery service port
|
|
41
|
+
ip: '192.168.1.100', // Your server IP
|
|
42
|
+
metadata: {
|
|
43
|
+
httpPort: '8080', // Your actual HTTP server port
|
|
44
|
+
version: '1.0.0', // Any custom data
|
|
45
|
+
deviceName: 'Main Server'
|
|
43
46
|
}
|
|
44
47
|
});
|
|
45
48
|
|
|
46
49
|
console.log('Server is now discoverable on the network');
|
|
47
50
|
|
|
48
|
-
// Stop
|
|
49
|
-
await NetworkDiscovery.
|
|
51
|
+
// Stop publishing when needed
|
|
52
|
+
await NetworkDiscovery.stopServer();
|
|
50
53
|
```
|
|
51
54
|
|
|
52
55
|
### Client Mode - Discover Services
|
|
53
56
|
```typescript
|
|
54
|
-
//
|
|
55
|
-
NetworkDiscovery.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
console.log('Server Addresses:', service.addresses);
|
|
60
|
-
|
|
61
|
-
// Connect to your server
|
|
62
|
-
connectToServer(service.addresses[0], service.port);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// Listen for lost services
|
|
66
|
-
NetworkDiscovery.addListener('serviceLost', (service) => {
|
|
67
|
-
console.log('Service lost:', service.serviceName);
|
|
57
|
+
// Find a server on the network
|
|
58
|
+
const result = await NetworkDiscovery.findServer({
|
|
59
|
+
serviceName: 'SSSPOSServer',
|
|
60
|
+
serviceType: '_ssspos._tcp',
|
|
61
|
+
timeout: 15000 // Optional, defaults to 10000ms
|
|
68
62
|
});
|
|
69
63
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// Stop discovery when needed
|
|
77
|
-
await NetworkDiscovery.stopDiscovery();
|
|
78
|
-
|
|
79
|
-
// Clean up listeners
|
|
80
|
-
await NetworkDiscovery.removeAllListeners();
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### Complete Example - Auto-Connect Flow
|
|
84
|
-
|
|
85
|
-
**Server Side:**
|
|
86
|
-
```typescript
|
|
87
|
-
import { NetworkDiscovery } from '@cappitolian/network-discovery';
|
|
88
|
-
import { Network } from '@capacitor/network';
|
|
89
|
-
|
|
90
|
-
async startServer() {
|
|
91
|
-
// Get device IP
|
|
92
|
-
const ip = await this.getLocalIP();
|
|
93
|
-
|
|
94
|
-
// Advertise server
|
|
95
|
-
await NetworkDiscovery.startAdvertising({
|
|
96
|
-
serviceName: 'MyAppServer',
|
|
97
|
-
serviceType: '_http._tcp', // Must match the server's serviceType or '_myapp._tcp' for custom service to avoid conflicts with other services
|
|
98
|
-
port: 8080,
|
|
99
|
-
txtRecord: {
|
|
100
|
-
ip: ip,
|
|
101
|
-
serverName: 'Production Server'
|
|
102
|
-
}
|
|
103
|
-
});
|
|
64
|
+
if (result) {
|
|
65
|
+
console.log('Server found!');
|
|
66
|
+
console.log('IP:', result.ip);
|
|
67
|
+
console.log('Discovery Port:', result.port);
|
|
68
|
+
console.log('HTTP Port:', result.metadata.httpPort);
|
|
69
|
+
console.log('Version:', result.metadata.version);
|
|
104
70
|
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
// Your IP extraction logic here
|
|
111
|
-
return '192.168.1.100';
|
|
112
|
-
}
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
**Client Side:**
|
|
116
|
-
```typescript
|
|
117
|
-
import { NetworkDiscovery } from '@cappitolian/network-discovery';
|
|
118
|
-
|
|
119
|
-
async findAndConnectToServer() {
|
|
120
|
-
return new Promise((resolve, reject) => {
|
|
121
|
-
|
|
122
|
-
// Listen for server
|
|
123
|
-
NetworkDiscovery.addListener('serviceFound', async (service) => {
|
|
124
|
-
if (service.serviceName === 'MyAppServer') {
|
|
125
|
-
console.log('Server found!');
|
|
126
|
-
|
|
127
|
-
const serverIP = service.txtRecord?.ip || service.addresses[0];
|
|
128
|
-
const serverPort = service.port;
|
|
129
|
-
|
|
130
|
-
// Stop discovery
|
|
131
|
-
await NetworkDiscovery.stopDiscovery();
|
|
132
|
-
await NetworkDiscovery.removeAllListeners();
|
|
133
|
-
|
|
134
|
-
// Connect to server
|
|
135
|
-
resolve({ ip: serverIP, port: serverPort });
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
// Start discovery
|
|
140
|
-
NetworkDiscovery.startDiscovery({
|
|
141
|
-
serviceType: '_http._tcp' // Must match the server's serviceType or '_myapp._tcp' for custom service to avoid conflicts with other services
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
// Timeout after 10 seconds
|
|
145
|
-
setTimeout(() => {
|
|
146
|
-
reject(new Error('Server not found'));
|
|
147
|
-
}, 10000);
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Usage
|
|
152
|
-
async onLoginClick() {
|
|
153
|
-
try {
|
|
154
|
-
const server = await this.findAndConnectToServer();
|
|
155
|
-
console.log(`Connecting to ${server.ip}:${server.port}`);
|
|
156
|
-
// Your connection logic here
|
|
157
|
-
} catch (error) {
|
|
158
|
-
console.error('Could not find server:', error);
|
|
159
|
-
}
|
|
71
|
+
// Connect to your HTTP server
|
|
72
|
+
const httpUrl = `http://${result.ip}:${result.metadata.httpPort}`;
|
|
73
|
+
// Make your API calls here
|
|
74
|
+
} else {
|
|
75
|
+
console.log('Server not found within timeout');
|
|
160
76
|
}
|
|
161
77
|
```
|
|
162
78
|
|
|
@@ -166,104 +82,70 @@ async onLoginClick() {
|
|
|
166
82
|
|
|
167
83
|
### Methods
|
|
168
84
|
|
|
169
|
-
#### `
|
|
85
|
+
#### `startServer(options: StartServerOptions)`
|
|
170
86
|
|
|
171
87
|
Publishes a service on the local network.
|
|
172
88
|
|
|
173
89
|
**Parameters:**
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
Stops advertising the service.
|
|
90
|
+
```typescript
|
|
91
|
+
{
|
|
92
|
+
serviceName: string; // Name of your service
|
|
93
|
+
serviceType: string; // Service type (e.g., "_ssspos._tcp")
|
|
94
|
+
port: number; // Discovery service port
|
|
95
|
+
ip: string; // Your server IP address
|
|
96
|
+
metadata?: { // Optional custom key-value pairs
|
|
97
|
+
[key: string]: string;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
```
|
|
186
101
|
|
|
187
|
-
|
|
102
|
+
#### `stopServer()`
|
|
188
103
|
|
|
189
|
-
|
|
104
|
+
Stops publishing the service.
|
|
190
105
|
|
|
191
|
-
#### `
|
|
106
|
+
#### `findServer(options: FindServerOptions)`
|
|
192
107
|
|
|
193
|
-
|
|
108
|
+
Searches for a service on the local network.
|
|
194
109
|
|
|
195
110
|
**Parameters:**
|
|
196
|
-
- `serviceType` (string): Type of service to search for (must match server's type)
|
|
197
|
-
- `domain` (string, optional): Domain to search in (default: "local.")
|
|
198
|
-
|
|
199
|
-
**Returns:** `Promise<void>`
|
|
200
|
-
|
|
201
|
-
---
|
|
202
|
-
|
|
203
|
-
#### `stopDiscovery()`
|
|
204
|
-
|
|
205
|
-
Stops the service discovery.
|
|
206
|
-
|
|
207
|
-
**Returns:** `Promise<{ success: boolean }>`
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
|
|
211
|
-
### Events
|
|
212
|
-
|
|
213
|
-
#### `serviceFound`
|
|
214
|
-
|
|
215
|
-
Fired when a service is discovered.
|
|
216
|
-
|
|
217
|
-
**Payload:**
|
|
218
111
|
```typescript
|
|
219
112
|
{
|
|
220
113
|
serviceName: string;
|
|
221
114
|
serviceType: string;
|
|
222
|
-
|
|
223
|
-
hostName: string;
|
|
224
|
-
port: number;
|
|
225
|
-
addresses: string[]; // Array of IP addresses
|
|
226
|
-
txtRecord?: { // Custom data from server
|
|
227
|
-
[key: string]: string;
|
|
228
|
-
};
|
|
115
|
+
timeout?: number; // Default: 10000ms
|
|
229
116
|
}
|
|
230
117
|
```
|
|
231
118
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
#### `serviceLost`
|
|
235
|
-
|
|
236
|
-
Fired when a previously discovered service is no longer available.
|
|
237
|
-
|
|
238
|
-
**Payload:**
|
|
119
|
+
**Returns:**
|
|
239
120
|
```typescript
|
|
240
121
|
{
|
|
241
|
-
|
|
242
|
-
|
|
122
|
+
ip: string;
|
|
123
|
+
port: number;
|
|
124
|
+
metadata: { [key: string]: string };
|
|
243
125
|
}
|
|
244
126
|
```
|
|
245
127
|
|
|
246
128
|
---
|
|
247
129
|
|
|
248
|
-
##
|
|
249
|
-
|
|
250
|
-
Service types must follow the format: `_name._protocol`
|
|
130
|
+
## Architecture Best Practices
|
|
251
131
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
132
|
+
### Separate Ports Pattern
|
|
133
|
+
```
|
|
134
|
+
Port 8080 → HTTP Server (GET, POST, API endpoints)
|
|
135
|
+
Port 8081 → Network Discovery (mDNS/Bonjour)
|
|
136
|
+
```
|
|
257
137
|
|
|
258
|
-
**
|
|
138
|
+
**Why:**
|
|
139
|
+
1. Avoids port binding conflicts
|
|
140
|
+
2. Clear separation of concerns
|
|
141
|
+
3. Easier debugging
|
|
142
|
+
4. HTTP server can restart without affecting discovery
|
|
259
143
|
|
|
260
144
|
---
|
|
261
145
|
|
|
262
146
|
## Permissions
|
|
263
147
|
|
|
264
148
|
### Android
|
|
265
|
-
|
|
266
|
-
Add to `AndroidManifest.xml`:
|
|
267
149
|
```xml
|
|
268
150
|
<uses-permission android:name="android.permission.INTERNET" />
|
|
269
151
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
@@ -272,33 +154,26 @@ Add to `AndroidManifest.xml`:
|
|
|
272
154
|
```
|
|
273
155
|
|
|
274
156
|
### iOS
|
|
275
|
-
|
|
276
|
-
No additional permissions required. Bonjour is enabled by default.
|
|
277
|
-
|
|
278
|
-
Optionally, add to `Info.plist` to declare your service:
|
|
279
157
|
```xml
|
|
158
|
+
<key>NSLocalNetworkUsageDescription</key>
|
|
159
|
+
<string>This app needs access to the local network to discover and connect to other devices.</string>
|
|
160
|
+
|
|
280
161
|
<key>NSBonjourServices</key>
|
|
281
162
|
<array>
|
|
282
|
-
<string>
|
|
163
|
+
<string>_ssspos._tcp</string>
|
|
283
164
|
</array>
|
|
284
165
|
```
|
|
285
166
|
|
|
286
167
|
---
|
|
287
168
|
|
|
288
|
-
##
|
|
289
|
-
|
|
290
|
-
- **iOS** (Bonjour/NetService)
|
|
291
|
-
- **Android** (Network Service Discovery)
|
|
292
|
-
- **Web** (Not implemented - throws unimplemented error)
|
|
293
|
-
|
|
294
|
-
---
|
|
295
|
-
|
|
296
|
-
## Requirements
|
|
169
|
+
## Cross-Platform Compatibility
|
|
297
170
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
171
|
+
| Server → Client | Status |
|
|
172
|
+
|----------------|--------|
|
|
173
|
+
| Android → Android | ✅ Working |
|
|
174
|
+
| Android → iOS | ✅ Working |
|
|
175
|
+
| iOS → Android | ✅ Working |
|
|
176
|
+
| iOS → iOS | ✅ Working |
|
|
302
177
|
|
|
303
178
|
---
|
|
304
179
|
|
|
@@ -306,39 +181,41 @@ Optionally, add to `Info.plist` to declare your service:
|
|
|
306
181
|
|
|
307
182
|
### Services not being discovered
|
|
308
183
|
|
|
309
|
-
1.
|
|
310
|
-
2.
|
|
311
|
-
3.
|
|
312
|
-
4.
|
|
313
|
-
5.
|
|
184
|
+
1. Check both devices are on same WiFi network
|
|
185
|
+
2. Verify service types match exactly
|
|
186
|
+
3. Check firewall/router settings (some block mDNS)
|
|
187
|
+
4. Android: Ensure multicast is enabled
|
|
188
|
+
5. iOS: Device not in Low Power Mode
|
|
314
189
|
|
|
315
|
-
### Server not
|
|
190
|
+
### iOS Server not visible to Android
|
|
316
191
|
|
|
317
|
-
|
|
318
|
-
2. **Check network permissions** are granted
|
|
319
|
-
3. **Restart the app** after installing the plugin
|
|
192
|
+
**Fixed in v1.0.0+**
|
|
320
193
|
|
|
321
|
-
|
|
194
|
+
If still experiencing issues:
|
|
195
|
+
1. Update to latest version
|
|
196
|
+
2. Use separate ports (8080 for HTTP, 8081 for discovery)
|
|
197
|
+
3. Check logs for "iOS Servidor LISTO" (iOS) and "NSD_DEBUG" (Android)
|
|
322
198
|
|
|
323
|
-
|
|
324
|
-
```typescript
|
|
325
|
-
// Check plugin is loaded
|
|
326
|
-
console.log('Plugin available:', NetworkDiscovery);
|
|
199
|
+
### Debugging
|
|
327
200
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
201
|
+
**Android:**
|
|
202
|
+
```bash
|
|
203
|
+
adb logcat | grep NSD_DEBUG
|
|
331
204
|
```
|
|
332
205
|
|
|
206
|
+
**iOS:**
|
|
207
|
+
Look for logs with `NSD_LOG:` in Xcode Console
|
|
208
|
+
|
|
333
209
|
---
|
|
334
210
|
|
|
335
|
-
##
|
|
211
|
+
## Changelog
|
|
336
212
|
|
|
337
|
-
|
|
338
|
-
-
|
|
339
|
-
-
|
|
340
|
-
-
|
|
341
|
-
-
|
|
213
|
+
### v1.0.0 (Current)
|
|
214
|
+
- ✅ Fixed iOS server → Android client discovery
|
|
215
|
+
- ✅ Added anti-ghosting IP resolution
|
|
216
|
+
- ✅ Improved cross-platform compatibility
|
|
217
|
+
- ✅ Enhanced logging for debugging
|
|
218
|
+
- ✅ Support for separate discovery/HTTP ports
|
|
342
219
|
|
|
343
220
|
---
|
|
344
221
|
|
|
@@ -346,20 +223,6 @@ NetworkDiscovery.addListener('serviceLost', (s) => console.log('Lost:', s));
|
|
|
346
223
|
|
|
347
224
|
MIT
|
|
348
225
|
|
|
349
|
-
---
|
|
350
|
-
|
|
351
|
-
## Support
|
|
352
|
-
|
|
353
|
-
If you encounter any issues or have feature requests, please open an issue on the [GitHub repository](https://github.com/alessandrycruz1987/network-discovery).
|
|
354
|
-
|
|
355
|
-
---
|
|
356
|
-
|
|
357
|
-
## Contributing
|
|
358
|
-
|
|
359
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
360
|
-
|
|
361
|
-
---
|
|
362
|
-
|
|
363
226
|
## Credits
|
|
364
227
|
|
|
365
|
-
|
|
228
|
+
Developed by Alessandry Cruz for Cappitolian projects.
|
package/android/src/main/java/com/cappitolian/plugins/networkdiscovery/NetworkDiscovery.java
CHANGED
|
@@ -4,49 +4,155 @@ import android.content.Context;
|
|
|
4
4
|
import android.net.nsd.NsdManager;
|
|
5
5
|
import android.net.nsd.NsdServiceInfo;
|
|
6
6
|
import android.net.wifi.WifiManager;
|
|
7
|
+
import android.os.Handler;
|
|
8
|
+
import android.os.Looper;
|
|
9
|
+
import com.getcapacitor.JSObject;
|
|
10
|
+
import java.nio.charset.StandardCharsets;
|
|
7
11
|
import java.util.Map;
|
|
8
12
|
|
|
9
13
|
public class NetworkDiscovery {
|
|
10
14
|
private NsdManager nsdManager;
|
|
11
15
|
private WifiManager.MulticastLock multicastLock;
|
|
12
|
-
private NsdManager.RegistrationListener
|
|
16
|
+
private NsdManager.RegistrationListener activeRegistrationListener;
|
|
17
|
+
private NsdManager.DiscoveryListener activeDiscoveryListener;
|
|
18
|
+
|
|
19
|
+
public interface Callback {
|
|
20
|
+
void success(JSObject data);
|
|
21
|
+
void error(String msg);
|
|
22
|
+
}
|
|
13
23
|
|
|
14
24
|
public NetworkDiscovery(Context context) {
|
|
15
25
|
nsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
|
|
16
26
|
WifiManager wm = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
|
|
17
27
|
multicastLock = wm.createMulticastLock("SSSPOS_Lock");
|
|
28
|
+
multicastLock.setReferenceCounted(true);
|
|
18
29
|
}
|
|
19
30
|
|
|
20
|
-
public void startServer(String name, String type, int port, Map<String, String> metadata, final
|
|
21
|
-
|
|
31
|
+
public void startServer(String name, String type, int port, Map<String, String> metadata, final Callback callback) {
|
|
32
|
+
stopServer(null);
|
|
33
|
+
if (!multicastLock.isHeld()) multicastLock.acquire();
|
|
34
|
+
|
|
35
|
+
// TRUCO: Incluimos la IP en el nombre por si los metadatos fallan en iOS
|
|
36
|
+
String ipForName = (metadata != null && metadata.containsKey("ip")) ? metadata.get("ip") : "";
|
|
37
|
+
String displayName = ipForName.isEmpty() ? name : name + "-" + ipForName;
|
|
22
38
|
|
|
23
39
|
NsdServiceInfo serviceInfo = new NsdServiceInfo();
|
|
24
|
-
serviceInfo.setServiceName(
|
|
40
|
+
serviceInfo.setServiceName(displayName);
|
|
25
41
|
serviceInfo.setServiceType(type);
|
|
26
42
|
serviceInfo.setPort(port);
|
|
27
43
|
|
|
44
|
+
// IMPORTANTE: Atributos antes de registrar
|
|
28
45
|
if (metadata != null) {
|
|
29
46
|
for (Map.Entry<String, String> entry : metadata.entrySet()) {
|
|
30
|
-
serviceInfo.setAttribute(entry.getKey(), entry.getValue());
|
|
47
|
+
serviceInfo.setAttribute(entry.getKey().toLowerCase(), entry.getValue());
|
|
31
48
|
}
|
|
32
49
|
}
|
|
33
50
|
|
|
34
|
-
|
|
35
|
-
@Override
|
|
36
|
-
public void
|
|
37
|
-
@Override
|
|
38
|
-
public void
|
|
39
|
-
|
|
40
|
-
|
|
51
|
+
activeRegistrationListener = new NsdManager.RegistrationListener() {
|
|
52
|
+
@Override public void onServiceRegistered(NsdServiceInfo info) { callback.success(new JSObject()); }
|
|
53
|
+
@Override public void onRegistrationFailed(NsdServiceInfo info, int err) { callback.error("REG_ERR_" + err); }
|
|
54
|
+
@Override public void onServiceUnregistered(NsdServiceInfo info) {}
|
|
55
|
+
@Override public void onUnregistrationFailed(NsdServiceInfo info, int err) {}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
nsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, activeRegistrationListener);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public void findServer(String name, String type, int timeout, final Callback callback) {
|
|
62
|
+
stopDiscovery();
|
|
63
|
+
if (!multicastLock.isHeld()) multicastLock.acquire();
|
|
64
|
+
|
|
65
|
+
final String cleanType = type.startsWith("_") ? type : "_" + type;
|
|
66
|
+
final boolean[] finished = {false};
|
|
67
|
+
|
|
68
|
+
activeDiscoveryListener = new NsdManager.DiscoveryListener() {
|
|
69
|
+
@Override public void onDiscoveryStarted(String s) {}
|
|
70
|
+
@Override public void onStartDiscoveryFailed(String s, int i) { if(!finished[0]){ finished[0]=true; callback.error("START_FAIL"); } }
|
|
41
71
|
@Override
|
|
42
|
-
public void
|
|
72
|
+
public void onServiceFound(NsdServiceInfo service) {
|
|
73
|
+
// 1. Log para ver qué está viendo Android realmente antes de filtrar
|
|
74
|
+
System.out.println("NSD_DEBUG: Servicio encontrado en red: " + service.getServiceName());
|
|
75
|
+
|
|
76
|
+
// 2. Filtro estricto: El nombre debe empezar con el prefijo exacto
|
|
77
|
+
if (service.getServiceName().startsWith(name)) {
|
|
78
|
+
nsdManager.resolveService(service, new NsdManager.ResolveListener() {
|
|
79
|
+
@Override public void onResolveFailed(NsdServiceInfo nsi, int i) {
|
|
80
|
+
System.out.println("NSD_DEBUG: Fallo al resolver servicio: " + i);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@Override
|
|
84
|
+
public void onServiceResolved(NsdServiceInfo resolved) {
|
|
85
|
+
if (!finished[0]) {
|
|
86
|
+
String sName = resolved.getServiceName();
|
|
87
|
+
|
|
88
|
+
// --- LÓGICA ANTI-GHOSTING ---
|
|
89
|
+
// Intentamos sacar la IP del nombre primero (es la fuente de verdad)
|
|
90
|
+
String ipFromName = "";
|
|
91
|
+
if (sName.contains("-")) {
|
|
92
|
+
ipFromName = sName.substring(sName.lastIndexOf("-") + 1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Si el nombre no tiene IP, usamos la resuelta por el sistema
|
|
96
|
+
String resolvedIp = resolved.getHost().getHostAddress();
|
|
97
|
+
if (resolvedIp.startsWith("/")) resolvedIp = resolvedIp.substring(1);
|
|
98
|
+
|
|
99
|
+
// Decisión final de IP
|
|
100
|
+
String finalIp = (!ipFromName.isEmpty()) ? ipFromName : resolvedIp;
|
|
101
|
+
|
|
102
|
+
// IMPORTANTE: Si la IP resuelta es 0.0.0.0 o vacía, ignoramos este evento
|
|
103
|
+
if (finalIp.equals("0.0.0.0") || finalIp.isEmpty()) return;
|
|
104
|
+
|
|
105
|
+
finished[0] = true;
|
|
106
|
+
stopDiscovery();
|
|
107
|
+
|
|
108
|
+
JSObject res = new JSObject();
|
|
109
|
+
JSObject meta = new JSObject();
|
|
110
|
+
for (Map.Entry<String, byte[]> entry : resolved.getAttributes().entrySet()) {
|
|
111
|
+
meta.put(entry.getKey(), new String(entry.getValue(), StandardCharsets.UTF_8));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
res.put("ip", finalIp);
|
|
115
|
+
res.put("port", resolved.getPort());
|
|
116
|
+
res.put("metadata", meta);
|
|
117
|
+
|
|
118
|
+
System.out.println("NSD_DEBUG: IP Final entregada al Cliente: " + finalIp);
|
|
119
|
+
callback.success(res);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
@Override public void onStopDiscoveryFailed(String s, int i) {}
|
|
126
|
+
@Override public void onDiscoveryStopped(String s) {}
|
|
127
|
+
@Override public void onServiceLost(NsdServiceInfo s) {}
|
|
43
128
|
};
|
|
44
129
|
|
|
45
|
-
nsdManager.
|
|
130
|
+
nsdManager.discoverServices(cleanType, NsdManager.PROTOCOL_DNS_SD, activeDiscoveryListener);
|
|
131
|
+
new Handler(Looper.getMainLooper()).postDelayed(() -> {
|
|
132
|
+
if (!finished[0]) { finished[0]=true; stopDiscovery(); callback.error("TIMEOUT_ERROR"); }
|
|
133
|
+
}, timeout);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
public void stopServer(Callback c) {
|
|
137
|
+
if (activeRegistrationListener != null) { try { nsdManager.unregisterService(activeRegistrationListener); } catch (Exception e) {} }
|
|
138
|
+
activeRegistrationListener = null;
|
|
139
|
+
if (multicastLock.isHeld()) multicastLock.release();
|
|
140
|
+
if (c != null) c.success(new JSObject());
|
|
46
141
|
}
|
|
47
142
|
|
|
48
|
-
public
|
|
49
|
-
|
|
50
|
-
|
|
143
|
+
public void stopDiscovery() {
|
|
144
|
+
if (activeDiscoveryListener != null) {
|
|
145
|
+
try {
|
|
146
|
+
nsdManager.stopServiceDiscovery(activeDiscoveryListener);
|
|
147
|
+
System.out.println("NSD_DEBUG: Discovery detenido.");
|
|
148
|
+
} catch (Exception e) {
|
|
149
|
+
System.out.println("NSD_DEBUG: Error al detener discovery: " + e.getMessage());
|
|
150
|
+
}
|
|
151
|
+
activeDiscoveryListener = null;
|
|
152
|
+
}
|
|
153
|
+
// Liberamos el lock solo si se detiene por completo el proceso
|
|
154
|
+
if (multicastLock != null && multicastLock.isHeld()) {
|
|
155
|
+
multicastLock.release();
|
|
156
|
+
}
|
|
51
157
|
}
|
|
52
158
|
}
|