ZaStoGram_desktop/Telegram/SourceFiles/mtproto/session/private/transport.cpp
loop-uh 1e19d26187
All checks were successful
Desktop source guards / guards (push) Successful in 12s
Качать файлы через туннель частями по 8 КБ на коротких соединениях
ТСПУ замораживает каждое соединение с Cloudflare примерно после 16 КБ
входящих: на ПК (лог 25.09) ни одна сессия туннеля к DC1 не получила больше
13 КБ, а часть файла 128 КБ целиком не проходила ни разу (send_count=51),
поэтому файлы в «Избранном» стояли на 0.

- Для DC, чей медийный релей подавлен и ушёл в туннель, часть 128 КБ
  собирается из 16 кусков по 8 КБ; для CDN части не делятся.
- До 8 сессий сразу, в каждой один запрос: скорость даёт параллельность.
- Файловое соединение туннеля переоткрывается на границе пакета после
  4 КБ входящих, до заморозки; такие сокеты пишутся одной сводной строкой
  wss_tunnel_rotated, строки переподключения для них не пишутся.
- Туннель подавляется только если после upgrade не пришло ничего: раньше
  три заморозки отправляли DC1 в прямой TCP, который сеть режет целиком,
  и файлы не грузились совсем по две минуты.
2026-09-25 15:01:01 +03:00

210 lines
5.9 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "mtproto/session/private/session_private.h"
#include "mtproto/session/private/timings.h"
#include "mtproto/instance/mtp_instance.h"
#include "mtproto/proxy/diagnostics.h"
namespace MTP::details {
SessionTransport::TimingState::TimingState(
not_null<RuntimeEnvironment*> runtime,
not_null<SessionTransport*> owner,
not_null<QThread*> thread)
: retryTimer(runtime->async().makeTimer(
thread,
[=] { owner->retryByTimer(); }))
, oldConnectionTimer(runtime->async().makeTimer(
thread,
[=] { owner->markConnectionOld(); }))
, waitForConnectedTimer(runtime->async().makeTimer(
thread,
[=] { owner->waitConnectedFailed(); }))
, waitForReceivedTimer(runtime->async().makeTimer(
thread,
[=] { owner->waitReceivedFailed(); }))
, waitForBetterTimer(runtime->async().makeTimer(
thread,
[=] { owner->waitBetterFailed(); }))
, waitForReceived(kMinReceiveTimeout)
, waitForConnected(kMinConnectedTimeout)
, pingSender(runtime->async().makeTimer(
thread,
[=] { owner->_owner->sendPingByTimer(); }))
, checkSentRequestsTimer(runtime->async().makeTimer(
thread,
[=] { owner->_owner->checkSentRequests(); }))
, clearOldContainersTimer(runtime->async().makeTimer(
thread,
[=] { owner->_owner->_messageHandler.clearOldContainers(); })) {
}
SessionTransport::SessionTransport(
not_null<SessionPrivate*> owner,
not_null<RuntimeEnvironment*> runtime,
not_null<QThread*> thread,
uint64 proxyGeneration)
: _owner(owner)
, _timing(runtime, this, thread) {
_state.proxyGeneration = proxyGeneration;
_state.mtproxyAttempt = { .proxyGeneration = proxyGeneration };
}
SessionTransport::~SessionTransport() = default;
void SessionTransport::start() {
connectToServer();
}
void SessionTransport::setRetryTimeout(int timeout) {
_timing.retryTimeout = timeout;
}
void SessionTransport::scheduleRetryTimeout(int timeout) {
_timing.retryTimeout = timeout;
_timing.retryTimer.callOnce(_timing.retryTimeout);
_timing.retryWillFinish = crl::now() + _timing.retryTimeout;
}
void SessionTransport::schedulePing(crl::time timeout) {
_timing.pingSender.callOnce(timeout);
}
void SessionTransport::scheduleCheckSentRequests(crl::time timeout) {
_timing.checkSentRequestsTimer.callOnce(timeout);
}
void SessionTransport::scheduleClearOldContainers(
crl::time timeout,
bool repeated) {
if (repeated) {
_timing.clearOldContainersTimer.callEach(timeout);
} else {
_timing.clearOldContainersTimer.callOnce(timeout);
}
}
void SessionTransport::resetRetryTimeout() {
_timing.retryTimeout = 1;
}
bool SessionTransport::retryTimerActive() const {
return _timing.retryTimer.isActive();
}
bool SessionTransport::checkSentRequestsTimerActive() const {
return _timing.checkSentRequestsTimer.isActive();
}
bool SessionTransport::clearOldContainersTimerActive() const {
return _timing.clearOldContainersTimer.isActive();
}
int SessionTransport::retryTimeout() const {
return _timing.retryTimeout;
}
qint64 SessionTransport::retryWillFinish() const {
return _timing.retryWillFinish;
}
AbstractConnection *SessionTransport::connection() const {
return _state.connection.get();
}
bool SessionTransport::hasReceivedData() const {
return _state.connection && !_state.connection->received().empty();
}
mtpBuffer SessionTransport::takeReceivedData() {
Assert(_state.connection != nullptr);
Assert(!_state.connection->received().empty());
auto result = std::move(_state.connection->received().front());
_state.connection->received().pop_front();
return result;
}
QString SessionTransport::activeTransport() const {
return _state.connection ? _state.connection->transport() : QString();
}
QString SessionTransport::connectionTag() const {
return _state.connection ? _state.connection->tag() : u"none"_q;
}
crl::time SessionTransport::connectionPingTime() const {
return _state.connection ? _state.connection->pingTime() : 0;
}
auto SessionTransport::serviceRequest() const
-> AbstractConnection::TransportServiceRequest {
Assert(_state.connection != nullptr);
return _state.connection->serviceRequest();
}
bool SessionTransport::serviceRequestNeeded(
AbstractConnection::TransportServiceRequest request) const {
return _state.connection && _state.connection->serviceRequestNeeded(request);
}
mtpBuffer SessionTransport::prepareSecurePacket(
uint64 keyId,
MTPint128 msgKey,
uint32 size) const {
Assert(_state.connection != nullptr);
return _state.connection->prepareSecurePacket(keyId, msgKey, size);
}
void SessionTransport::sendData(
mtpBuffer &&buffer,
AbstractConnection::SendDataContext context) {
Assert(_state.connection != nullptr);
_state.connection->sendData(std::move(buffer), context);
}
void SessionTransport::logInfo(const QString &message) const {
Assert(_state.connection != nullptr);
_state.connection->logInfo(message);
}
bool SessionTransport::empty() const {
return !_state.connection && _state.testConnections.empty();
}
void SessionTransport::startContainerCleanup() {
_timing.clearOldContainersTimer.callEach(kSentContainerLives);
}
void SessionTransport::noteMtprotoPayloadReceived() {
_timing.retryTimeout = 1;
const auto firstPayload = !_state.mtprotoDataReceived;
_state.webKeyNotFoundStrikes = 0;
if (firstPayload) {
_state.mtprotoDataReceived = true;
_state.mtprotoSilentTimeouts = 0;
_owner->logMtprotoEvent(
ProxyDiagnosticsPhase::MtpFirstDataReceived,
ProxyDiagnosticsSeverity::Info,
u"first mtproto payload received"_q);
_quietReconnect = false;
}
if (_state.connection) {
_state.connection->markProxyMtprotoPayloadReceived();
}
_state.startedConnectingAt = crl::time(0);
}
ProxyConnectionAttempt SessionTransport::currentProxyAttempt() const {
return _state.connection
? _state.connection->proxyConnectionAttempt()
: _state.mtproxyAttempt;
}
} // namespace MTP::details