Skip to content

Commit c2cbf61

Browse files
authored
feat: Option to send to the last received from address. (#305)
* feat: Option to send to the last received from address. * Cache the last used pair.
1 parent f3db829 commit c2cbf61

File tree

3 files changed

+88
-10
lines changed

3 files changed

+88
-10
lines changed

src/main/java/org/ice4j/ice/Component.java

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import java.beans.*;
2121
import java.io.*;
22+
import java.lang.ref.*;
2223
import java.net.*;
2324
import java.util.*;
2425
import java.util.concurrent.*;
@@ -160,6 +161,17 @@ public class Component
160161
*/
161162
private BufferHandler bufferCallback = null;
162163

164+
/**
165+
* The remote address of the last received payload packet.
166+
*/
167+
private SocketAddress lastReceivedFrom = null;
168+
169+
/**
170+
* A reference to the last {@link CandidatePair} that was used to send a packet to. If the remote address that
171+
* we receive payload from changes, this reference is cleared.
172+
*/
173+
private WeakReference<CandidatePair> lastUsedPair = new WeakReference<>(null);
174+
163175
/**
164176
* Creates a new <tt>Component</tt> with the specified <tt>componentID</tt>
165177
* as a child of the specified <tt>IceMediaStream</tt>.
@@ -1207,17 +1219,59 @@ public Logger getLogger()
12071219
public void send(byte[] buffer, int offset, int length)
12081220
throws IOException
12091221
{
1210-
CandidatePair pair = getSelectedPair();
1222+
CandidatePair pair = lastUsedPair.get();
12111223
if (pair == null)
12121224
{
1213-
logger.debug("No selected pair, will try valid for sending");
1214-
pair = parentStream.getValidPair(this);
1215-
if (pair == null)
1225+
pair = findPair(lastReceivedFrom);
1226+
lastUsedPair = new WeakReference<>(pair);
1227+
}
1228+
1229+
if (pair == null)
1230+
{
1231+
throw new IOException("No valid pair.");
1232+
}
1233+
1234+
AddressAndSocket addressAndSocket = getAddressAndSocket(pair);
1235+
if (addressAndSocket == null)
1236+
{
1237+
throw new IOException("No valid socket.");
1238+
}
1239+
1240+
DatagramPacket p = new DatagramPacket(buffer, offset, length);
1241+
p.setSocketAddress(addressAndSocket.remoteAddress);
1242+
addressAndSocket.socket.send(p);
1243+
}
1244+
1245+
private CandidatePair findPair(SocketAddress remoteAddress)
1246+
{
1247+
CandidatePair pair = null;
1248+
if (AgentConfig.config.getSendToLastReceivedFromAddress() && remoteAddress != null)
1249+
{
1250+
for (CandidatePair keepAlivePair : keepAlivePairs)
12161251
{
1217-
throw new IOException("No valid pair.");
1252+
if (keepAlivePair.getState() == CandidatePairState.SUCCEEDED
1253+
&& remoteAddress.equals(keepAlivePair.getRemoteCandidate().getTransportAddress()))
1254+
{
1255+
pair = keepAlivePair;
1256+
break;
1257+
}
12181258
}
12191259
}
1260+
if (pair == null)
1261+
{
1262+
pair = getSelectedPair();
1263+
}
1264+
if (pair == null)
1265+
{
1266+
logger.debug("No selected pair, will try valid for sending");
1267+
pair = parentStream.getValidPair(this);
1268+
}
1269+
1270+
return pair;
1271+
}
12201272

1273+
private AddressAndSocket getAddressAndSocket(CandidatePair pair)
1274+
{
12211275
LocalCandidate localCandidate = pair.getLocalCandidate();
12221276
if (localCandidate != null && localCandidate.getBase() != null)
12231277
{
@@ -1229,12 +1283,9 @@ public void send(byte[] buffer, int offset, int length)
12291283

12301284
if (socket == null)
12311285
{
1232-
throw new IOException("No socket found to send on.");
1286+
return null;
12331287
}
1234-
1235-
DatagramPacket p = new DatagramPacket(buffer, offset, length);
1236-
p.setSocketAddress(remoteAddress);
1237-
socket.send(p);
1288+
return new AddressAndSocket(remoteAddress, socket);
12381289
}
12391290

12401291
/**
@@ -1256,6 +1307,12 @@ public void handleBuffer(@NotNull Buffer buffer)
12561307

12571308
try
12581309
{
1310+
SocketAddress remoteAddress = buffer.getRemoteAddress();
1311+
if (remoteAddress == null || !remoteAddress.equals(lastReceivedFrom))
1312+
{
1313+
lastUsedPair = new WeakReference<>(null);
1314+
lastReceivedFrom = buffer.getRemoteAddress();
1315+
}
12591316
bufferCallback.handleBuffer(buffer);
12601317
}
12611318
catch (Exception e)
@@ -1273,4 +1330,16 @@ public void setBufferCallback(BufferHandler bufferCallback)
12731330
{
12741331
this.bufferCallback = bufferCallback;
12751332
}
1333+
1334+
private static class AddressAndSocket
1335+
{
1336+
private final SocketAddress remoteAddress;
1337+
private final IceSocketWrapper socket;
1338+
1339+
private AddressAndSocket(SocketAddress remoteAddress, IceSocketWrapper socket)
1340+
{
1341+
this.remoteAddress = remoteAddress;
1342+
this.socket = socket;
1343+
}
1344+
}
12761345
}

src/main/kotlin/org/ice4j/ice/AgentConfig.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ class AgentConfig {
8282
"ice4j.use-component-socket".from(configSource)
8383
}
8484

85+
val sendToLastReceivedFromAddress: Boolean by config {
86+
"ice4j.send-to-last-received-from-address".from(configSource)
87+
}
88+
8589
companion object {
8690
@JvmField
8791
val config = AgentConfig()

src/main/resources/reference.conf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ ice4j {
1616
// the socket instance from the desired [CandidatePair] must be used.
1717
use-component-socket = true
1818

19+
// Instead of always using the selected pair to send payload, use the last valid pair that we've received payload on.
20+
// This effectively allows the remote side to decide dynamically which of the keep-alive pairs to be used (by sending
21+
// payload on it).
22+
send-to-last-received-from-address = false
23+
1924
// Whether remote IP addresses should be redacted in log messages
2025
redact-remote-addresses = false
2126

0 commit comments

Comments
 (0)