1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2024-12-23 21:17:58 +01:00

Jingle Session Establishment Refactoring

git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@7457 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
Thiago Camargo 2007-03-11 22:11:42 +00:00 committed by thiago
parent 4a86390b17
commit 41fbc3906a
7 changed files with 289 additions and 72 deletions

View file

@ -496,7 +496,7 @@ public class JingleManager implements JingleSessionListener {
jingleSession.removeListener(this); jingleSession.removeListener(this);
jingleSessions.remove(jingleSession); jingleSessions.remove(jingleSession);
jingleSession.close(); jingleSession.close();
System.err.println("Declined"); System.err.println("Declined:"+reason);
} }
public void sessionRedirected(String redirection, JingleSession jingleSession) { public void sessionRedirected(String redirection, JingleSession jingleSession) {

View file

@ -98,8 +98,8 @@ public class ICECandidate extends TransportCandidate implements Comparable {
* @param type type as defined in ICE-12 * @param type type as defined in ICE-12
*/ */
public ICECandidate(String ip, int generation, int network, public ICECandidate(String ip, int generation, int network,
String password, int port, String username, String password, int port, String username,
int preference, String type) { int preference, String type) {
super(ip, port, generation); super(ip, port, generation);
proto = Protocol.UDP; proto = Protocol.UDP;
@ -252,50 +252,61 @@ public class ICECandidate extends TransportCandidate implements Comparable {
Thread checkThread = new Thread(new Runnable() { Thread checkThread = new Thread(new Runnable() {
public void run() { public void run() {
boolean isUsable = false;
final TestResult result = new TestResult();
// Media Proxy don´t have Echo features. // Media Proxy don´t have Echo features.
// If its a relayed candidate we assumpt that is Checked. // If its a relayed candidate we assumpt that is NOT Valid while other candidates still being checked.
// The negotiator MUST add then in the correct situations
if (getType().equals("relay")) { if (getType().equals("relay")) {
try { triggerCandidateChecked(false);
Thread.sleep(TransportNegotiator.CANDIDATES_ACCEPT_PERIOD*2);
}
catch (InterruptedException e) {
// Do Nothing
}
triggerCandidateChecked(true);
return; return;
} }
ResultListener resultListener = new ResultListener() {
public void testFinished(TestResult testResult) {
if (testResult.isReachable()) {
result.setResult(true);
}
}
};
for (TransportCandidate candidate : localCandidates) { for (TransportCandidate candidate : localCandidates) {
CandidateEcho echo = candidate.getCandidateEcho(); CandidateEcho echo = candidate.getCandidateEcho();
if (echo != null) { if (echo != null) {
try { if (candidate instanceof ICECandidate) {
InetAddress address = InetAddress.getByName(getIp()); ICECandidate iceCandidate = (ICECandidate) candidate;
if (echo.test(address, getPort(),2000)) isUsable = true; if (!iceCandidate.getType().equals("relay")) {
if (isUsable) break; try {
} echo.addResultListener(resultListener);
catch (UnknownHostException e) { InetAddress address = InetAddress.getByName(getIp());
e.printStackTrace(); echo.testASync(address, getPort());
}
catch (UnknownHostException e) {
e.printStackTrace();
}
}
} }
} }
} }
if (isUsable) { for (int i = 0; i < 10 && !result.isReachable(); i++)
System.out.println("Checked using UDP Echo:" + getLocalIp()); try {
triggerCandidateChecked(isUsable); System.err.println(i);
return; Thread.sleep(300);
}
catch (InterruptedException e) {
e.printStackTrace();
}
for (TransportCandidate candidate : localCandidates) {
CandidateEcho echo = candidate.getCandidateEcho();
if (echo != null) {
echo.removeResultListener(resultListener);
}
} }
InetAddress candAddress; triggerCandidateChecked(result.isReachable());
try {
candAddress = InetAddress.getByName(getIp());
isUsable = candAddress.isReachable(TransportResolver.CHECK_TIMEOUT);
}
catch (Exception e) {
isUsable = false;
}
triggerCandidateChecked(isUsable);
//TODO candidate is being checked trigger //TODO candidate is being checked trigger
//candidatesChecking.remove(cand); //candidatesChecking.remove(cand);
@ -327,14 +338,16 @@ public class ICECandidate extends TransportCandidate implements Comparable {
if (other.getChannel() != null) { if (other.getChannel() != null) {
return false; return false;
} }
} else if (!getChannel().equals(other.getChannel())) { }
else if (!getChannel().equals(other.getChannel())) {
return false; return false;
} }
if (getId() == null) { if (getId() == null) {
if (other.getId() != null) { if (other.getId() != null) {
return false; return false;
} }
} else if (!getId().equals(other.getId())) { }
else if (!getId().equals(other.getId())) {
return false; return false;
} }
if (getNetwork() != other.getNetwork()) { if (getNetwork() != other.getNetwork()) {
@ -344,7 +357,8 @@ public class ICECandidate extends TransportCandidate implements Comparable {
if (other.getPassword() != null) { if (other.getPassword() != null) {
return false; return false;
} }
} else if (!getPassword().equals(other.password)) { }
else if (!getPassword().equals(other.password)) {
return false; return false;
} }
if (getPreference() != other.getPreference()) { if (getPreference() != other.getPreference()) {
@ -354,14 +368,16 @@ public class ICECandidate extends TransportCandidate implements Comparable {
if (other.getProto() != null) { if (other.getProto() != null) {
return false; return false;
} }
} else if (!getProto().equals(other.getProto())) { }
else if (!getProto().equals(other.getProto())) {
return false; return false;
} }
if (getUsername() == null) { if (getUsername() == null) {
if (other.getUsername() != null) { if (other.getUsername() != null) {
return false; return false;
} }
} else if (!getUsername().equals(other.getUsername())) { }
else if (!getUsername().equals(other.getUsername())) {
return false; return false;
} }
return true; return true;
@ -370,9 +386,11 @@ public class ICECandidate extends TransportCandidate implements Comparable {
public boolean isNull() { public boolean isNull() {
if (super.isNull()) { if (super.isNull()) {
return true; return true;
} else if (getProto().isNull()) { }
else if (getProto().isNull()) {
return true; return true;
} else if (getChannel().isNull()) { }
else if (getChannel().isNull()) {
return true; return true;
} }
return false; return false;
@ -391,7 +409,8 @@ public class ICECandidate extends TransportCandidate implements Comparable {
ICECandidate tc = (ICECandidate) arg; ICECandidate tc = (ICECandidate) arg;
if (getPreference() < tc.getPreference()) { if (getPreference() < tc.getPreference()) {
return -1; return -1;
} else if (getPreference() > tc.getPreference()) { }
else if (getPreference() > tc.getPreference()) {
return 1; return 1;
} }
} }

View file

@ -0,0 +1,32 @@
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright 2003-2005 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.jingle.nat;
/**
* Listener for ECHO Test Results
*
* @author Thiago Camargo
*/
public interface ResultListener {
public void testFinished(TestResult result);
}

View file

@ -0,0 +1,72 @@
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2002-2006 Jive Software. All rights reserved.
* ====================================================================
* The Jive Software License (based on Apache Software License, Version 1.1)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by
* Jive Software (http://www.jivesoftware.com)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Smack" and "Jive Software" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please
* contact webmaster@jivesoftware.com.
*
* 5. Products derived from this software may not be called "Smack",
* nor may "Smack" appear in their name, without prior written
* permission of Jive Software.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*/
package org.jivesoftware.smackx.jingle.nat;
/**
* Result of an ECHO Test
*
* @author Thiago Camargo
*/
public class TestResult {
private boolean result = false;
public boolean isReachable() {
return result;
}
public void setResult(boolean result) {
this.result = result;
}
}

View file

@ -649,12 +649,13 @@ public abstract class TransportCandidate {
DatagramSocket socket = null; DatagramSocket socket = null;
byte password[] = null; byte password[] = null;
List<DatagramListener> listeners = new ArrayList<DatagramListener>(); List<DatagramListener> listeners = new ArrayList<DatagramListener>();
List<ResultListener> resultListeners = new ArrayList<ResultListener>();
boolean enabled = true; boolean enabled = true;
boolean ended = false; boolean ended = false;
long tries = 10; long tries = 10;
public CandidateEcho(TransportCandidate candidate) throws UnknownHostException, SocketException { public CandidateEcho(TransportCandidate candidate) throws UnknownHostException, SocketException {
this.socket = new DatagramSocket(candidate.getPort(), InetAddress.getByName("0.0.0.0")); this.socket = new DatagramSocket(candidate.getPort(), InetAddress.getByName(candidate.getLocalIp()));
Random r = new Random(); Random r = new Random();
password = longToByteArray((Math.abs(r.nextLong()))); password = longToByteArray((Math.abs(r.nextLong())));
} }
@ -712,27 +713,30 @@ public abstract class TransportCandidate {
socket.close(); socket.close();
} }
private void fireTestResult(TestResult testResult) {
for (ResultListener resultListener : resultListeners)
resultListener.testFinished(testResult);
}
public boolean test(final InetAddress address, final int port) { public boolean test(final InetAddress address, final int port) {
return test(address, port, 2000); return test(address, port, 2000);
} }
public boolean test(final InetAddress address, final int port, int timeout) { public boolean test(final InetAddress address, final int port, int timeout) {
ended=false; ended = false;
final TestResults testResults = new TestResults(); final TestResult testResult = new TestResult();
DatagramListener listener = new DatagramListener() { DatagramListener listener = new DatagramListener() {
public boolean datagramReceived(DatagramPacket datagramPacket) { public boolean datagramReceived(DatagramPacket datagramPacket) {
if (datagramPacket.getAddress().equals(address) && datagramPacket.getPort() == port) { if (datagramPacket.getAddress().equals(address) && datagramPacket.getPort() == port) {
if (Arrays.equals(datagramPacket.getData(), password)) { if (Arrays.equals(datagramPacket.getData(), password)) {
testResults.setResult(true); testResult.setResult(true);
ended = true; ended = true;
return true; return true;
} }
} }
testResults.setResult(false);
ended = true;
return false; return false;
} }
}; };
@ -765,7 +769,59 @@ public abstract class TransportCandidate {
this.removeListener(listener); this.removeListener(listener);
return testResults.isReachable(); return testResult.isReachable();
}
public void testASync(final InetAddress address, final int port) {
Thread thread = new Thread(new Runnable() {
public void run() {
DatagramListener listener = new DatagramListener() {
public boolean datagramReceived(DatagramPacket datagramPacket) {
if (datagramPacket.getAddress().equals(address) && datagramPacket.getPort() == port) {
if (Arrays.equals(datagramPacket.getData(), password)) {
TestResult testResult = new TestResult();
testResult.setResult(true);
fireTestResult(testResult);
ended = true;
return true;
}
}
return false;
}
};
addListener(listener);
DatagramPacket packet = new DatagramPacket(password, password.length);
packet.setAddress(address);
packet.setPort(port);
long delay = 200;
try {
for (int i = 0; i < tries; i++) {
socket.send(packet);
if (ended) break;
try {
Thread.sleep(delay);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
catch (IOException e) {
// Do Nothing
}
removeListener(listener);
}
});
thread.start();
} }
public void addListener(DatagramListener listener) { public void addListener(DatagramListener listener) {
@ -776,17 +832,12 @@ public abstract class TransportCandidate {
listeners.remove(listener); listeners.remove(listener);
} }
public class TestResults { public void addResultListener(ResultListener resultListener) {
resultListeners.add(resultListener);
}
private boolean result=false; public void removeResultListener(ResultListener resultListener) {
resultListeners.remove(resultListener);
public boolean isReachable() {
return result;
}
public void setResult(boolean result) {
this.result = result;
}
} }
} }

View file

@ -52,7 +52,7 @@ public abstract class TransportNegotiator extends JingleNegotiator {
// The time we give to the candidates check before we accept or decline the // The time we give to the candidates check before we accept or decline the
// transport (in milliseconds) // transport (in milliseconds)
public final static int CANDIDATES_ACCEPT_PERIOD = 3000; public final static int CANDIDATES_ACCEPT_PERIOD = 4000;
// The session this nenotiator belongs to // The session this nenotiator belongs to
private final JingleSession session; private final JingleSession session;
@ -69,6 +69,9 @@ public abstract class TransportNegotiator extends JingleNegotiator {
// Valid remote candidates // Valid remote candidates
private final List<TransportCandidate> validRemoteCandidates = new ArrayList<TransportCandidate>(); private final List<TransportCandidate> validRemoteCandidates = new ArrayList<TransportCandidate>();
// Accepted Remote Candidates
private final List<TransportCandidate> acceptedRemoteCandidates = new ArrayList<TransportCandidate>();
// The best local candidate we have offered (and accepted by the other part) // The best local candidate we have offered (and accepted by the other part)
private TransportCandidate acceptedLocalCandidate; private TransportCandidate acceptedLocalCandidate;
@ -165,10 +168,6 @@ public abstract class TransportNegotiator extends JingleNegotiator {
public void close() { public void close() {
super.close(); super.close();
for (TransportCandidate candidate : offeredCandidates)
if (candidate.getCandidateEcho() != null)
candidate.removeCandidateEcho();
} }
public List<TransportCandidate> getOfferedCandidates() { public List<TransportCandidate> getOfferedCandidates() {
@ -283,10 +282,10 @@ public abstract class TransportNegotiator extends JingleNegotiator {
// Sleep for some time, waiting for the candidates checks // Sleep for some time, waiting for the candidates checks
int totalTime = (CANDIDATES_ACCEPT_PERIOD + (TransportResolver.CHECK_TIMEOUT * (resolver.getCandidatesList().size() + 1))); int totalTime = (CANDIDATES_ACCEPT_PERIOD + TransportResolver.CHECK_TIMEOUT);
int tries = (int) Math.ceil(totalTime / 1000); int tries = (int) Math.ceil(totalTime / 1000);
for (int i = 0; i < tries; i++) { for (int i = 0; i < tries - 2; i++) {
try { try {
Thread.sleep(1000); Thread.sleep(1000);
} }
@ -301,18 +300,64 @@ public abstract class TransportNegotiator extends JingleNegotiator {
if (bestRemote != null && (state == pending || state == active)) { if (bestRemote != null && (state == pending || state == active)) {
// Accepting the remote candidate // Accepting the remote candidate
Jingle jout = new Jingle(Jingle.Action.TRANSPORTACCEPT); if (!acceptedRemoteCandidates.contains(bestRemote)) {
jout.addTransport(getJingleTransport(bestRemote)); Jingle jout = new Jingle(Jingle.Action.TRANSPORTACCEPT);
jout.addTransport(getJingleTransport(bestRemote));
// Send the packet
js.sendFormattedJingle(jin, jout);
// Send the packet
js.sendFormattedJingle(jin, jout);
acceptedRemoteCandidates.add(bestRemote);
}
if (isEstablished()) { if (isEstablished()) {
setState(active); setState(active);
break; break;
} }
} }
} }
// Once we are in pending state, look for any valid remote
// candidate, and send an "accept" if we have one...
TransportCandidate bestRemote = getBestRemoteCandidate();
if (bestRemote == null) {
for (TransportCandidate candidate : remoteCandidates) {
if (candidate instanceof ICECandidate) {
ICECandidate iceCandidate = (ICECandidate) candidate;
if (iceCandidate.getType().equals("relay")) {
//TODO Check if the relay is reacheable
addValidRemoteCandidate(iceCandidate);
}
}
}
}
for (int i = 0; i < 2; i++) {
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
bestRemote = getBestRemoteCandidate();
State state = getState();
if (bestRemote != null && (state == pending || state == active)) {
if (!acceptedRemoteCandidates.contains(bestRemote)) {
Jingle jout = new Jingle(Jingle.Action.TRANSPORTACCEPT);
jout.addTransport(getJingleTransport(bestRemote));
// Send the packet
js.sendFormattedJingle(jin, jout);
acceptedRemoteCandidates.add(bestRemote);
}
if (isEstablished()) {
setState(active);
break;
}
}
}
if (getState() == null || !getState().equals(active)) { if (getState() == null || !getState().equals(active)) {
try { try {
session.terminate(); session.terminate();
@ -698,9 +743,7 @@ public abstract class TransportNegotiator extends JingleNegotiator {
setAcceptedLocalCandidate(cand); setAcceptedLocalCandidate(cand);
if (isEstablished()) { if (isEstablished()) {
System.out.println("SET ACTIVE"); System.out.println("SET ACTIVE");
setState(active); setState(active);
} }
} }

View file

@ -89,7 +89,7 @@ public abstract class TransportResolver {
public Type type = Type.rawupd; public Type type = Type.rawupd;
// the time, in milliseconds, before a check aborts // the time, in milliseconds, before a check aborts
public static final int CHECK_TIMEOUT = 4000; public static final int CHECK_TIMEOUT = 5000;
// Listeners for events // Listeners for events
private final ArrayList<TransportResolverListener> listeners = new ArrayList<TransportResolverListener>(); private final ArrayList<TransportResolverListener> listeners = new ArrayList<TransportResolverListener>();