Any kind of proper acknowledgement constitutes an alive connection for 30 seconds.

This commit is contained in:
Manevolent
2018-08-04 20:08:41 -04:00
parent 7239f3f7ef
commit 80a57784ef
5 changed files with 125 additions and 22 deletions
@@ -0,0 +1,45 @@
package com.github.manevolent.ts3j.event;
/*
* #%L
* TeamSpeak 3 Java API
* %%
* Copyright (C) 2014 Bert De Geyter
* %%
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* #L%
*/
import java.util.Map;
public class DisconnectedEvent extends BaseEvent {
public DisconnectedEvent(Map<String, String> map) {
super(map);
}
public int getReasonId() {
return getInt("reasonid");
}
@Override
public void fire(TS3Listener listener) {
listener.onDisconnected(this);
}
}
@@ -63,4 +63,6 @@ public interface TS3Listener {
void onClientChannelGroupChanged(ClientChannelGroupChangedEvent e); void onClientChannelGroupChanged(ClientChannelGroupChangedEvent e);
void onClientChanged(ClientUpdatedEvent e); void onClientChanged(ClientUpdatedEvent e);
void onDisconnected(DisconnectedEvent e);
} }
@@ -37,6 +37,8 @@ import java.util.concurrent.*;
public abstract class AbstractTeamspeakClientSocket public abstract class AbstractTeamspeakClientSocket
extends AbstractTeamspeakSocket extends AbstractTeamspeakSocket
implements TeamspeakClientSocket { implements TeamspeakClientSocket {
public static long TIMEOUT = 30000L;
private static final byte[] INIT1_MAC = new byte[] { private static final byte[] INIT1_MAC = new byte[] {
0x54, 0x53, 0x33, 0x49, 0x4E, 0x49, 0x54, 0x31 0x54, 0x53, 0x33, 0x49, 0x4E, 0x49, 0x54, 0x31
}; };
@@ -66,6 +68,7 @@ public abstract class AbstractTeamspeakClientSocket
private int clientId = 0; private int clientId = 0;
private int packetId = 0; private int packetId = 0;
private long lastResponse;
private long lastPing; private long lastPing;
private final NetworkReader networkReader = new NetworkReader(); private final NetworkReader networkReader = new NetworkReader();
@@ -396,7 +399,7 @@ public abstract class AbstractTeamspeakClientSocket
response = new PacketResponse( response = new PacketResponse(
networkPacket, networkPacket,
sendQueue, sendQueue,
networkPacket.getHeader().getType().canResend() ? 10 : 0 networkPacket.getHeader().getType().canResend() ? 30 : 0
) )
); );
} else } else
@@ -408,13 +411,14 @@ public abstract class AbstractTeamspeakClientSocket
// If necessary, fulfill promise for the packet acknowledgement or throw TimeoutException // If necessary, fulfill promise for the packet acknowledgement or throw TimeoutException
if (response != null) { if (response != null) {
while (response.getRetries() < response.getMaxTries()) { while (response.getRetries() < response.getMaxTries() &&
getState() != ClientConnectionState.DISCONNECTED) {
try { try {
if (getState() == ClientConnectionState.DISCONNECTED) if (getState() == ClientConnectionState.DISCONNECTED)
throw new IllegalStateException("no longer connected"); throw new IllegalStateException("no longer connected");
// Wait one cycle for an ACK // Wait one cycle for an ACK
response.waitForAcknowledgement(1000); response.waitForAcknowledgement(1000L);
break; break;
} catch (TimeoutException e) { } catch (TimeoutException e) {
if (response.willResend()) if (response.willResend())
@@ -530,9 +534,14 @@ public abstract class AbstractTeamspeakClientSocket
networkPacket = readNetworkPacket((int) timeout); networkPacket = readNetworkPacket((int) timeout);
} catch (SocketTimeoutException e) { } catch (SocketTimeoutException e) {
if (getState() == ClientConnectionState.CONNECTED) { if (getState() == ClientConnectionState.CONNECTED) {
if (System.currentTimeMillis() - lastResponse >= TIMEOUT) {
Ts3Debugging.debug("Connection timed out.");
setState(ClientConnectionState.DISCONNECTED); // Timeout
} else {
writePacket(new PacketBody4Ping(getRole().getOut())); writePacket(new PacketBody4Ping(getRole().getOut()));
lastPing = System.currentTimeMillis(); lastPing = System.currentTimeMillis();
} }
}
continue; continue;
} }
@@ -593,16 +602,28 @@ public abstract class AbstractTeamspeakClientSocket
switch (packet.getHeader().getType()) { switch (packet.getHeader().getType()) {
case ACK: case ACK:
response = sendQueue.get(((PacketBody6Ack)packet.getBody()).getPacketId()); response = sendQueue.get(((PacketBody6Ack)packet.getBody()).getPacketId());
if (response == null) if (response == null) {
Ts3Debugging.debug("Unrecognized ACK: " + ((PacketBody6Ack)packet.getBody()).getPacketId()); Ts3Debugging.debug("Unrecognized ACK: " + ((PacketBody6Ack) packet.getBody()).getPacketId());
} else {
lastResponse = System.currentTimeMillis();
}
handle = false; handle = false;
break; break;
case ACK_LOW: case ACK_LOW:
lastResponse = System.currentTimeMillis();
response = sendQueueLow.get(((PacketBody7AckLow)packet.getBody()).getPacketId()); response = sendQueueLow.get(((PacketBody7AckLow)packet.getBody()).getPacketId());
if (response == null) if (response == null) {
Ts3Debugging.debug("Unrecognized ACK_LOW: " + ((PacketBody6Ack)packet.getBody()).getPacketId()); Ts3Debugging.debug("Unrecognized ACK_LOW: " + ((PacketBody6Ack) packet.getBody()).getPacketId());
} else {
lastResponse = System.currentTimeMillis();
}
handle = false; handle = false;
break; break;
case PONG:
lastResponse = System.currentTimeMillis();
default: default:
handle = true; handle = true;
response = null; response = null;
@@ -9,9 +9,7 @@ import com.github.manevolent.ts3j.command.*;
import com.github.manevolent.ts3j.command.parameter.CommandSingleParameter; import com.github.manevolent.ts3j.command.parameter.CommandSingleParameter;
import com.github.manevolent.ts3j.command.response.AbstractCommandResponse; import com.github.manevolent.ts3j.command.response.AbstractCommandResponse;
import com.github.manevolent.ts3j.command.response.CommandResponse; import com.github.manevolent.ts3j.command.response.CommandResponse;
import com.github.manevolent.ts3j.event.ChannelListEvent; import com.github.manevolent.ts3j.event.*;
import com.github.manevolent.ts3j.event.TS3Event;
import com.github.manevolent.ts3j.event.TS3Listener;
import com.github.manevolent.ts3j.identity.LocalIdentity; import com.github.manevolent.ts3j.identity.LocalIdentity;
import com.github.manevolent.ts3j.identity.Uid; import com.github.manevolent.ts3j.identity.Uid;
import com.github.manevolent.ts3j.protocol.NetworkPacket; import com.github.manevolent.ts3j.protocol.NetworkPacket;
@@ -58,7 +56,7 @@ public class LocalTeamspeakClientSocket
private List<TS3Listener> listeners = new LinkedList<>(); private List<TS3Listener> listeners = new LinkedList<>();
private int acceptingReturnCode = 0; private int acceptingReturnCode = Integer.MAX_VALUE;
private int lastReturnCode = 0; private int lastReturnCode = 0;
private Integer serverId = null; private Integer serverId = null;
@@ -80,6 +78,27 @@ public class LocalTeamspeakClientSocket
}); });
namedProcessors.put("channellistfinished", new ChannelListFinishedHandler()); namedProcessors.put("channellistfinished", new ChannelListFinishedHandler());
namedProcessors.put("notifyclientleftview", new CommandProcessor() {
@Override
public void process(AbstractTeamspeakClientSocket client, SingleCommand command)
throws CommandProcessException {
int clientId = Integer.parseInt(command.get("clid").getValue());
if (clientId == getClientId()) {
try {
setState(ClientConnectionState.DISCONNECTED);
} catch (Exception e) {
throw new RuntimeException(e);
}
for (TS3Listener listener : listeners)
listener.onDisconnected(new DisconnectedEvent(command.toMap()));
} else {
for (TS3Listener listener : listeners)
listener.onClientLeave(new ClientLeaveEvent(command.toMap()));
}
}
});
} }
public void addListener(TS3Listener listener) { public void addListener(TS3Listener listener) {
@@ -164,6 +183,9 @@ public class LocalTeamspeakClientSocket
setClientId(0); setClientId(0);
setPacketId(0); setPacketId(0);
setGenerationId(0); setGenerationId(0);
serverId = null;
awaitingCommands.clear();
acceptingReturnCode = lastReturnCode = 0;
setOption("client.hostname", remote.getHostString()); setOption("client.hostname", remote.getHostString());
@@ -745,4 +767,24 @@ public class LocalTeamspeakClientSocket
executeCommand(command).complete(); executeCommand(command).complete();
} }
public void disconnect()
throws
IOException, TimeoutException,
ExecutionException, InterruptedException {
switch (getState()) {
case CONNECTING:
case RETRIEVING_DATA:
waitForState(ClientConnectionState.CONNECTED, 10000L);
case CONNECTED:
writePacket(new PacketBody2Command(
ProtocolRole.CLIENT,
new SingleCommand("clientdisconnect", ProtocolRole.CLIENT))
);
break;
}
}
} }
@@ -40,16 +40,9 @@ public class ServerConnectionTest {
assertEquals(client.getState(), ClientConnectionState.CONNECTED); assertEquals(client.getState(), ClientConnectionState.CONNECTED);
client.sendServerMessage("Heyyyy"); client.disconnect();
while (true) { client.waitForState(ClientConnectionState.DISCONNECTED, 10000L);
Thread.sleep(1000L);
for (Channel channel : client.listChannels()) {
Ts3Debugging.debug(channel.getName());
}
}
} catch (Throwable ex) { } catch (Throwable ex) {
ex.printStackTrace(); ex.printStackTrace();
} }