• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SpiNNakerManchester / JavaSpiNNaker / 6310285782

26 Sep 2023 08:47AM UTC coverage: 36.367% (-0.5%) from 36.866%
6310285782

Pull #658

github

dkfellows
Merge branch 'master' into java-17
Pull Request #658: Update Java version to 17 and JEE to 9

1675 of 1675 new or added lines in 266 files covered. (100.0%)

8368 of 23010 relevant lines covered (36.37%)

0.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

31.65
/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/UDPConnection.java
1
/*
2
 * Copyright (c) 2018 The University of Manchester
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     https://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package uk.ac.manchester.spinnaker.connections;
17

18
import static java.lang.String.format;
19
import static java.net.InetAddress.getByAddress;
20
import static java.nio.ByteBuffer.wrap;
21
import static java.util.Objects.requireNonNullElse;
22
import static java.util.stream.Collectors.joining;
23
import static java.util.stream.IntStream.range;
24
import static org.slf4j.LoggerFactory.getLogger;
25
import static uk.ac.manchester.spinnaker.messages.Constants.IPV4_SIZE;
26
import static uk.ac.manchester.spinnaker.messages.Constants.SCP_SCAMP_PORT;
27
import static uk.ac.manchester.spinnaker.messages.sdp.SDPHeader.Flag.REPLY_NOT_EXPECTED;
28
import static uk.ac.manchester.spinnaker.messages.sdp.SDPPort.RUNNING_COMMAND_SDP_PORT;
29
import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc;
30
import static uk.ac.manchester.spinnaker.utils.MathUtils.hexbyte;
31
import static uk.ac.manchester.spinnaker.utils.Ping.ping;
32

33
import java.io.EOFException;
34
import java.io.IOException;
35
import java.net.DatagramPacket;
36
import java.net.DatagramSocket;
37
import java.net.Inet4Address;
38
import java.net.InetAddress;
39
import java.net.InetSocketAddress;
40
import java.net.SocketAddress;
41
import java.net.SocketTimeoutException;
42
import java.net.UnknownHostException;
43
import java.nio.ByteBuffer;
44

45
import org.slf4j.Logger;
46

47
import com.google.errorprone.annotations.ForOverride;
48
import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper;
49

50
import uk.ac.manchester.spinnaker.connections.model.Connection;
51
import uk.ac.manchester.spinnaker.messages.sdp.SDPHeader;
52
import uk.ac.manchester.spinnaker.messages.sdp.SDPLocation;
53
import uk.ac.manchester.spinnaker.messages.sdp.SDPMessage;
54

55
/**
56
 * A connection to SpiNNaker over UDP/IPv4.
57
 *
58
 * @param <T>
59
 *            The type of message to be received. It's possible for the received
60
 *            information to even be metadata about the message, and not the
61
 *            content of the message.
62
 */
63
public abstract class UDPConnection<T> implements Connection {
64
        private static final Logger log = getLogger(UDPConnection.class);
1✔
65

66
        private static final int RECEIVE_BUFFER_SIZE = 1048576;
67

68
        private static final int PING_COUNT = 5;
69

70
        private static final int PACKET_MAX_SIZE = 300;
71

72
        /**
73
         * The type of traffic being sent on a socket.
74
         *
75
         * @see DatagramSocket#setTrafficClass(int)
76
         */
77
        public enum TrafficClass {
1✔
78
                /** Minimise cost. */
79
                IPTOS_LOWCOST(0x02),
1✔
80
                /** Maximise reliability. */
81
                IPTOS_RELIABILITY(0x04),
1✔
82
                /** Maximise throughput. */
83
                IPTOS_THROUGHPUT(0x08),
1✔
84
                /** Minimise delay. */
85
                IPTOS_LOWDELAY(0x10);
1✔
86

87
                private final int value;
88

89
                TrafficClass(int value) {
1✔
90
                        this.value = value;
1✔
91
                }
1✔
92
        }
93

94
        private boolean canSend;
95

96
        private Inet4Address remoteIPAddress;
97

98
        private InetSocketAddress remoteAddress;
99

100
        private final DatagramSocket socket;
101

102
        private int receivePacketSize = PACKET_MAX_SIZE;
1✔
103

104
        /**
105
         * Main constructor, any argument of which could {@code null}.
106
         * <p>
107
         * No default constructors are provided as it would not be possible to
108
         * disambiguate between ones with only a local host/port like
109
         * {@link IPAddressConnection} and ones with only remote host/port like
110
         * {@link BMPConnection}.
111
         *
112
         * @param localHost
113
         *            The local host to bind to. If {@code null}, it defaults to
114
         *            binding to all interfaces, unless {@code remoteHost} is
115
         *            specified, in which case binding is done to the IP address
116
         *            that will be used to send packets.
117
         * @param localPort
118
         *            The local port to bind to, 0 (or {@code null}) or between 1025
119
         *            and 65535.
120
         * @param remoteHost
121
         *            The remote host name or IP address to send packets to. If
122
         *            {@code null}, the socket will be available for listening only,
123
         *            and will throw an exception if used for sending.
124
         * @param remotePort
125
         *            The remote port to send packets to. If {@code remoteHost} is
126
         *            {@code null}, this is ignored. If {@code remoteHost} is
127
         *            specified, this must also be specified as non-zero for the
128
         *            connection to allow sending.
129
         * @param trafficClass
130
         *            What sort of traffic is this socket going to send. If
131
         *            {@code null}, no traffic class will be used. Receive-only
132
         *            sockets should leave this as {@code null}.
133
         * @throws IOException
134
         *             If there is an error setting up the communication channel
135
         */
136
        public UDPConnection(InetAddress localHost, Integer localPort,
137
                        InetAddress remoteHost, Integer remotePort,
138
                        TrafficClass trafficClass) throws IOException {
1✔
139
                canSend = (remoteHost != null && remotePort != null && remotePort > 0);
1✔
140
                socket = initialiseSocket(localHost, localPort, remoteHost, remotePort,
1✔
141
                                trafficClass);
142
                if (log.isDebugEnabled()) {
1✔
143
                        log.debug("{} socket created ({} <--> {})", getClass().getName(),
×
144
                                        localAddr(), remoteAddr());
×
145
                }
146
        }
1✔
147

148
        /**
149
         * Make a connection where actual operations on the socket will be
150
         * delegated. If you use this constructor, you <strong>must</strong>
151
         * override {@link #isClosed()}, and possibly {@link #isConnected()} as
152
         * well.
153
         *
154
         * @param remoteHost
155
         *            The remote host name or IP address to send packets to. If
156
         *            {@code null}, the socket will be available for listening only,
157
         *            and will throw an exception if used for sending.
158
         * @param remotePort
159
         *            The remote port to send packets to. If {@code remoteHost} is
160
         *            {@code null}, this is ignored. If {@code remoteHost} is
161
         *            specified, this must also be specified as non-zero for the
162
         *            connection to allow sending.
163
         */
164
        UDPConnection(InetAddress remoteHost, Integer remotePort) {
×
165
                canSend = (remoteHost != null && remotePort != null && remotePort > 0);
×
166
                if (canSend) {
×
167
                        remoteIPAddress = (Inet4Address) remoteHost;
×
168
                        remoteAddress = new InetSocketAddress(remoteIPAddress, remotePort);
×
169
                }
170
                socket = null;
×
171
        }
×
172

173
        /**
174
         * Set the maximum size of packet that can be received. Packets larger than
175
         * this will be truncated. The default is large enough for any packet that
176
         * is sent by SCAMP.
177
         *
178
         * @param receivePacketSize
179
         *            The new maximum packet size.
180
         */
181
        protected void setReceivePacketSize(int receivePacketSize) {
182
                this.receivePacketSize = receivePacketSize;
×
183
        }
×
184

185
        private static final InetSocketAddress ANY =
1✔
186
                        new InetSocketAddress((InetAddress) null, 0);
187

188
        /**
189
         * Set up a UDP/IPv4 socket.
190
         *
191
         * @param localHost
192
         *            Local side address.
193
         * @param localPort
194
         *            Local side port.
195
         * @param remoteHost
196
         *            Remote side address.
197
         * @param remotePort
198
         *            Remote side port.
199
         * @param trafficClass
200
         *            Traffic class, at least for sending. If {@code null}, no
201
         *            traffic class will be set.
202
         * @return The configured, connected socket.
203
         * @throws IOException
204
         *             If anything fails.
205
         */
206
        @ForOverride
207
        DatagramSocket initialiseSocket(InetAddress localHost, Integer localPort,
208
                        InetAddress remoteHost, Integer remotePort,
209
                        TrafficClass trafficClass) throws IOException {
210
                // SpiNNaker only speaks IPv4
211
                var sock = new DatagramSocket(createLocalAddress(localHost, localPort));
1✔
212
                if (trafficClass != null) {
1✔
213
                        sock.setTrafficClass(trafficClass.value);
1✔
214
                }
215
                sock.setReceiveBufferSize(RECEIVE_BUFFER_SIZE);
1✔
216
                if (canSend) {
1✔
217
                        remoteIPAddress = (Inet4Address) remoteHost;
1✔
218
                        remoteAddress = new InetSocketAddress(remoteIPAddress, remotePort);
1✔
219
                        sock.connect(remoteAddress);
1✔
220
                }
221
                return sock;
1✔
222
        }
223

224
        private static SocketAddress createLocalAddress(InetAddress localHost,
225
                        Integer localPort) throws UnknownHostException {
226
                // Convert null into wildcard
227
                if (localPort == null) {
1✔
228
                        localPort = 0;
1✔
229
                }
230
                Inet4Address localAddr;
231
                try {
232
                        if (localHost == null) {
1✔
233
                                localAddr = (Inet4Address) getByAddress(new byte[IPV4_SIZE]);
1✔
234
                        } else {
235
                                localAddr = (Inet4Address) localHost;
×
236
                        }
237
                } catch (ClassCastException e) {
×
238
                        throw new UnknownHostException("SpiNNaker only talks IPv4");
×
239
                }
1✔
240
                return new InetSocketAddress(localAddr, localPort);
1✔
241
        }
242

243
        /**
244
         * Get the local socket address. (Sockets have two ends, one local, one
245
         * remote.)
246
         * <p>
247
         * This operation is <em>delegatable</em>; see
248
         * {@link DelegatingSCPConnection}.
249
         *
250
         * @return The socket's local address
251
         * @throws IOException
252
         *             If the socket is closed.
253
         */
254
        @ForOverride
255
        protected InetSocketAddress getLocalAddress() throws IOException {
256
                if (socket == null) {
×
257
                        return null;
×
258
                }
259
                return (InetSocketAddress) socket.getLocalSocketAddress();
×
260
        }
261

262
        private InetSocketAddress localAddr() {
263
                try {
264
                        return requireNonNullElse(getLocalAddress(), ANY);
×
265
                } catch (IOException e) {
×
266
                        return ANY;
×
267
                }
268
        }
269

270
        /**
271
         * Get the remote socket address. (Sockets have two ends, one local, one
272
         * remote.)
273
         * <p>
274
         * This operation is <em>delegatable</em>; see
275
         * {@link DelegatingSCPConnection}.
276
         *
277
         * @return The socket's remote address
278
         */
279
        protected InetSocketAddress getRemoteAddress() {
280
                return remoteAddress;
×
281
        }
282

283
        private InetSocketAddress remoteAddr() {
284
                return requireNonNullElse(getRemoteAddress(), ANY);
×
285
        }
286

287
        /** @return The local IP address to which the connection is bound. */
288
        @Override
289
        public final InetAddress getLocalIPAddress() {
290
                try {
291
                        return getLocalAddress().getAddress();
×
292
                } catch (NullPointerException | IOException e) {
×
293
                        return null;
×
294
                }
295
        }
296

297
        /** @return The local port to which the connection is bound. */
298
        @Override
299
        public final int getLocalPort() {
300
                try {
301
                        return getLocalAddress().getPort();
×
302
                } catch (NullPointerException | IOException e) {
×
303
                        return -1;
×
304
                }
305
        }
306

307
        /**
308
         * @return The remote IP address to which the connection is connected, or
309
         *         {@code null} if it is not connected.
310
         */
311
        @Override
312
        public final InetAddress getRemoteIPAddress() {
313
                try {
314
                        return getRemoteAddress().getAddress();
×
315
                } catch (NullPointerException e) {
×
316
                        return null;
×
317
                }
318
        }
319

320
        /**
321
         * @return The remote port to which the connection is connected, or zero if
322
         *         it is not connected.
323
         */
324
        @Override
325
        public final int getRemotePort() {
326
                try {
327
                        return getRemoteAddress().getPort();
×
328
                } catch (NullPointerException e) {
×
329
                        return -1;
×
330
                }
331
        }
332

333
        @Override
334
        public final ByteBuffer receive(Integer timeout)
335
                        throws SocketTimeoutException, IOException, InterruptedException {
336
                if (timeout != null) {
×
337
                        return receive(convertTimeout(timeout));
×
338
                }
339
                // Want to wait forever but the underlying engine won't...
340
                while (true) {
341
                        try {
342
                                return receive(convertTimeout(timeout));
×
343
                        } catch (SocketTimeoutException e) {
×
344
                                continue;
×
345
                        }
346
                }
347
        }
348

349
        /**
350
         * Receive data from the connection.
351
         *
352
         * @param timeout
353
         *            The timeout in milliseconds
354
         * @return The data received, in a little-endian buffer
355
         * @throws SocketTimeoutException
356
         *             If a timeout occurs before any data is received
357
         * @throws EOFException
358
         *             If the connection is closed
359
         * @throws IOException
360
         *             If an error occurs receiving the data
361
         * @throws InterruptedException
362
         *             If communications are interrupted.
363
         */
364
        public final ByteBuffer receive(int timeout)
365
                        throws SocketTimeoutException, IOException, InterruptedException {
366
                if (isClosed()) {
×
367
                        throw new EOFException();
×
368
                }
369
                return doReceive(timeout);
×
370
        }
371

372
        /**
373
         * Receive data from the connection.
374
         * <p>
375
         * This operation is <em>delegatable</em>; see
376
         * {@link DelegatingSCPConnection}.
377
         *
378
         * @param timeout
379
         *            The timeout in milliseconds
380
         * @return The data received, in a little-endian buffer
381
         * @throws SocketTimeoutException
382
         *             If a timeout occurs before any data is received
383
         * @throws IOException
384
         *             If an error occurs receiving the data
385
         * @throws InterruptedException
386
         *             If communications are interrupted.
387
         */
388
        @ForOverride
389
        protected ByteBuffer doReceive(int timeout)
390
                        throws SocketTimeoutException, IOException, InterruptedException {
391
                socket.setSoTimeout(timeout);
1✔
392
                var buffer = alloc(receivePacketSize);
1✔
393
                var pkt = new DatagramPacket(buffer.array(), receivePacketSize);
1✔
394
                socket.receive(pkt);
×
395
                buffer.position(pkt.getLength()).flip();
×
396
                if (log.isDebugEnabled()) {
×
397
                        log.debug("received data of length {} from {}", buffer.remaining(),
×
398
                                        pkt.getSocketAddress());
×
399
                        log.debug("message data: {}", describe(buffer));
×
400
                }
401
                return buffer;
×
402
        }
403

404
        @Override
405
        public final UDPPacket receiveWithAddress(int timeout)
406
                        throws SocketTimeoutException, IOException {
407
                if (isClosed()) {
×
408
                        throw new EOFException();
×
409
                }
410
                return doReceiveWithAddress(timeout);
×
411
        }
412

413
        /**
414
         * Receive data from the connection along with the address where the data
415
         * was received from.
416
         * <p>
417
         * This operation is <em>delegatable</em>; see
418
         * {@link DelegatingSCPConnection}.
419
         *
420
         * @param timeout
421
         *            The timeout in milliseconds
422
         * @return The datagram packet received
423
         * @throws SocketTimeoutException
424
         *             If a timeout occurs before any data is received
425
         * @throws IOException
426
         *             If an error occurs receiving the data
427
         */
428
        @ForOverride
429
        protected UDPPacket doReceiveWithAddress(int timeout)
430
                        throws SocketTimeoutException, IOException {
431
                socket.setSoTimeout(timeout);
×
432
                var buffer = alloc(receivePacketSize);
×
433
                var pkt = new DatagramPacket(buffer.array(), receivePacketSize);
×
434
                socket.receive(pkt);
×
435
                buffer.position(pkt.getLength()).flip();
×
436
                if (log.isDebugEnabled()) {
×
437
                        log.debug("received data of length {} from {}", buffer.remaining(),
×
438
                                        pkt.getSocketAddress());
×
439
                        log.debug("message data: {}", describe(buffer));
×
440
                }
441
                return new UDPPacket(buffer,
×
442
                                (InetSocketAddress) pkt.getSocketAddress());
×
443
        }
444

445
        /**
446
         * Receives a SpiNNaker message from this connection. Blocks until a message
447
         * has been received.
448
         *
449
         * @return the received message
450
         * @throws IOException
451
         *             If there is an error receiving the message
452
         * @throws InterruptedException
453
         *             If communications are interrupted.
454
         * @throws IllegalArgumentException
455
         *             If one of the fields of the SpiNNaker message is invalid
456
         */
457
        public T receiveMessage() throws IOException, InterruptedException {
458
                return receiveMessage(0);
×
459
        }
460

461
        /**
462
         * Receives a SpiNNaker message from this connection. Blocks until a message
463
         * has been received, or a timeout occurs.
464
         *
465
         * @param timeout
466
         *            The time in seconds to wait for the message to arrive, or
467
         *            until the connection is closed.
468
         * @return the received message
469
         * @throws IOException
470
         *             If there is an error receiving the message
471
         * @throws InterruptedException
472
         *             If communications are interrupted.
473
         * @throws SocketTimeoutException
474
         *             If there is a timeout during receiving
475
         * @throws IllegalArgumentException
476
         *             If one of the fields of the SpiNNaker message is invalid
477
         */
478
        public abstract T receiveMessage(int timeout)
479
                        throws IOException, InterruptedException;
480

481
        /**
482
         * Create the actual message to send.
483
         *
484
         * @param data
485
         *            The content of the message
486
         * @param remoteAddress
487
         *            Where to send it to
488
         * @return The full packet to send.
489
         */
490
        private static DatagramPacket formSendPacket(
491
                        ByteBuffer data, InetSocketAddress remoteAddress) {
492
                if (!data.hasArray()) {
1✔
493
                        // Yuck; must copy because can't touch the backing array
494
                        var buffer = new byte[data.remaining()];
×
495
                        data.duplicate().get(buffer);
×
496
                        return new DatagramPacket(buffer, 0, buffer.length, remoteAddress);
×
497
                } else {
498
                        // Unsafe, but we can get away with it as we send immediately
499
                        // and never actually write to the array
500
                        return new DatagramPacket(
1✔
501
                                        data.array(), data.arrayOffset() + data.position(),
1✔
502
                                        data.remaining(), remoteAddress);
1✔
503
                }
504
        }
505

506
        /**
507
         * Send data down this connection.
508
         *
509
         * @param data
510
         *            The data to be sent
511
         * @throws EOFException
512
         *             If the connection is closed
513
         * @throws IOException
514
         *             If there is an error sending the data
515
         * @throws IllegalStateException
516
         *             If the data packet doesn't hold a real message; zero-length
517
         *             messages are not supported!
518
         */
519
        public final void send(DatagramPacket data) throws IOException {
520
                send(wrap(data.getData(), data.getOffset(), data.getLength()));
×
521
        }
×
522

523
        /**
524
         * Send data down this connection.
525
         * <p>
526
         * This operation is <em>delegatable</em>; see
527
         * {@link DelegatingSCPConnection}.
528
         *
529
         * @param data
530
         *            The data to be sent; the position in this buffer will
531
         *            <em>not</em> be updated by this method
532
         * @throws IOException
533
         *             If there is an error sending the data
534
         */
535
        @ForOverride
536
        protected void doSend(ByteBuffer data) throws IOException {
537
                if (log.isDebugEnabled()) {
1✔
538
                        log.debug("sending data of length {} to {}", data.remaining(),
×
539
                                        getRemoteAddress());
×
540
                        log.debug("message data: {}", describe(data));
×
541
                }
542
                socket.send(formSendPacket(data, remoteAddress));
1✔
543
        }
1✔
544

545
        @Override
546
        public final void send(ByteBuffer data) throws IOException {
547
                if (!canSend) {
1✔
548
                        throw new IOException("Remote host and/or port not set; "
×
549
                                        + "data cannot be sent with this connection");
550
                }
551
                if (isClosed()) {
1✔
552
                        throw new EOFException();
×
553
                }
554
                if (!data.hasRemaining()) {
1✔
555
                        throw new IllegalStateException(
×
556
                                        "data buffer must have bytes to send");
557
                }
558
                doSend(data);
1✔
559
        }
1✔
560

561
        /**
562
         * Send data down this connection.
563
         *
564
         * @param data
565
         *            The data to be sent
566
         * @param address
567
         *            Where to send (must be non-{@code null})
568
         * @param port
569
         *            What port to send to (must be non-zero)
570
         * @throws EOFException
571
         *             If the connection is closed
572
         * @throws IOException
573
         *             If there is an error sending the data
574
         * @throws IllegalStateException
575
         *             If the data packet doesn't hold a real message; zero-length
576
         *             messages are not supported!
577
         */
578
        public final void sendTo(DatagramPacket data, InetAddress address, int port)
579
                        throws IOException {
580
                sendTo(wrap(data.getData(), data.getOffset(), data.getLength()),
×
581
                                address, port);
582
        }
×
583

584
        /**
585
         * Send data down this connection.
586
         *
587
         * @param data
588
         *            The data to be sent
589
         * @param address
590
         *            Where to send (must be non-{@code null})
591
         * @param port
592
         *            What port to send to (must be non-zero)
593
         * @throws EOFException
594
         *             If the connection is closed
595
         * @throws IOException
596
         *             If there is an error sending the data
597
         * @throws IllegalStateException
598
         *             If the data array doesn't hold a message; zero-length
599
         *             messages are not supported!
600
         */
601
        public final void sendTo(byte[] data, InetAddress address, int port)
602
                        throws IOException {
603
                sendTo(wrap(data, 0, data.length), address, port);
×
604
        }
×
605

606
        @Override
607
        public final void sendTo(ByteBuffer data, InetAddress address, int port)
608
                        throws IOException {
609
                if (isClosed()) {
×
610
                        throw new EOFException();
×
611
                }
612
                if (!data.hasRemaining()) {
×
613
                        throw new IllegalStateException(
×
614
                                        "data buffer must have bytes to send");
615
                }
616
                doSendTo(data, address, port);
×
617
        }
×
618

619
        /**
620
         * Send data down this connection.
621
         * <p>
622
         * This operation is <em>delegatable</em>; see
623
         * {@link DelegatingSCPConnection}.
624
         *
625
         * @param data
626
         *            The data to be sent
627
         * @param address
628
         *            Where to send (must be non-{@code null})
629
         * @param port
630
         *            What port to send to (must be non-zero)
631
         * @throws IOException
632
         *             If there is an error sending the data
633
         */
634
        @ForOverride
635
        protected void doSendTo(ByteBuffer data, InetAddress address, int port)
636
                        throws IOException {
637
                var addr = new InetSocketAddress(address, port);
×
638
                if (log.isDebugEnabled()) {
×
639
                        log.debug("sending data of length {} to {}", data.remaining(),
×
640
                                        addr);
641
                        log.debug("message data: {}", describe(data));
×
642
                }
643
                socket.send(formSendPacket(data, addr));
×
644
        }
×
645

646
        private String describe(ByteBuffer data) {
647
                int pos = data.position();
×
648
                return range(0, data.remaining())
×
649
                                .mapToObj(i -> hexbyte(data.get(pos + i)))
×
650
                                .collect(joining(",", "[", "]"));
×
651
        }
652

653
        @Override
654
        public boolean isConnected() {
655
                if (!canSend) {
×
656
                        return false;
×
657
                }
658
                for (int i = 0; i < PING_COUNT; i++) {
×
659
                        if (ping(remoteIPAddress) == 0) {
×
660
                                return true;
×
661
                        }
662
                }
663
                return false;
×
664
        }
665

666
        @Override
667
        @OverridingMethodsMustInvokeSuper
668
        public void close() throws IOException {
669
                if (socket == null) {
1✔
670
                        return;
×
671
                }
672
                socket.close();
1✔
673
        }
1✔
674

675
        @Override
676
        public boolean isClosed() {
677
                return socket.isClosed();
1✔
678
        }
679

680
        /**
681
         * Sends a port trigger message using a connection to (hopefully) open a
682
         * port in a NAT and/or firewall to allow incoming packets to be received.
683
         *
684
         * @param host
685
         *            The address of the SpiNNaker board to which the message should
686
         *            be sent
687
         * @throws IOException
688
         *             If anything goes wrong
689
         */
690
        public final void sendPortTriggerMessage(InetAddress host)
691
                        throws IOException {
692
                /*
693
                 * Set up the message so that no reply is expected and it is sent to an
694
                 * invalid port for SCAMP. The current version of SCAMP will reject this
695
                 * message, but then fail to send a response since the
696
                 * REPLY_NOT_EXPECTED flag is set (see scamp-3.c line 728 and 625-644)
697
                 */
698
                var triggerMessage = new SDPMessage(new SDPHeader(REPLY_NOT_EXPECTED,
×
699
                                new SDPLocation(0, 0, 0), RUNNING_COMMAND_SDP_PORT));
700
                sendTo(triggerMessage.getMessageData(null), host, SCP_SCAMP_PORT);
×
701
        }
×
702

703
        @Override
704
        public String toString() {
705
                return format("%s(%s <-%s-> %s)",
×
706
                                getClass().getSimpleName().replaceAll("^.*\\.", ""),
×
707
                                localAddr(), isClosed() ? "|" : "", remoteAddr());
×
708
        }
709
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc