2013-10-25 15:19:00 +00:00
|
|
|
/*
|
2013-12-20 19:25:49 +00:00
|
|
|
* This is the source code of Telegram for Android v. 1.3.2.
|
2013-10-25 15:19:00 +00:00
|
|
|
* It is licensed under GNU GPL v. 2 or later.
|
|
|
|
* You should have received a copy of the license in this archive (see LICENSE).
|
|
|
|
*
|
|
|
|
* Copyright Nikolai Kudashov, 2013.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package org.telegram.messenger;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.net.InetSocketAddress;
|
2014-02-11 14:32:09 +00:00
|
|
|
import java.net.SocketTimeoutException;
|
2013-10-25 15:19:00 +00:00
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
import java.nio.ByteOrder;
|
|
|
|
import java.util.Timer;
|
|
|
|
import java.util.TimerTask;
|
|
|
|
|
|
|
|
import jawnae.pyronet.PyroClient;
|
|
|
|
import jawnae.pyronet.PyroSelector;
|
2013-12-20 19:25:49 +00:00
|
|
|
import jawnae.pyronet.PyroClientAdapter;
|
2013-10-25 15:19:00 +00:00
|
|
|
|
|
|
|
public class TcpConnection extends PyroClientAdapter {
|
|
|
|
public enum TcpConnectionState {
|
|
|
|
TcpConnectionStageIdle,
|
|
|
|
TcpConnectionStageConnecting,
|
|
|
|
TcpConnectionStageReconnecting,
|
|
|
|
TcpConnectionStageConnected,
|
|
|
|
TcpConnectionStageSuspended
|
|
|
|
}
|
|
|
|
|
|
|
|
public abstract static interface TcpConnectionDelegate {
|
|
|
|
public abstract void tcpConnectionClosed(TcpConnection connection);
|
|
|
|
public abstract void tcpConnectionConnected(TcpConnection connection);
|
|
|
|
public abstract void tcpConnectionQuiackAckReceived(TcpConnection connection, int ack);
|
2014-02-28 22:28:25 +00:00
|
|
|
public abstract void tcpConnectionReceivedData(TcpConnection connection, ByteBufferDesc data, int length);
|
2013-12-20 19:25:49 +00:00
|
|
|
public abstract void tcpConnectionProgressChanged(TcpConnection connection, long messageId, int currentSize, int length);
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private static PyroSelector selector;
|
|
|
|
private PyroClient client;
|
|
|
|
public TcpConnectionState connectionState;
|
|
|
|
public volatile int channelToken = 0;
|
|
|
|
private String hostAddress;
|
|
|
|
private int hostPort;
|
2013-12-20 19:25:49 +00:00
|
|
|
private int datacenterId;
|
2013-10-25 15:19:00 +00:00
|
|
|
private int failedConnectionCount;
|
|
|
|
public TcpConnectionDelegate delegate;
|
2014-02-28 22:28:25 +00:00
|
|
|
private ByteBufferDesc restOfTheData;
|
2013-12-20 19:25:49 +00:00
|
|
|
private long lastMessageId = 0;
|
|
|
|
private boolean hasSomeDataSinceLastConnect = false;
|
|
|
|
private int willRetryConnectCount = 5;
|
|
|
|
private boolean isNextPort = false;
|
2014-02-04 18:36:55 +00:00
|
|
|
private final Integer timerSync = 1;
|
2014-02-11 14:32:09 +00:00
|
|
|
private boolean wasConnected;
|
2014-02-28 22:28:25 +00:00
|
|
|
private int lastPacketLength;
|
2013-10-25 15:19:00 +00:00
|
|
|
|
|
|
|
public int transportRequestClass;
|
|
|
|
|
|
|
|
private boolean firstPacket;
|
|
|
|
|
|
|
|
private Timer reconnectTimer;
|
|
|
|
|
2013-12-20 19:25:49 +00:00
|
|
|
public TcpConnection(int did) {
|
2013-10-25 15:19:00 +00:00
|
|
|
if (selector == null) {
|
|
|
|
selector = new PyroSelector();
|
|
|
|
selector.spawnNetworkThread("network thread");
|
2014-02-28 22:28:25 +00:00
|
|
|
BuffersStorage storage = BuffersStorage.Instance;
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
2013-12-20 19:25:49 +00:00
|
|
|
datacenterId = did;
|
2013-10-25 15:19:00 +00:00
|
|
|
connectionState = TcpConnectionState.TcpConnectionStageIdle;
|
|
|
|
}
|
|
|
|
|
|
|
|
static volatile Integer nextChannelToken = 1;
|
|
|
|
static int generateChannelToken() {
|
|
|
|
return nextChannelToken++;
|
|
|
|
}
|
|
|
|
|
2013-12-20 19:25:49 +00:00
|
|
|
public int getDatacenterId() {
|
|
|
|
return datacenterId;
|
|
|
|
}
|
|
|
|
|
2013-10-25 15:19:00 +00:00
|
|
|
public void connect() {
|
|
|
|
selector.scheduleTask(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2013-12-20 19:25:49 +00:00
|
|
|
if ((connectionState == TcpConnectionState.TcpConnectionStageConnected || connectionState == TcpConnectionState.TcpConnectionStageConnecting) && client != null) {
|
2013-10-25 15:19:00 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
connectionState = TcpConnectionState.TcpConnectionStageConnecting;
|
|
|
|
try {
|
2013-12-22 23:47:35 +00:00
|
|
|
try {
|
2014-02-04 18:36:55 +00:00
|
|
|
synchronized (timerSync) {
|
|
|
|
if (reconnectTimer != null) {
|
|
|
|
reconnectTimer.cancel();
|
|
|
|
reconnectTimer = null;
|
|
|
|
}
|
2013-12-22 23:47:35 +00:00
|
|
|
}
|
|
|
|
} catch (Exception e2) {
|
|
|
|
FileLog.e("tmessages", e2);
|
|
|
|
}
|
2013-12-20 19:25:49 +00:00
|
|
|
Datacenter datacenter = ConnectionsManager.Instance.datacenterWithId(datacenterId);
|
|
|
|
hostAddress = datacenter.getCurrentAddress();
|
|
|
|
hostPort = datacenter.getCurrentPort();
|
|
|
|
FileLog.d("tmessages", String.format(TcpConnection.this + " Connecting (%s:%d)", hostAddress, hostPort));
|
2013-10-25 15:19:00 +00:00
|
|
|
firstPacket = true;
|
2014-02-28 22:28:25 +00:00
|
|
|
if (restOfTheData != null) {
|
|
|
|
BuffersStorage.Instance.reuseFreeBuffer(restOfTheData);
|
|
|
|
restOfTheData = null;
|
|
|
|
}
|
|
|
|
lastPacketLength = 0;
|
2014-02-11 14:32:09 +00:00
|
|
|
wasConnected = false;
|
2013-12-20 19:25:49 +00:00
|
|
|
hasSomeDataSinceLastConnect = false;
|
2013-10-25 15:19:00 +00:00
|
|
|
if (client != null) {
|
|
|
|
client.removeListener(TcpConnection.this);
|
|
|
|
client.dropConnection();
|
2013-12-20 19:25:49 +00:00
|
|
|
client = null;
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
client = selector.connect(new InetSocketAddress(hostAddress, hostPort));
|
|
|
|
client.addListener(TcpConnection.this);
|
2013-12-20 19:25:49 +00:00
|
|
|
if (isNextPort) {
|
|
|
|
client.setTimeout(8000);
|
|
|
|
} else {
|
2014-02-11 14:32:09 +00:00
|
|
|
client.setTimeout(15000);
|
2013-12-20 19:25:49 +00:00
|
|
|
}
|
2013-10-25 15:19:00 +00:00
|
|
|
selector.wakeup();
|
|
|
|
} catch (Exception e) {
|
2013-12-20 19:25:49 +00:00
|
|
|
try {
|
2014-02-04 18:36:55 +00:00
|
|
|
synchronized (timerSync) {
|
|
|
|
if (reconnectTimer != null) {
|
|
|
|
reconnectTimer.cancel();
|
|
|
|
reconnectTimer = null;
|
|
|
|
}
|
2013-12-20 19:25:49 +00:00
|
|
|
}
|
|
|
|
} catch (Exception e2) {
|
|
|
|
FileLog.e("tmessages", e2);
|
|
|
|
}
|
|
|
|
connectionState = TcpConnectionState.TcpConnectionStageReconnecting;
|
|
|
|
if (delegate != null) {
|
2014-02-04 18:36:55 +00:00
|
|
|
final TcpConnectionDelegate finalDelegate = delegate;
|
2013-12-20 19:25:49 +00:00
|
|
|
Utilities.stageQueue.postRunnable(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2014-02-04 18:36:55 +00:00
|
|
|
finalDelegate.tcpConnectionClosed(TcpConnection.this);
|
2013-12-20 19:25:49 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2013-12-24 22:25:13 +00:00
|
|
|
|
2013-12-20 19:25:49 +00:00
|
|
|
failedConnectionCount++;
|
|
|
|
if (failedConnectionCount == 1) {
|
|
|
|
if (hasSomeDataSinceLastConnect) {
|
2014-02-11 14:32:09 +00:00
|
|
|
willRetryConnectCount = 3;
|
2013-12-20 19:25:49 +00:00
|
|
|
} else {
|
|
|
|
willRetryConnectCount = 1;
|
|
|
|
}
|
|
|
|
}
|
2013-12-24 22:25:13 +00:00
|
|
|
if (ConnectionsManager.isNetworkOnline()) {
|
|
|
|
isNextPort = true;
|
|
|
|
if (failedConnectionCount > willRetryConnectCount) {
|
|
|
|
Datacenter datacenter = ConnectionsManager.Instance.datacenterWithId(datacenterId);
|
|
|
|
datacenter.nextAddressOrPort();
|
|
|
|
failedConnectionCount = 0;
|
|
|
|
}
|
2013-12-20 19:25:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FileLog.e("tmessages", e);
|
|
|
|
FileLog.d("tmessages", "Reconnect " + hostAddress + ":" + hostPort + " " + TcpConnection.this);
|
|
|
|
try {
|
|
|
|
reconnectTimer = new Timer();
|
|
|
|
reconnectTimer.schedule(new TimerTask() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
selector.scheduleTask(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
try {
|
2014-02-04 18:36:55 +00:00
|
|
|
synchronized (timerSync) {
|
|
|
|
if (reconnectTimer != null) {
|
|
|
|
reconnectTimer.cancel();
|
|
|
|
reconnectTimer = null;
|
|
|
|
}
|
2013-12-20 19:25:49 +00:00
|
|
|
}
|
|
|
|
} catch (Exception e2) {
|
|
|
|
FileLog.e("tmessages", e2);
|
|
|
|
}
|
|
|
|
connect();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}, failedConnectionCount >= 3 ? 500 : 300, failedConnectionCount >= 3 ? 500 : 300);
|
|
|
|
} catch (Exception e3) {
|
|
|
|
FileLog.e("tmessages", e3);
|
|
|
|
}
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-12-20 19:25:49 +00:00
|
|
|
private void suspendConnectionInternal() {
|
2014-02-04 18:36:55 +00:00
|
|
|
synchronized (timerSync) {
|
|
|
|
if (reconnectTimer != null) {
|
|
|
|
reconnectTimer.cancel();
|
|
|
|
reconnectTimer = null;
|
|
|
|
}
|
2013-12-20 19:25:49 +00:00
|
|
|
}
|
|
|
|
if (connectionState == TcpConnectionState.TcpConnectionStageIdle || connectionState == TcpConnectionState.TcpConnectionStageSuspended) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
FileLog.d("tmessages", "suspend connnection " + TcpConnection.this);
|
|
|
|
connectionState = TcpConnectionState.TcpConnectionStageSuspended;
|
|
|
|
if (client != null) {
|
|
|
|
client.removeListener(TcpConnection.this);
|
|
|
|
client.dropConnection();
|
|
|
|
client = null;
|
|
|
|
}
|
|
|
|
if (delegate != null) {
|
2014-02-04 18:36:55 +00:00
|
|
|
final TcpConnectionDelegate finalDelegate = delegate;
|
2013-12-20 19:25:49 +00:00
|
|
|
Utilities.stageQueue.postRunnable(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2014-02-04 18:36:55 +00:00
|
|
|
finalDelegate.tcpConnectionClosed(TcpConnection.this);
|
2013-12-20 19:25:49 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
firstPacket = true;
|
2014-02-28 22:28:25 +00:00
|
|
|
if (restOfTheData != null) {
|
|
|
|
BuffersStorage.Instance.reuseFreeBuffer(restOfTheData);
|
|
|
|
restOfTheData = null;
|
|
|
|
}
|
|
|
|
lastPacketLength = 0;
|
2013-12-20 19:25:49 +00:00
|
|
|
channelToken = 0;
|
2014-02-11 14:32:09 +00:00
|
|
|
wasConnected = false;
|
2013-12-20 19:25:49 +00:00
|
|
|
}
|
|
|
|
|
2013-10-25 15:19:00 +00:00
|
|
|
public void suspendConnection(boolean task) {
|
|
|
|
if (task) {
|
|
|
|
selector.scheduleTask(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2013-12-20 19:25:49 +00:00
|
|
|
suspendConnectionInternal();
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
2013-12-20 19:25:49 +00:00
|
|
|
suspendConnectionInternal();
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void resumeConnection() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private void reconnect() {
|
|
|
|
suspendConnection(false);
|
|
|
|
connectionState = TcpConnectionState.TcpConnectionStageReconnecting;
|
|
|
|
connect();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void sendData(final byte[] data, final boolean reportAck, final boolean startResponseTimeout) {
|
|
|
|
selector.scheduleTask(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (connectionState == TcpConnectionState.TcpConnectionStageIdle ||
|
|
|
|
connectionState == TcpConnectionState.TcpConnectionStageReconnecting ||
|
2013-12-20 19:25:49 +00:00
|
|
|
connectionState == TcpConnectionState.TcpConnectionStageSuspended || client == null) {
|
2013-10-25 15:19:00 +00:00
|
|
|
connect();
|
|
|
|
}
|
|
|
|
|
2013-12-20 19:25:49 +00:00
|
|
|
if (client == null || client.isDisconnected()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-25 15:19:00 +00:00
|
|
|
int packetLength = data.length / 4;
|
|
|
|
|
|
|
|
SerializedData buffer = new SerializedData();
|
|
|
|
if (packetLength < 0x7f) {
|
|
|
|
if (reportAck) {
|
|
|
|
packetLength |= (1 << 7);
|
|
|
|
}
|
|
|
|
buffer.writeByte(packetLength);
|
|
|
|
} else {
|
|
|
|
packetLength = (packetLength << 8) + 0x7f;
|
|
|
|
if (reportAck) {
|
|
|
|
packetLength |= (1 << 7);
|
|
|
|
}
|
|
|
|
buffer.writeInt32(packetLength);
|
|
|
|
}
|
|
|
|
buffer.writeRaw(data);
|
|
|
|
|
|
|
|
final byte[] packet = buffer.toByteArray();
|
|
|
|
|
2013-12-20 19:25:49 +00:00
|
|
|
ByteBuffer sendBuffer = ByteBuffer.allocate((firstPacket ? 1 : 0) + packet.length);
|
|
|
|
sendBuffer.rewind();
|
|
|
|
sendBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
|
|
|
if (firstPacket) {
|
|
|
|
sendBuffer.put((byte)0xef);
|
|
|
|
firstPacket = false;
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
2013-12-20 19:25:49 +00:00
|
|
|
sendBuffer.put(packet);
|
|
|
|
sendBuffer.rewind();
|
|
|
|
client.write(sendBuffer);
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private void readData(ByteBuffer buffer) throws Exception {
|
2014-02-28 22:28:25 +00:00
|
|
|
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
|
|
|
buffer.rewind();
|
|
|
|
|
|
|
|
ByteBuffer parseLaterBuffer = null;
|
2013-10-25 15:19:00 +00:00
|
|
|
if (restOfTheData != null) {
|
2014-02-28 22:28:25 +00:00
|
|
|
if (lastPacketLength == 0) {
|
|
|
|
//FileLog.e("tmessages", this + " write addition data to restOfTheData");
|
|
|
|
if (restOfTheData.capacity() - restOfTheData.position() >= buffer.limit()) {
|
|
|
|
restOfTheData.limit(restOfTheData.position() + buffer.limit());
|
|
|
|
restOfTheData.put(buffer);
|
|
|
|
buffer = restOfTheData.buffer;
|
|
|
|
//FileLog.e("tmessages", this + " no need to recreate buffer");
|
|
|
|
} else {
|
|
|
|
ByteBufferDesc newBuffer = BuffersStorage.Instance.getFreeBuffer(restOfTheData.limit() + buffer.limit());
|
|
|
|
restOfTheData.rewind();
|
|
|
|
newBuffer.put(restOfTheData.buffer);
|
|
|
|
newBuffer.put(buffer);
|
|
|
|
buffer = newBuffer.buffer;
|
|
|
|
BuffersStorage.Instance.reuseFreeBuffer(restOfTheData);
|
|
|
|
restOfTheData = newBuffer;
|
|
|
|
//FileLog.e("tmessages", this + " NEED to recreate buffer");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//FileLog.e("tmessages", this + " write buffer to restOfTheData buffer of len = " + lastPacketLength);
|
|
|
|
int len = 0;
|
|
|
|
if (lastPacketLength - restOfTheData.position() <= buffer.limit()) {
|
|
|
|
len = lastPacketLength - restOfTheData.position();
|
|
|
|
//FileLog.e("tmessages", this + " received buffer - OK!");
|
|
|
|
} else {
|
|
|
|
len = buffer.limit();
|
|
|
|
//FileLog.e("tmessages", this + " received buffer less than need");
|
|
|
|
}
|
|
|
|
int oldLimit = buffer.limit();
|
|
|
|
buffer.limit(len);
|
|
|
|
restOfTheData.put(buffer);
|
|
|
|
buffer.limit(oldLimit);
|
|
|
|
if (restOfTheData.position() != lastPacketLength) {
|
|
|
|
//FileLog.e("tmessages", this + " don't get much data to restOfTheData");
|
|
|
|
if (lastMessageId != -1 && lastMessageId != 0) {
|
|
|
|
if (delegate != null) {
|
|
|
|
final TcpConnectionDelegate finalDelegate = delegate;
|
|
|
|
final int arg2 = restOfTheData.position();
|
|
|
|
final int arg3 = lastPacketLength;
|
|
|
|
Utilities.stageQueue.postRunnable(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
finalDelegate.tcpConnectionProgressChanged(TcpConnection.this, lastMessageId, arg2, arg3);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
//FileLog.e("tmessages", this + " get much data to restOfTheData - OK!");
|
|
|
|
if (buffer.hasRemaining()) {
|
|
|
|
parseLaterBuffer = buffer;
|
|
|
|
//FileLog.e("tmessages", this + " something remain in the received buffer");
|
|
|
|
} else {
|
|
|
|
parseLaterBuffer = null;
|
|
|
|
}
|
|
|
|
buffer = restOfTheData.buffer;
|
|
|
|
}
|
|
|
|
}
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
buffer.rewind();
|
|
|
|
|
|
|
|
while (buffer.hasRemaining()) {
|
2013-12-20 19:25:49 +00:00
|
|
|
if (!hasSomeDataSinceLastConnect) {
|
|
|
|
Datacenter datacenter = ConnectionsManager.Instance.datacenterWithId(datacenterId);
|
|
|
|
datacenter.storeCurrentAddressAndPortNum();
|
|
|
|
isNextPort = false;
|
2014-02-28 22:28:25 +00:00
|
|
|
client.setTimeout(25000);
|
2013-12-20 19:25:49 +00:00
|
|
|
}
|
|
|
|
hasSomeDataSinceLastConnect = true;
|
2013-10-25 15:19:00 +00:00
|
|
|
|
|
|
|
int currentPacketLength;
|
|
|
|
buffer.mark();
|
|
|
|
byte fByte = buffer.get();
|
|
|
|
|
|
|
|
if ((fByte & (1 << 7)) != 0) {
|
|
|
|
buffer.reset();
|
|
|
|
if (buffer.remaining() < 4) {
|
2014-02-28 22:28:25 +00:00
|
|
|
ByteBufferDesc reuseLater = restOfTheData;
|
|
|
|
restOfTheData = BuffersStorage.Instance.getFreeBuffer(16384);
|
2013-10-25 15:19:00 +00:00
|
|
|
restOfTheData.put(buffer);
|
2014-02-28 22:28:25 +00:00
|
|
|
restOfTheData.limit(restOfTheData.position());
|
|
|
|
lastPacketLength = 0;
|
|
|
|
//FileLog.e("tmessages", this + " 1 - size less than 4 bytes - write to free buffer");
|
|
|
|
if (reuseLater != null) {
|
|
|
|
BuffersStorage.Instance.reuseFreeBuffer(reuseLater);
|
|
|
|
//FileLog.e("tmessages", this + " 1 - reuse later buffer1");
|
|
|
|
}
|
2013-10-25 15:19:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
buffer.order(ByteOrder.BIG_ENDIAN);
|
|
|
|
final int ackId = buffer.getInt() & (~(1 << 31));
|
|
|
|
if (delegate != null) {
|
2014-02-04 18:36:55 +00:00
|
|
|
final TcpConnectionDelegate finalDelegate = delegate;
|
2013-10-25 15:19:00 +00:00
|
|
|
Utilities.stageQueue.postRunnable(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2014-02-04 18:36:55 +00:00
|
|
|
finalDelegate.tcpConnectionQuiackAckReceived(TcpConnection.this, ackId);
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fByte != 0x7f) {
|
|
|
|
currentPacketLength = ((int)fByte) * 4;
|
|
|
|
} else {
|
|
|
|
buffer.reset();
|
|
|
|
if (buffer.remaining() < 4) {
|
2014-02-28 22:28:25 +00:00
|
|
|
//FileLog.e("tmessages", this + " 2 - size less than 4 bytes - write to free buffer");
|
|
|
|
if (restOfTheData == null || restOfTheData != null && restOfTheData.position() != 0) {
|
|
|
|
ByteBufferDesc reuseLater = restOfTheData;
|
|
|
|
restOfTheData = BuffersStorage.Instance.getFreeBuffer(16384);
|
|
|
|
restOfTheData.put(buffer);
|
|
|
|
restOfTheData.limit(restOfTheData.position());
|
|
|
|
lastPacketLength = 0;
|
|
|
|
if (reuseLater != null) {
|
|
|
|
BuffersStorage.Instance.reuseFreeBuffer(reuseLater);
|
|
|
|
//FileLog.e("tmessages", this + " 2 - reuse later buffer1");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
restOfTheData.position(restOfTheData.limit());
|
|
|
|
}
|
2013-10-25 15:19:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
currentPacketLength = (buffer.getInt() >> 8) * 4;
|
|
|
|
}
|
|
|
|
|
2013-12-20 19:25:49 +00:00
|
|
|
if (currentPacketLength % 4 != 0 || currentPacketLength > 2 * 1024 * 1024) {
|
2014-03-05 16:42:10 +00:00
|
|
|
FileLog.e("tmessages", "Invalid packet length");
|
2013-12-20 19:25:49 +00:00
|
|
|
reconnect();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-25 15:19:00 +00:00
|
|
|
if (currentPacketLength < buffer.remaining()) {
|
2013-12-20 19:25:49 +00:00
|
|
|
FileLog.d("tmessages", TcpConnection.this + " Received message len " + currentPacketLength + " but packet larger " + buffer.remaining());
|
|
|
|
lastMessageId = 0;
|
2013-10-25 15:19:00 +00:00
|
|
|
} else if (currentPacketLength == buffer.remaining()) {
|
2013-12-20 19:25:49 +00:00
|
|
|
FileLog.d("tmessages", TcpConnection.this + " Received message len " + currentPacketLength + " equal to packet size");
|
|
|
|
lastMessageId = 0;
|
2013-10-25 15:19:00 +00:00
|
|
|
} else {
|
2013-12-20 19:25:49 +00:00
|
|
|
FileLog.d("tmessages", TcpConnection.this + " Received packet size less(" + buffer.remaining() + ") then message size(" + currentPacketLength + ")");
|
|
|
|
if (buffer.remaining() >= 152 && (transportRequestClass & RPCRequest.RPCRequestClassDownloadMedia) != 0) {
|
|
|
|
if (lastMessageId == 0) {
|
|
|
|
byte[] temp = new byte[152];
|
|
|
|
buffer.get(temp);
|
|
|
|
lastMessageId = ConnectionsManager.Instance.needsToDecodeMessageIdFromPartialData(TcpConnection.this, temp);
|
|
|
|
}
|
|
|
|
if (lastMessageId != -1 && lastMessageId != 0) {
|
|
|
|
if (delegate != null) {
|
2014-02-04 18:36:55 +00:00
|
|
|
final TcpConnectionDelegate finalDelegate = delegate;
|
2013-12-20 19:25:49 +00:00
|
|
|
final int arg2 = buffer.remaining();
|
|
|
|
final int arg3 = currentPacketLength;
|
|
|
|
Utilities.stageQueue.postRunnable(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2014-02-04 18:36:55 +00:00
|
|
|
finalDelegate.tcpConnectionProgressChanged(TcpConnection.this, lastMessageId, arg2, arg3);
|
2013-12-20 19:25:49 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
2014-02-28 22:28:25 +00:00
|
|
|
|
|
|
|
ByteBufferDesc reuseLater = null;
|
|
|
|
int len = currentPacketLength + (fByte != 0x7f ? 1 : 4);
|
|
|
|
if (restOfTheData != null && restOfTheData.capacity() < len) {
|
|
|
|
reuseLater = restOfTheData;
|
|
|
|
restOfTheData = null;
|
|
|
|
//FileLog.e("tmessages", this + " not enough space for len, recreate buffer = " + len);
|
|
|
|
}
|
|
|
|
if (restOfTheData == null) {
|
|
|
|
//FileLog.e("tmessages", this + " write to restOfTheData, get buffer len = " + len);
|
|
|
|
buffer.reset();
|
|
|
|
restOfTheData = BuffersStorage.Instance.getFreeBuffer(len);
|
|
|
|
restOfTheData.put(buffer);
|
|
|
|
} else {
|
|
|
|
restOfTheData.position(restOfTheData.limit());
|
|
|
|
restOfTheData.limit(len);
|
|
|
|
}
|
|
|
|
lastPacketLength = len;
|
|
|
|
if (reuseLater != null) {
|
|
|
|
BuffersStorage.Instance.reuseFreeBuffer(reuseLater);
|
|
|
|
//FileLog.e("tmessages", this + " 3 - reuse later buffer1");
|
|
|
|
}
|
2013-10-25 15:19:00 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-28 22:28:25 +00:00
|
|
|
final int length = currentPacketLength;
|
|
|
|
final ByteBufferDesc toProceed = BuffersStorage.Instance.getFreeBuffer(currentPacketLength);
|
|
|
|
int old = buffer.limit();
|
|
|
|
buffer.limit(buffer.position() + currentPacketLength);
|
|
|
|
toProceed.put(buffer);
|
|
|
|
buffer.limit(old);
|
|
|
|
toProceed.rewind();
|
2013-10-25 15:19:00 +00:00
|
|
|
|
|
|
|
if (delegate != null) {
|
2014-02-04 18:36:55 +00:00
|
|
|
final TcpConnectionDelegate finalDelegate = delegate;
|
2013-10-25 15:19:00 +00:00
|
|
|
Utilities.stageQueue.postRunnable(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2014-02-28 22:28:25 +00:00
|
|
|
finalDelegate.tcpConnectionReceivedData(TcpConnection.this, toProceed, length);
|
|
|
|
BuffersStorage.Instance.reuseFreeBuffer(toProceed);
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2014-02-28 22:28:25 +00:00
|
|
|
|
|
|
|
if (restOfTheData != null) {
|
|
|
|
if (lastPacketLength != 0 && restOfTheData.position() == lastPacketLength || lastPacketLength == 0 && !restOfTheData.hasRemaining()) {
|
|
|
|
BuffersStorage.Instance.reuseFreeBuffer(restOfTheData);
|
|
|
|
restOfTheData = null;
|
|
|
|
//FileLog.e("tmessages", this + " restOfTheData parsed null it");
|
|
|
|
} else {
|
|
|
|
restOfTheData.compact();
|
|
|
|
restOfTheData.limit(restOfTheData.position());
|
|
|
|
restOfTheData.position(0);
|
|
|
|
//FileLog.e("tmessages", this + " restOfTheData NOT parsed, compact");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parseLaterBuffer != null) {
|
|
|
|
//FileLog.e("tmessages", this + " there is parseLaterBuffer");
|
|
|
|
buffer = parseLaterBuffer;
|
|
|
|
parseLaterBuffer = null;
|
|
|
|
}
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-11 14:32:09 +00:00
|
|
|
public void handleDisconnect(PyroClient client, Exception e, boolean timedout) {
|
2014-02-04 18:36:55 +00:00
|
|
|
synchronized (timerSync) {
|
|
|
|
if (reconnectTimer != null) {
|
|
|
|
reconnectTimer.cancel();
|
|
|
|
reconnectTimer = null;
|
|
|
|
}
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
2013-12-20 19:25:49 +00:00
|
|
|
if (e != null) {
|
|
|
|
FileLog.d("tmessages", "Disconnected " + TcpConnection.this + " with error " + e);
|
|
|
|
} else {
|
|
|
|
FileLog.d("tmessages", "Disconnected " + TcpConnection.this);
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
2014-02-11 14:44:02 +00:00
|
|
|
boolean switchToNextPort = wasConnected && !hasSomeDataSinceLastConnect && timedout;
|
2013-10-25 15:19:00 +00:00
|
|
|
firstPacket = true;
|
2014-02-28 22:28:25 +00:00
|
|
|
if (restOfTheData != null) {
|
|
|
|
BuffersStorage.Instance.reuseFreeBuffer(restOfTheData);
|
|
|
|
restOfTheData = null;
|
|
|
|
}
|
2013-10-25 15:19:00 +00:00
|
|
|
channelToken = 0;
|
2014-02-28 22:28:25 +00:00
|
|
|
lastPacketLength = 0;
|
2014-02-11 14:32:09 +00:00
|
|
|
wasConnected = false;
|
2013-10-25 15:19:00 +00:00
|
|
|
if (connectionState != TcpConnectionState.TcpConnectionStageSuspended && connectionState != TcpConnectionState.TcpConnectionStageIdle) {
|
|
|
|
connectionState = TcpConnectionState.TcpConnectionStageIdle;
|
|
|
|
}
|
|
|
|
if (delegate != null) {
|
2014-02-04 18:36:55 +00:00
|
|
|
final TcpConnectionDelegate finalDelegate = delegate;
|
2013-10-25 15:19:00 +00:00
|
|
|
Utilities.stageQueue.postRunnable(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2014-02-04 18:36:55 +00:00
|
|
|
finalDelegate.tcpConnectionClosed(TcpConnection.this);
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2013-12-20 19:25:49 +00:00
|
|
|
if (connectionState == TcpConnectionState.TcpConnectionStageIdle &&
|
|
|
|
((transportRequestClass & RPCRequest.RPCRequestClassGeneric) != 0 && (datacenterId == ConnectionsManager.Instance.currentDatacenterId || datacenterId == ConnectionsManager.Instance.movingToDatacenterId))) {
|
2013-10-25 15:19:00 +00:00
|
|
|
failedConnectionCount++;
|
2013-12-20 19:25:49 +00:00
|
|
|
if (failedConnectionCount == 1) {
|
|
|
|
if (hasSomeDataSinceLastConnect) {
|
|
|
|
willRetryConnectCount = 5;
|
|
|
|
} else {
|
|
|
|
willRetryConnectCount = 1;
|
|
|
|
}
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
2013-12-24 22:25:13 +00:00
|
|
|
if (ConnectionsManager.isNetworkOnline()) {
|
|
|
|
isNextPort = true;
|
2014-02-11 14:44:02 +00:00
|
|
|
if (failedConnectionCount > willRetryConnectCount || switchToNextPort) {
|
2013-12-24 22:25:13 +00:00
|
|
|
Datacenter datacenter = ConnectionsManager.Instance.datacenterWithId(datacenterId);
|
|
|
|
datacenter.nextAddressOrPort();
|
|
|
|
failedConnectionCount = 0;
|
|
|
|
}
|
2013-12-20 19:25:49 +00:00
|
|
|
}
|
|
|
|
FileLog.d("tmessages", "Reconnect " + hostAddress + ":" + hostPort + " " + TcpConnection.this);
|
2013-10-25 15:19:00 +00:00
|
|
|
try {
|
|
|
|
reconnectTimer = new Timer();
|
|
|
|
reconnectTimer.schedule(new TimerTask() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
selector.scheduleTask(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
try {
|
2014-02-04 18:36:55 +00:00
|
|
|
synchronized (timerSync) {
|
|
|
|
if (reconnectTimer != null) {
|
|
|
|
reconnectTimer.cancel();
|
|
|
|
reconnectTimer = null;
|
|
|
|
}
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
} catch (Exception e2) {
|
2013-12-20 19:25:49 +00:00
|
|
|
FileLog.e("tmessages", e2);
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
connect();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2013-12-20 19:25:49 +00:00
|
|
|
}, failedConnectionCount > 3 ? 500 : 300, failedConnectionCount > 3 ? 500 : 300);
|
2013-10-25 15:19:00 +00:00
|
|
|
} catch (Exception e3) {
|
2013-12-20 19:25:49 +00:00
|
|
|
FileLog.e("tmessages", e3);
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void connectedClient(PyroClient client) {
|
|
|
|
connectionState = TcpConnectionState.TcpConnectionStageConnected;
|
|
|
|
channelToken = generateChannelToken();
|
2014-02-11 14:32:09 +00:00
|
|
|
wasConnected = true;
|
2013-12-20 19:25:49 +00:00
|
|
|
FileLog.d("tmessages", String.format(TcpConnection.this + " Connected (%s:%d)", hostAddress, hostPort));
|
2013-10-25 15:19:00 +00:00
|
|
|
if (delegate != null) {
|
2014-02-04 18:36:55 +00:00
|
|
|
final TcpConnectionDelegate finalDelegate = delegate;
|
2013-10-25 15:19:00 +00:00
|
|
|
Utilities.stageQueue.postRunnable(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2014-02-04 18:36:55 +00:00
|
|
|
finalDelegate.tcpConnectionConnected(TcpConnection.this);
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2013-12-20 19:25:49 +00:00
|
|
|
public void unconnectableClient(PyroClient client, Exception cause) {
|
2014-02-11 14:32:09 +00:00
|
|
|
handleDisconnect(client, cause, false);
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void droppedClient(PyroClient client, IOException cause) {
|
|
|
|
super.droppedClient(client, cause);
|
2014-02-11 14:32:09 +00:00
|
|
|
handleDisconnect(client, cause, (cause instanceof SocketTimeoutException));
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void disconnectedClient(PyroClient client) {
|
2014-02-11 14:32:09 +00:00
|
|
|
handleDisconnect(client, null, false);
|
2013-10-25 15:19:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void receivedData(PyroClient client, ByteBuffer data) {
|
|
|
|
try {
|
|
|
|
failedConnectionCount = 0;
|
|
|
|
readData(data);
|
|
|
|
} catch (Exception e) {
|
2014-02-28 22:28:25 +00:00
|
|
|
FileLog.e("tmessages", e);
|
2013-10-25 15:19:00 +00:00
|
|
|
reconnect();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void sentData(PyroClient client, int bytes) {
|
|
|
|
}
|
|
|
|
}
|