Skip to content
This repository was archived by the owner on Aug 15, 2026. It is now read-only.

Commit 2d95a66

Browse files
committed
Fix protocol header layout: cmd uint8 + seq uint16 to match server
1 parent 2d3fd5d commit 2d95a66

10 files changed

Lines changed: 115 additions & 103 deletions

lib/api/api_registration_service.dart

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ typedef Lz4Decompress =
2626

2727
class RegistrationService {
2828
Socket? _socket;
29-
int _seq = 0;
29+
// Начинаем с 1, чтобы seq=0 (server push) не совпал с pending.
30+
int _seq = 1;
3031
final Map<int, Completer<dynamic>> _pending = {};
3132
bool _isConnected = false;
3233
final _random = Random();
@@ -160,15 +161,12 @@ class RegistrationService {
160161

161162
void _processPacket(Uint8List packet) {
162163
try {
163-
final ver = packet[0];
164-
final cmd = ByteData.view(packet.buffer).getUint16(1, Endian.big);
165-
final seq = packet[3];
166-
final opcode = ByteData.view(packet.buffer).getUint16(4, Endian.big);
167-
final packedLen = ByteData.view(
168-
packet.buffer,
169-
6,
170-
4,
171-
).getUint32(0, Endian.big);
164+
final bd = ByteData.view(packet.buffer, packet.offsetInBytes, packet.lengthInBytes);
165+
final ver = bd.getUint8(0);
166+
final cmd = bd.getUint8(1);
167+
final seq = bd.getUint16(2, Endian.big);
168+
final opcode = bd.getUint16(4, Endian.big);
169+
final packedLen = bd.getUint32(6, Endian.big);
172170

173171
final compFlag = packedLen >> 24;
174172
final payloadLen = packedLen & 0x00FFFFFF;
@@ -215,20 +213,19 @@ class RegistrationService {
215213
int opcode,
216214
Map<String, dynamic> payload,
217215
) {
218-
final verB = Uint8List(1)..[0] = ver;
219-
final cmdB = Uint8List(2)
220-
..buffer.asByteData().setUint16(0, cmd, Endian.big);
221-
final seqB = Uint8List(1)..[0] = seq;
222-
final opcodeB = Uint8List(2)
223-
..buffer.asByteData().setUint16(0, opcode, Endian.big);
216+
// Заголовок: ver(1) + cmd(1) + seq(2 BE) + opcode(2 BE) + packedLen(4 BE) = 10 байт
217+
final header = ByteData(10);
218+
header.setUint8(0, ver);
219+
header.setUint8(1, cmd);
220+
header.setUint16(2, seq & 0xFFFF, Endian.big);
221+
header.setUint16(4, opcode, Endian.big);
224222

225223
final payloadBytes = msgpack.serialize(payload);
226224
final payloadLen = payloadBytes.length & 0xFFFFFF;
227-
final payloadLenB = Uint8List(4)
228-
..buffer.asByteData().setUint32(0, payloadLen, Endian.big);
225+
header.setUint32(6, payloadLen, Endian.big);
229226

230227
final packet = Uint8List.fromList(
231-
verB + cmdB + seqB + opcodeB + payloadLenB + payloadBytes,
228+
header.buffer.asUint8List() + payloadBytes,
232229
);
233230

234231
print('═══════════════════════════════════════════════════════════');
@@ -585,7 +582,7 @@ class RegistrationService {
585582
throw Exception('Не подключено к серверу');
586583
}
587584

588-
_seq = (_seq + 1) % 256;
585+
_seq = (_seq + 1) & 0xFFFF;
589586
final seq = _seq;
590587
final packet = _packPacket(10, 0, seq, opcode, payload);
591588

lib/api/api_service.dart

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ class ApiService {
101101
StreamSubscription? _socketSubscription;
102102
Timer? _pingTimer;
103103
Timer? _analyticsTimer;
104-
int _seq = 0;
104+
// Начинаем с 1, чтобы seq=0 (которым обычно приходят server pushes) никогда не совпал с pending request.
105+
int _seq = 1;
105106

106107
late final PendingRequestsManager _pendingManager;
107108
bool _socketConnected = false;
@@ -359,7 +360,7 @@ class ApiService {
359360

360361
// Проверяем ответ
361362
final cmd = response['cmd'] as int?;
362-
if (cmd != 0x100 && cmd != 256) {
363+
if (cmd != 1) {
363364
return null;
364365
}
365366

@@ -762,11 +763,13 @@ class ApiService {
762763
// Отправляем в стрим
763764
_emitLocal(message);
764765

765-
// Завершаем pending только для пакетов-ответов (cmd ∈ {256/0x100=ok, 512/0x200=notFound, 768/0x300=error}).
766-
// Пуши от сервера приходят с cmd=0/request и могут случайно совпасть по seq c нашим pending,
767-
// что и приводило к крешам типа `payload=40` — pending получал чужой push вместо ответа.
766+
// Завершаем pending только для пакетов-ответов (ok/notFound/error).
767+
// Push'и от сервера (cmd=push) имеют то же значение, что и ok, и тоже могут совпасть
768+
// по seq с нашим pending. Их фильтрация делается на уровне seq: server pushes обычно
769+
// приходят с seq=0 (мы свой counter стартуем с 1+) либо seq, не выданным нашим клиентом.
770+
// Здесь же отсеиваем только request-пакеты (cmd=0), которых от сервера быть не должно.
768771
final cmd = parsed.cmd;
769-
final isResponse = cmd == 256 || cmd == 512 || cmd == 768;
772+
final isResponse = cmd == CmdType.ok || cmd == CmdType.notFound || cmd == CmdType.error;
770773
if (isResponse) {
771774
// Передаем ВЕСЬ message (включая cmd), а не только payload
772775
_pendingManager.complete(parsed.seq, message);
@@ -810,7 +813,7 @@ class ApiService {
810813
}
811814
}
812815

813-
final seq = _seq++ % 256;
816+
final seq = _seq++ & 0xFFFF;
814817

815818
final completer = _pendingManager.register(
816819
seq,
@@ -856,7 +859,7 @@ class ApiService {
856859
}
857860
}
858861

859-
final seq = _seq++ % 256;
862+
final seq = _seq++ & 0xFFFF;
860863

861864
final completer = _pendingManager.register(
862865
seq,
@@ -883,21 +886,18 @@ class ApiService {
883886
int opcode,
884887
Map<String, dynamic> payload,
885888
) {
886-
final verB = Uint8List(1)..[0] = ver;
887-
final cmdB = Uint8List(2)
888-
..buffer.asByteData().setUint16(0, cmd, Endian.big);
889-
final seqB = Uint8List(1)..[0] = seq;
890-
final opcodeB = Uint8List(2)
891-
..buffer.asByteData().setUint16(0, opcode, Endian.big);
889+
// Заголовок: ver(1) + cmd(1) + seq(2 BE) + opcode(2 BE) + packedLen(4 BE) = 10 байт
890+
final header = ByteData(10);
891+
header.setUint8(0, ver);
892+
header.setUint8(1, cmd);
893+
header.setUint16(2, seq & 0xFFFF, Endian.big);
894+
header.setUint16(4, opcode, Endian.big);
892895

893896
final payloadBytes = msgpack.serialize(payload);
894897
final payloadLen = payloadBytes.length & 0xFFFFFF;
895-
final payloadLenB = Uint8List(4)
896-
..buffer.asByteData().setUint32(0, payloadLen, Endian.big);
898+
header.setUint32(6, payloadLen, Endian.big);
897899

898-
return Uint8List.fromList(
899-
verB + cmdB + seqB + opcodeB + payloadLenB + payloadBytes,
900-
);
900+
return Uint8List.fromList(header.buffer.asUint8List() + payloadBytes);
901901
}
902902

903903
Future<int> _sendMessage(
@@ -937,7 +937,7 @@ class ApiService {
937937
}
938938
}
939939

940-
final seq = _seq++ % 256;
940+
final seq = _seq++ & 0xFFFF;
941941

942942
// Регистрируем pending request ДО отправки
943943
_pendingManager.register(
@@ -1087,7 +1087,7 @@ class ApiService {
10871087

10881088
final resp64 = await messages.firstWhere((m) => m['seq'] == seq64);
10891089
final cmd = resp64['cmd'] as int?;
1090-
if (cmd == 0x300 || cmd == 768) {
1090+
if (cmd == 3) {
10911091
final err = resp64['payload'];
10921092
if (err is Map && err['error'] == 'attachment.not.ready') {
10931093
throw err;

lib/api/api_service_calls.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ extension ApiServiceCalls on ApiService {
134134

135135
// Проверяем cmd ответа
136136
final cmd = response['cmd'] as int?;
137-
if (cmd != 0x100 && cmd != 256) {
137+
if (cmd != 1) {
138138
final error = response['payload']?['error'] ?? 'Неизвестная ошибка';
139139
throw Exception('Ошибка инициации звонка: $error');
140140
}
@@ -199,7 +199,7 @@ extension ApiServiceCalls on ApiService {
199199

200200
// Проверяем cmd ответа
201201
final cmd = response['cmd'] as int?;
202-
if (cmd != 0x100 && cmd != 256) {
202+
if (cmd != 1) {
203203
final error = response['payload']?['error'] ?? 'Неизвестная ошибка';
204204
throw Exception('Ошибка начала группового звонка: $error');
205205
}
@@ -266,7 +266,7 @@ extension ApiServiceCalls on ApiService {
266266

267267
// Проверяем cmd ответа
268268
final cmd = response['cmd'] as int?;
269-
if (cmd != 0x100 && cmd != 256) {
269+
if (cmd != 1) {
270270
final error = response['payload']?['error'] ?? 'Неизвестная ошибка';
271271
throw Exception('Ошибка присоединения к звонку: $error');
272272
}

lib/api/api_service_chats.dart

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ extension ApiServiceChats on ApiService {
367367
}
368368

369369
if (opcode == 19 &&
370-
(chatResponse['cmd'] == 0x100 || chatResponse['cmd'] == 256)) {
370+
(chatResponse['cmd'] == 1)) {
371371
print("✅ Авторизация (opcode 19) успешна. Сессия ГОТОВА.");
372372

373373
_isSessionReady = true;
@@ -1412,7 +1412,7 @@ extension ApiServiceChats on ApiService {
14121412
return false;
14131413
}
14141414

1415-
return response['cmd'] == 0x100 || response['cmd'] == 256;
1415+
return response['cmd'] == 1;
14161416
} catch (e) {
14171417
print('Ошибка при редактировании сообщения: $e');
14181418
return false;
@@ -1534,7 +1534,7 @@ extension ApiServiceChats on ApiService {
15341534
return false;
15351535
}
15361536

1537-
return response['cmd'] == 0x100 || response['cmd'] == 256;
1537+
return response['cmd'] == 1;
15381538
} catch (e) {
15391539
print('Ошибка при удалении сообщения: $e');
15401540
return false;
@@ -1651,7 +1651,7 @@ extension ApiServiceChats on ApiService {
16511651

16521652
final resp64 = await sendRequest(64, payload);
16531653
final cmd = resp64['cmd'] as int?;
1654-
if (cmd == 0x300 || cmd == 768) {
1654+
if (cmd == 3) {
16551655
final err = resp64['payload'];
16561656
if (err is Map && err['error'] == 'attachment.not.ready') {
16571657
throw err;
@@ -1772,7 +1772,7 @@ extension ApiServiceChats on ApiService {
17721772

17731773
final resp64 = await sendRequest(64, payload);
17741774
final cmd = resp64['cmd'] as int?;
1775-
if (cmd == 0x300 || cmd == 768) {
1775+
if (cmd == 3) {
17761776
final err = resp64['payload'];
17771777
if (err is Map && err['error'] == 'attachment.not.ready') {
17781778
throw err;
@@ -1897,7 +1897,7 @@ extension ApiServiceChats on ApiService {
18971897

18981898
final resp64 = await sendRequest(64, payload);
18991899
final cmd = resp64['cmd'] as int?;
1900-
if (cmd == 0x300 || cmd == 768) {
1900+
if (cmd == 3) {
19011901
final err = resp64['payload'];
19021902
if (err is Map && err['error'] == 'attachment.not.ready') {
19031903
throw err;

0 commit comments

Comments
 (0)