Kritik babagan protokol lan pendekatan organisasi Telegram. Bagian 1, teknis: pengalaman nulis klien saka awal - TL, MT

Bubar, kiriman wis wiwit katon luwih kerep ing Habré babagan carane Telegram apik, carane sarwa lan pengalaman sedulur Durov ing mbangun sistem jaringan, etc. Ing wektu sing padha, mung sawetara wong sing nyemplungake awake dhewe ing piranti teknis - paling akeh nggunakake API Bot berbasis JSON sing cukup prasaja (lan beda banget karo MTProto), lan biasane mung nampa. ing iman kabeh sing pujian lan PR sing revolve watara utusan. Meh setahun setengah kepungkur, kancaku ing NPO Eselon Vasily (sayange, akun Habré wis dibusak bebarengan karo konsep) wiwit nulis klien Telegram dhewe saka awal ing Perl, lan banjur penulis garis kasebut gabung. Apa Perl, sawetara bakal langsung takon? Amarga wis ana proyek kaya ngono ing basa liya, nyatane, iki dudu titikane, bisa uga ana basa liya ing ngendi perpustakaan rampung, lan miturut penulis kudu lunga kabeh saka ngeruk. Kajaba iku, kriptografi kaya ngono - dipercaya, nanging verifikasi. Kanthi produk keamanan-fokus, sampeyan ora bisa mung gumantung ing perpustakaan siap-digawe vendor lan wuta pracaya iku (Nanging, iki topik kanggo liyane ing bagean liya). Ing wayahe, perpustakaan bisa digunakake kanthi apik ing tingkat "tengah" (ngidini sampeyan nggawe panjaluk API).

Nanging, ora bakal akeh kriptografi lan matématika ing seri kiriman iki. Nanging bakal ana akeh rincian teknis lan crutches arsitektur liyane (uga bakal migunani kanggo wong-wong sing ora bakal nulis saka ngeruk, nanging bakal nggunakake perpustakaan ing sembarang basa). Dadi, tujuan utama yaiku nyoba ngetrapake klien saka awal miturut dokumentasi resmi. Yaiku, umpamane kode sumber klien resmi ditutup (maneh, ing bagean kapindho, kita bakal mbukak kanthi luwih rinci babagan topik apa tenan iki. iku kelakon Dadi), nanging, kaya ing jaman biyen, umpamane, ana standar kaya RFC - apa bisa nulis klien miturut spesifikasi dhewe, "tanpa ngintip" menyang kode sumber, malah resmi (Telegram Desktop, seluler ), malah Telethon ora resmi?

Gamelan:

Dokumentasi ... ana? Bener ta?..

Pecahan cathetan kanggo artikel iki wiwit diklumpukake musim panas pungkasan. Kabeh wektu iki ing situs resmi https://core.telegram.org dokumentasi ana ing Layer 23, i.e. macet nang endi wae ing 2014 (elinga, nalika iku durung ana saluran?). Mesthine, ing teori, iki kudu bisa ngetrapake klien kanthi fungsional ing wektu kasebut ing 2014. Nanging sanajan ing negara iki, dokumentasi kasebut, pisanan, ora lengkap, lan kaping pindho, ing panggonan sing mbantah dhewe. Telung wulan kepungkur, ing September 2019 ora sengaja ketemu sing situs wis nganyari gedhe saka dokumentasi, kanggo Layer rampung seger 105, karo cathetan sing saiki kabeh kudu maca maneh. Pancen, akeh artikel sing wis direvisi, nanging akeh sing tetep ora owah. Mulane, nalika maca kritik ing ngisor iki babagan dokumentasi, sampeyan kudu mbudidaya manawa sawetara perkara kasebut ora cocog maneh, nanging sawetara isih cukup. Sawise kabeh, 5 taun ing donya modern ora mung akeh, nanging banget akeh. Wiwit kuwi (utamane yen sampeyan ora nganggep geochat sing dibuwang lan ditangekake wiwit saiki), jumlah metode API ing skema kasebut mundhak saka satus nganti luwih saka rong atus seket!

Ngendi kanggo miwiti minangka penulis enom?

Ora Matter yen sampeyan nulis saka ngeruk utawa nggunakake, contone, siap-digawe perpustakaan kaya Telethon kanggo Python utawa Madeline kanggo PHP, ing kasus apa wae, sampeyan kudu luwih dhisik ndhaftar aplikasi sampeyan - entuk paramèter api_id и api_hash (wong-wong sing kerja karo API VKontakte langsung ngerti) sing server bakal ngenali aplikasi kasebut. Iki kudu kanggo alasan legal, nanging kita bakal pirembagan liyane babagan apa penulis perpustakaan ora bisa nerbitaké ing bagean liya. Mbok menawa sampeyan bakal puas karo nilai tes, sanajan winates banget - nyatane saiki sampeyan bisa ndhaptar nomer sampeyan mung siji aplikasi, supaya ora rush headlong.

Saiki, saka sudut pandang teknis, kita mesthine wis kasengsem yen sawise registrasi kita kudu nampa kabar saka Telegram babagan nganyari dokumentasi, protokol, lsp. Yaiku, siji bisa nganggep yen situs kanthi dermaga mung "dicetak" lan terus kerja khusus karo wong-wong sing wiwit nggawe klien, amarga. luwih gampang. Nanging ora, ora ana sing diamati, ora ana informasi sing teka.

Lan yen sampeyan nulis saka ngeruk, banjur nggunakake paramèter ditampa iku isih adoh. sanadyan https://core.telegram.org/ lan ngomong babagan iki pisanan ing Miwiti, nyatane, sampeyan kudu ngetrapake Protokol MTProto - nanging yen sampeyan pracaya tata letak miturut model OSI ing mburi kaca gambaran umum saka protokol, banjur rampung muspra.

Nyatane, sadurunge MTProto lan sawise, ing sawetara tingkat bebarengan (minangka jaringan asing sing kerja ing kernel OS ngomong, nglanggar lapisan), topik sing gedhe, nglarani lan nggegirisi bakal ana ing dalan ...

Serialisasi binar: TL (Type Language) lan skema, lan lapisan, lan akeh tembung medeni liyane

Topik iki, nyatane, minangka kunci kanggo masalah Telegram. Lan bakal ana akeh tembung elek yen sampeyan nyoba kanggo delve menyang.

Dadi, skema. Yen sampeyan ngelingi tembung iki, ngomong, Skema JSONSampeyan mikir bener. Tujuane padha: sawetara basa kanggo njlèntrèhaké sakumpulan data sing bisa dikirim. Iki, nyatane, ing ngendi persamaan kasebut rampung. Yen saka kaca Protokol MTProto, utawa saka wit sumber klien resmi, kita bakal nyoba mbukak sawetara skema, kita bakal weruh kaya:

int ? = Int;
long ? = Long;
double ? = Double;
string ? = String;

vector#1cb5c415 {t:Type} # [ t ] = Vector t;

rpc_error#2144ca19 error_code:int error_message:string = RpcError;

rpc_answer_unknown#5e2ad36e = RpcDropAnswer;
rpc_answer_dropped_running#cd78e586 = RpcDropAnswer;
rpc_answer_dropped#a43ad8b7 msg_id:long seq_no:int bytes:int = RpcDropAnswer;

msg_container#73f1f8dc messages:vector<%Message> = MessageContainer;

---functions---

set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:bytes = Set_client_DH_params_answer;

ping#7abe77ec ping_id:long = Pong;
ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong;

invokeAfterMsg#cb9f372d msg_id:long query:!X = X;
invokeAfterMsgs#3dc4b4f0 msg_ids:Vector<long> query:!X = X;

account.updateProfile#78515775 flags:# first_name:flags.0?string last_name:flags.1?string about:flags.2?string = User;
account.sendChangePhoneCode#8e57deb flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode;

Wong sing sepisanan ndeleng iki kanthi intuisi bakal ngerteni mung bagean saka apa sing ditulis - uga, iki minangka struktur (sanajan ing endi jenenge, ing sisih kiwa utawa ing sisih tengen?), Ana kothak ing kono, sawise iku. jinis dadi liwat titik loro ... mbokmenawa. Ing kene, ing kurung sudut, bisa uga ana template kaya ing C ++ (nyatane, ora cukup). Lan apa tegese kabeh simbol liyane, tandha pitakon, tanda seru, persentase, kisi (lan temenan tegese beda ing macem-macem papan), saiki ana ing endi wae, nanging ora ana ing endi wae, nomer heksadesimal - lan sing paling penting, kepiye entuk saka iki. sing bener (sing ora bakal ditolak dening server) stream byte? Sampeyan kudu maca dokumentasi (Ya, ana pranala menyang skema ing versi JSON sing cedhak - nanging iki ora nggawe luwih jelas).

Mbukak kaca Serialisasi Data Biner lan terjun ing jagad gaib jamur lan matématika diskrèt, sing padha karo matan ing taun kaping 4. Abjad, jinis, nilai, combinator, combinator fungsional, wangun normal, jinis komposit, jinis polimorfik ... lan iku mung kaca pisanan! Sabanjure ngenteni sampeyan Basa TL, sing, sanajan wis ngemot conto panyuwunan lan respon sing ora pati penting, ora menehi jawaban kanggo kasus sing luwih khas, tegese sampeyan kudu ngliwati retelling matematika sing diterjemahake saka Rusia menyang Inggris ing wolung nested liyane. kaca!

Readers menowo karo basa fungsional lan inferensi jinis otomatis, mesthi, weruh ing gambaran basa iki, malah saka conto, luwih menowo, lan bisa ngomong sing iki umume ora ala ing asas. Bantahan kasebut yaiku:

  • ya wis tujuane muni apik, nanging sayangé ora entuk
  • pendhidhikan ing universitas Rusia beda-beda sanajan ing antarane spesialisasi IT - ora saben wong maca kursus sing cocog
  • Pungkasan, kaya sing bakal kita deleng, ing praktik kasebut ora dibutuhake, amarga mung subset winates malah TL sing diterangake digunakake

Minangka ngandika LeoNerd ing saluran #perl ing jaringan FreeNode IRC, nyoba ngleksanakake gerbang saka Telegram menyang Matrix (terjemahan kutipan kasebut ora akurat saka memori):

Iku ngrasa kaya wong sing pisanan dikenalaké kanggo ngetik teori, dadi bungah lan wiwit nyoba kanggo muter karo, ora tenan peduli yen perlu ing laku.

Deleng dhewe yen perlu kanggo jinis gundhul (int, dawa, lan sapiturute) minangka soko dhasar ora nuwuhake pitakonan - ing pungkasan kudu dileksanakake kanthi manual - contone, ayo nyoba kanggo njupuk saka wong-wong mau. vektor. Yaiku, nyatane, larik, yen sampeyan nelpon barang sing diasilake kanthi jeneng sing tepat.

Nanging sadurunge

Katrangan ringkes babagan subset sintaks TL kanggo sing ora… waca dokumentasi resmi

constructor = Type;
myVec ids:Vector<long> = Type;

fixed#abcdef34 id:int = Type2;

fixedVec set:Vector<Type2> = FixedVec;

constructorOne#crc32 field1:int = PolymorType;
constructorTwo#2crc32 field_a:long field_b:Type3 field_c:int = PolymorType;
constructorThree#deadcrc bit_flags_of_what_really_present:# optional_field4:bit_flags_of_what_really_present.1?Type = PolymorType;

an_id#12abcd34 id:int = Type3;
a_null#6789cdef = Type3;

Tansah miwiti definisi konstruktor, sawise kang, optionally (ing laku, tansah) liwat simbol # kudu CRC32 saka string deskripsi sing dinormalisasi saka jinis sing diwenehake. Sabanjure nerangake deskripsi lapangan, yen ana - jinis bisa kosong. Iku kabeh ends karo tandha witjaksono, jeneng jinis sing konstruktor diwenehi - sing, ing kasunyatan, subtipe - belongs. Jinis ing sisih tengen tandha padha yaiku polimorfik - yaiku, bisa cocog karo sawetara jinis tartamtu.

Yen definisi ana sawise baris ---functions---, banjur sintaksis bakal tetep padha, nanging makna bakal beda: konstruktor bakal dadi jeneng fungsi RPC, kolom bakal dadi paramèter (yaiku, bakal tetep persis struktur sing padha kaya sing diterangake ing ngisor iki. mung bakal dadi makna sing diwenehake), lan "jinis polimorfik ' yaiku jinis asil sing bali. Bener, isih bakal tetep polimorfik - mung ditetepake ing bagean kasebut ---types---, lan konstruktor iki ora bakal dianggep. Ketik overloads saka fungsi disebut dening argumen, i.e. sakperangan alesan, sawetara fungsi kanthi jeneng sing padha nanging teken beda, kaya ing C ++, ora kasedhiya ing TL.

Napa "konstruktor" lan "polimorfik" yen ora OOP? Inggih, nyatane, bakal luwih gampang kanggo wong mikir babagan OOP - jinis polimorfik minangka kelas abstrak, lan konstruktor minangka kelas turunan langsung, uga. final ing terminologi sawetara basa. Ing kasunyatan, mesthi, kene podho karo cara konstruktor overloaded nyata ing basa program OO. Amarga mung ana struktur data ing kene, ora ana cara (sanajan katrangan fungsi lan cara ing ngisor iki cukup bisa nggawe kebingungan ing sirah babagan apa, nanging babagan liya) - sampeyan bisa mikir konstruktor minangka nilai saka kang lagi dibangun ngetik nalika maca stream bita.

Kepiye kedadeyan iki? Deserializer, sing tansah maca 4 bait, ndeleng nilai 0xcrc32 - lan ngerti apa sing bakal kelakon sabanjure field1 karo jinis int, i.e. maca persis 4 bait, ing lapangan overlying iki karo jinis PolymorType maca. weruh 0x2crc32 lan mangerteni yen ana rong lapangan luwih, pisanan long, supaya kita maca 8 bait. Lan banjur maneh jinis Komplek, kang deserialized ing cara sing padha. Tuladhane, Type3 bisa diumumake ing skema sanalika loro konstruktor, masing-masing, kudu ketemu 0x12abcd34, sawise sampeyan kudu maca liyane 4 bait int, utawa 0x6789cdef, sawise iku ora bakal ana apa-apa. Liyane - sampeyan kudu mbuwang pangecualian. Ing kasus apa wae, sawise kita bali menyang maca 4 bait int lapangan field_c в constructorTwo lan kita rampung maca kita PolymorType.

Akhire, yen kejiret 0xdeadcrc kanggo constructorThree, banjur dadi luwih rumit. Lapangan pisanan kita bit_flags_of_what_really_present karo jinis # - nyatane, iki mung alias kanggo jinis nattegese "nomer alami". Sing, nyatane, unsigned int mung kasus, dening cara, nalika nomer unsigned ditemokake ing rencana nyata. Dadi, sabanjure konstruksi kanthi tandha pitakon, tegese iki minangka lapangan - bakal ana ing kabel mung yen bit sing cocog disetel ing lapangan sing dirujuk (kira-kira kaya operator ternary). Dadi, umpamane bit iki aktif, mula sampeyan kudu maca kolom kaya Type, sing ing conto kita duwe 2 konstruktor. Siji kosong (mung dumadi saka pengenal), liyane duwe lapangan ids karo jinis ids:Vector<long>.

Sampeyan bisa uga mikir yen loro cithakan lan generik apik utawa Jawa. Nanging ora. meh. Iki tunggal cilik saka kurung amba ing sirkuit nyata, lan iku ONLY digunakake kanggo Vector. Ing aliran bait, iki bakal dadi 4 bait CRC32 kanggo jinis Vektor dhewe, mesthi padha, banjur 4 bait - jumlah unsur array, banjur unsur kasebut dhewe.

Tambahake kasunyatan manawa serialisasi mesthi ana ing tembung 4 bait, kabeh jinis kelipatan - jinis sing dibangun uga diterangake. bytes и string karo serialization manual saka dawa lan Alignment iki dening 4 - uga, misale jek muni normal lan malah relatif efisien? Sanajan TL diklaim minangka serialisasi binar sing efisien, nanging ing neraka karo wong-wong mau, kanthi ekspansi apa wae, sanajan nilai boolean lan senar karakter tunggal nganti 4 bait, apa JSON isih bakal luwih kenthel? Deleng, sanajan lapangan sing ora perlu bisa dilewati dening panji-panji bit, kabeh apik, lan malah bisa ditambahake kanggo masa depan, apa sampeyan nambah lapangan opsional anyar menyang konstruktor mengko?

Nanging ora, yen sampeyan maca dudu katrangan singkat, nanging dokumentasi lengkap, lan mikir babagan implementasine. Kaping pisanan, konstruktor CRC32 diitung kanthi senar deskripsi teks skema sing dinormalisasi (mbusak spasi putih ekstra, lsp.) - dadi yen lapangan anyar ditambahake, string deskripsi jinis bakal diganti, lan mula CRC32 lan, akibate, serialisasi. Lan apa sing bakal ditindakake klien lawas yen dheweke nampa lapangan kanthi panji anyar, nanging dheweke ora ngerti apa sing kudu ditindakake sabanjure? ..

Kapindho, ayo padha ngelingi CRC32, kang digunakake kene ateges minangka fungsi hash kanggo nemtokake kanthi unik apa jinis sing lagi (de) serialized. Ing kene kita ngadhepi masalah tabrakan - lan ora, kemungkinan ora ana ing 232, nanging luwih akeh. Sapa sing eling yen CRC32 dirancang kanggo ndeteksi (lan mbenerake) kesalahan ing saluran komunikasi, lan kanthi mangkono nambah sifat kasebut kanggo ngrugekake wong liya? Contone, dheweke ora peduli babagan permutasi bait: yen sampeyan ngetung CRC32 saka rong baris, ing kaloro sampeyan bakal ngganti 4 bait pisanan karo 4 bait sabanjure - bakal padha. Nalika kita duwe strings teks saka aksara Latin (lan tandha wacan sethitik) minangka input, lan jeneng iki ora utamané acak, kemungkinan permutasi kuwi tambah akeh.

Miturut cara, sing mriksa apa ana tenan CRC32? Ing salah sawijining sumber awal (sanajan sadurunge Waltman) ana fungsi hash sing dikalikan saben karakter kanthi nomer 239, supaya ditresnani dening wong-wong iki, ha ha!

Akhire, oke, kita temen maujud sing konstruktor karo jinis lapangan Vector<int> и Vector<PolymorType> bakal duwe CRC32 beda. Lan babagan presentasi ing baris? Lan ing babagan teori, apa dadi bagéan saka jinis? Ayo dadi ngomong kita pass Uploaded saka sepuluh ewu nomer, uga, karo Vector<int> kabeh iku cetha, dawa lan liyane 40000 bait. Lan yen iki Vector<Type2>, sing kasusun saka mung siji lapangan int lan iku mung siji ing jinis - apa kita kudu mbaleni 10000xabcdef0 34 kaping lan banjur 4 bait int, utawa basa bisa nampilake iki kanggo kita saka konstruktor fixedVec lan tinimbang 80000 bait, transfer maneh mung 40000?

Iki dudu pitakonan teoretis sing ora aktif - bayangake sampeyan entuk dhaptar pangguna klompok, sing saben duwe id, jeneng ngarep, jeneng mburi - bedane jumlah data sing ditransfer liwat sambungan seluler bisa dadi signifikan. Iki minangka efektifitas serialisasi Telegram sing diiklanake kanggo kita.

Dadi…

Vektor, sing ora bisa disimpulake

Yen sampeyan nyoba kanggo Wade liwat kaca gambaran saka combinators lan bab, sampeyan bakal weruh sing vektor (lan malah matriks) wis resmi nyoba kanggo deduce sawetara sheets liwat tuples. Nanging ing pungkasan padha njaluk hammered, langkah pungkasan dilewati, lan definisi vektor mung diwenehi, kang uga ora kaiket kanggo jinis. Ana apa ing kene? Ing basa program, utamane sing fungsional, cukup khas kanggo njlèntrèhaké struktur kanthi rekursif - kompiler kanthi evaluasi kesed bakal ngerti kabeh lan nindakake. Ing basa serialisasi data nanging EFISIENSI dibutuhake: cukup kanggo nggambarake dhaftar, i.e. struktur saka rong unsur - sing pisanan minangka unsur data, sing kapindho yaiku struktur sing padha utawa spasi kosong kanggo buntut (pack (cons) ing Lisp). Nanging iki mesthi mbutuhake saben unsur tambahan nglampahi 4 bait (CRC32 ing cilik saka TL) kanggo njlèntrèhaké sawijining jinis. Iku gampang kanggo njlèntrèhaké array ukuran tetep, nanging ing cilik saka Uploaded saka dawa sadurunge dingerteni, kita break mati.

Dadi amarga TL ora ngidini sampeyan ngasilake vektor, mula kudu ditambahake ing sisih. Pungkasane dokumentasi kasebut ujar:

Serialisasi tansah nggunakake konstruktor padha "vektor" (const 0x1cb5c415 = crc32 ("vektor t: Tipe # [t] = Vektor t") sing ora gumantung ing Nilai tartamtu saka variabel jinis t.

Nilai parameter opsional t ora melu serialisasi amarga asale saka jinis asil (tansah dikenal sadurunge deserialization).

Delengen kanthi cetha: vector {t:Type} # [ t ] = Vector t - nanging ora ono definisi dhewe ora ngandika sing nomer pisanan kudu padha karo dawa vektor! Lan ora ngetutake saka ngendi wae. Iki diwenehake sing kudu dieling-eling lan dileksanakake nganggo tangan sampeyan. Ing papan liya, dokumentasi malah kanthi jujur ​​nyebutake manawa jinis kasebut palsu:

Pseudotype polimorfik Vektor t minangka "jinis" sing nilai kasebut minangka urutan nilai saka jinis t apa wae, kanthi kothak utawa kosong.

... nanging ora fokus ing. Nalika sampeyan, bosen mlaku-mlaku ing babagan matematika (bisa uga sampeyan ngerti saka kursus universitas), mutusake kanggo ngetung lan nonton carane bener bisa digunakake ing praktik, kesan tetep ana ing sirah sampeyan: ing kene Serius Mathematics adhedhasar , temenan Cool Wong (loro matématikawan -menang saka ACM), lan ora mung sapa. Ing goal - kanggo splurge - wis digayuh.

Miturut cara, babagan nomer. kelingan # iku sinonim nat, nomer alami:

Ana jinis ekspresi (jinisexpr) lan ekspresi angka (nat-expr). Nanging, padha ditetepake kanthi cara sing padha.

type-expr ::= expr
nat-expr ::= expr

nanging ing grammar padha diterangake ing cara sing padha, i.e. prabédan iki maneh kudu eling lan sijine menyang implementasine dening tangan.

Ya, jinis template (vector<int>, vector<User>) duwe pengenal umum (#1cb5c415), i.e. yen sampeyan ngerti telpon diumumake minangka

users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;

banjur sampeyan nunggu ora mung vektor, nanging vektor pangguna. Luwih tepate, kudune ngenteni - ing kode nyata, saben unsur, yen ora jinis gundhul, bakal duwe konstruktor, lan ing cara sing apik ing implementasine bakal perlu kanggo mriksa - lan kita dikirim persis ing saben unsur saka vektor iki. jinis sing? Lan yen ana sawetara jinis PHP, sing bisa ngemot macem-macem jinis ing macem-macem unsur?

Ing wektu iki, sampeyan mulai mikir - apa TL dibutuhake? Mungkin kanggo cart bakal bisa nggunakake serializer manungsa, protobuf padha sing wis ana banjur? Iku teori, ayo kang katon ing laku.

Implementasi TL sing ana ing kode

TL lair ing weteng VKontakte malah sadurunge acara sing kondhang kanthi adol saham Durov lan (mesthi), malah sadurunge pangembangan Telegram. Lan ing open source sumber saka implementasine pisanan sampeyan bisa nemokake akèh crutches lucu. Lan basa kasebut dhewe ditindakake ing kana luwih lengkap tinimbang saiki ing Telegram. Contone, hash ora digunakake ing skema kasebut (tegese pseudotype sing dibangun (kaya vektor) kanthi prilaku nyimpang). Utawa

Templates are not used now. Instead, the same universal constructors (for example, vector {t:Type} [t] = Vector t) are used w

ananging sumangga kita nimbang-nimbang supados kasampurnan gambaripun, supados saged ngambah, kados pundi kemawon, evolusiing Raksasa Pikiran.

#define ZHUKOV_BYTES_HACK

#ifdef ZHUKOV_BYTES_HACK

/* dirty hack for Zhukov request */

Utawa sing ayu iki:

    static const char *reserved_words_polymorhic[] = {

      "alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta", NULL

      };

Fragmen iki babagan cithakan, kayata:

intHash {alpha:Type} vector<coupleInt<alpha>> = IntHash<alpha>;

Iki minangka definisi jinis cithakan hashmap, minangka vektor pasangan int - Type. Ing C++ bakal katon kaya iki:

    template <T> class IntHash {
      vector<pair<int,T>> _map;
    }

dadi, alpha - tembung kunci! Nanging mung ing C ++ sampeyan bisa nulis T, nanging sampeyan kudu nulis alpha, beta ... Nanging ora luwih saka 8 paramèter, Fantasi rampung ing theta. Dadi misale jek yen ing St. Petersburg ana kira-kira dialog ing ngisor iki:

-- Надо сделать в TL шаблоны
-- Бл... Ну пусть параметры зовут альфа, бета,... Какие там ещё буквы есть... О, тэта!
-- Грамматика? Ну потом напишем

-- Смотрите, какой я синтаксис придумал для шаблонов и вектора!
-- Ты долбанулся, как мы это парсить будем?
-- Да не ссыте, он там один в схеме, захаркодить -- и ок

Nanging babagan implementasine pisanan TL "umum". Ayo pindhah menyang pertimbangan implementasine ing klien Telegram sing nyata.

Tembung Basil:

Vasily, [09.10.18 17:07] Paling kabeh, bokong iku panas saka kasunyatan sing padha ngaco munggah Bunch saka abstraksi, banjur padha hammered bolt ing wong, lan overlay codegegerator karo crutches.
Akibaté, pisanan saka dermaga pilot.jpg
Banjur saka kode jekichan.webp

Mesthi, saka wong sing kenal karo algoritma lan matematika, kita bisa nyana yen dheweke wis maca Aho, Ullman, lan kenal karo alat standar industri de facto kanggo nulis kompiler DSL sajrone pirang-pirang dekade, bener? ..

Pengarang telegram-cli yaiku Vitaliy Valtman, kaya sing bisa dingerteni saka kedadeyan format TLO ing njaba watesan (cli), anggota tim - saiki perpustakaan kanggo parsing TL diparengake kapisahapa kesan dheweke TL parser? ..

16.12 04:18 Vasily: miturutku, ana sing durung nguwasani lex + yacc
16.12 04:18 Vasily: yen ora, aku ora bisa nerangake
16.12 04:18 Vasily: uga, utawa padha dibayar kanggo nomer baris ing VK
16.12 04:19 Vasily: 3k+ baris liyane<censored> tinimbang parser

Mungkin pangecualian? Ayo ndeleng carane ora iki klien RESMI — Telegram Desktop:

    nametype = re.match(r'([a-zA-Z.0-9_]+)(#[0-9a-f]+)?([^=]*)=s*([a-zA-Z.<>0-9_]+);', line);
    if (not nametype):
      if (not re.match(r'vector#1cb5c415 {t:Type} # [ t ] = Vector t;', line)):
         print('Bad line found: ' + line);

1100+ garis ing Python, sawetara ekspresi reguler + kasus khusus saka jinis vektor, sing, mesthi, diumumake ing skema kaya sing kudune miturut sintaks TL, nanging dilebokake ing sintaks iki, ngurai luwih akeh. ... Pitakonan iku, apa keganggu karo kabeh mukjijat ikiиluwih puff, yen ora ana sing arep ngurai miturut dokumentasi tho?!

Miturut cara ... Elinga yen kita ngomong babagan mriksa CRC32? Dadi, ing generator kode Telegram Desktop ana dhaptar pangecualian kanggo jinis-jinis kasebut sing diitung CRC32. ora cocog kaya sing dituduhake ing diagram!

Vasily, [18.12 22:49] lan ing kene sampeyan kudu mikir apa TL kasebut dibutuhake.
yen aku pengin kekacoan karo implementasine alternatif, aku bakal miwiti nglebokake baris break, setengah saka parser bakal break ing multi-line definisi
tdesktop, Nanging, banget

Elingi titik bab siji-liners, kita bakal bali menyang sethitik mengko.

Oke, telegram-cli ora resmi, Telegram Desktop resmi, nanging kepiye liyane? Lan sapa ngerti?.. Ing kode klien Android, ora ana parser skema ing kabeh (sing ngundakake pitakonan babagan open source, nanging iki kanggo bagean kapindho), nanging ana sawetara bagéyan lucu liyane kode, nanging bab mau ing bagean ngisor iki.

Pitakonan liyane apa sing ditindakake serialisasi ing praktik? Contone, padha ngaco munggah, mesthi, karo kolom bit lan kolom kondisional:

vasil: flags.0? true
tegese lapangan saiki lan bener yen gendéra disetel

vasil: flags.1? int
tegese lapangan saiki lan kudu deserialized

Vasily: Bokong, aja diobong, apa sing sampeyan lakoni!
Vasily: Nang endi wae ing doc ana sing nyebutake yen bener iku jinis kosong saka nol dawa, nanging ora realistis kanggo ngumpulake soko saka docs.
Vasily: Ora ana uga ing implementasine mbukak, nanging ana akeh crutches lan peraga

Kepiye babagan Telethon? Nggoleki topik MTProto, conto - ana potongan kasebut ing dokumentasi, nanging tandha % iku mung diterangake minangka "cocog karo diwenehi gundhul-jinis", i.e. ing conto ing ngisor iki, salah siji kesalahan, utawa ana sing ora didokumentasikan:

Vasily, [22.06.18/18/38 XNUMX:XNUMX] Ing sak panggonan:

msg_container#73f1f8dc messages:vector message = MessageContainer;

Ing beda:

msg_container#73f1f8dc messages:vector<%Message> = MessageContainer;

Lan iki rong bedane gedhe, ing urip nyata ana sawetara jinis vektor telanjang

Aku durung weruh definisi vektor gundhul lan durung nemoni

Analisis ditulis ing telethon kanthi tangan

Skema dheweke menehi komentar babagan definisi kasebut msg_container

Maneh, pitakonan tetep babagan%. Iku ora diterangake.

Vadim Goncharov, [22.06.18/19/22 XNUMX:XNUMX] lan ing tdesktop?

Vasily, [22.06.18/19/23 XNUMX:XNUMX] Nanging parser TL ing regulator bisa uga ora bakal mangan.

// parsed manually

TL minangka abstraksi sing apik, ora ana sing ngetrapake kanthi lengkap

Lan ora ana% ing versi skema kasebut

Nanging ing kene dokumentasi kasebut mbantah dhewe, dadi xs

Iki ditemokake ing grammar, dheweke mung bisa lali kanggo njlèntrèhaké semantik

Inggih, sampeyan ndeleng dock ing TL, sampeyan ora bisa ngerti tanpa setengah liter

"Inggih, ayo ngomong," sing maca liyane bakal ngomong, "sampeyan ngritik kabeh, mula tuduhake kaya sing dikarepake."

Vasily mangsuli: "minangka parser, aku butuh kaya

    args: /* empty */ { $$ = NULL; }
        | args arg { $$ = g_list_append( $1, $2 ); }
        ;

    arg: LC_ID ':' type-term { $$ = tl_arg_new( $1, $3 ); }
            | LC_ID ':' condition '?' type-term { $$ = tl_arg_new_cond( $1, $5, $3 ); free($3); }
            | UC_ID ':' type-term { $$ = tl_arg_new( $1, $3 ); }
            | type-term { $$ = tl_arg_new( "", $1 ); }
            | '[' LC_ID ']' { $$ = tl_arg_new_mult( "", tl_type_new( $2, TYPE_MOD_NONE ) ); }
            ;

piye wae luwih kaya saka

struct tree *parse_args4 (void) {
  PARSE_INIT (type_args4);
  struct parse so = save_parse ();
  PARSE_TRY (parse_optional_arg_def);
  if (S) {
    tree_add_child (T, S);
  } else {
    load_parse (so);
  }
  if (LEX_CHAR ('!')) {
    PARSE_ADD (type_exclam);
    EXPECT ("!");
  }
  PARSE_TRY_PES (parse_type_term);
  PARSE_OK;
}

utawa

        # Regex to match the whole line
        match = re.match(r'''
            ^                  # We want to match from the beginning to the end
            ([w.]+)           # The .tl object can contain alpha_name or namespace.alpha_name
            (?:
                #             # After the name, comes the ID of the object
                ([0-9a-f]+)    # The constructor ID is in hexadecimal form
            )?                 # If no constructor ID was given, CRC32 the 'tl' to determine it

            (?:s              # After that, we want to match its arguments (name:type)
                {?             # For handling the start of the '{X:Type}' case
                w+            # The argument name will always be an alpha-only name
                :              # Then comes the separator between name:type
                [wd<>#.?!]+  # The type is slightly more complex, since it's alphanumeric and it can
                               # also have Vector<type>, flags:# and flags.0?default, plus :!X as type
                }?             # For handling the end of the '{X:Type}' case
            )*                 # Match 0 or more arguments
            s                 # Leave a space between the arguments and the equal
            =
            s                 # Leave another space between the equal and the result
            ([wd<>#.?]+)     # The result can again be as complex as any argument type
            ;$                 # Finally, the line should always end with ;
            ''', tl, re.IGNORECASE | re.VERBOSE)

iki lexer ENTIRE:

    ---functions---         return FUNCTIONS;
    ---types---             return TYPES;
    [a-z][a-zA-Z0-9_]*      yylval.string = strdup(yytext); return LC_ID;
    [A-Z][a-zA-Z0-9_]*      yylval.string = strdup(yytext); return UC_ID;
    [0-9]+                  yylval.number = atoi(yytext); return NUM;
    #[0-9a-fA-F]{1,8}       yylval.number = strtol(yytext+1, NULL, 16); return ID_HASH;

    n                      /* skip new line */
    [ t]+                  /* skip spaces */
    //.*$                 /* skip comments */
    /*.**/              /* skip comments */
    .                       return (int)yytext[0];

sing. sing luwih sederhana yaiku nggawe sing entheng."

Umumé, ing pungkasan, parser lan generator kode kanggo subset TL sing bener digunakake pas karo udakara 100 baris grammar lan ~ 300 baris generator (kalebu kabeh printkode sing digawe), kalebu jinis barang, jinis informasi kanggo introspeksi ing saben kelas. Saben jinis polimorfik diuripake menyang kelas basa abstrak kosong, lan konstruktor marisi saka iku lan duwe cara kanggo serialization lan deserialization.

Kurang jinis ing jinis basa

Ngetik sing kuat iku apik, ta? Ora, iki dudu holivar (sanajan aku luwih seneng basa dinamis), nanging postulat ing TL. Adhedhasar iku, basa kudu nyedhiyani kabeh jinis mriksa kanggo kita. Inggih, oke, supaya ora wong, nanging implementasine, nanging kudu paling njlèntrèhaké wong. Lan kesempatan apa sing dikarepake?

Kaping pisanan, watesan. Ing kene kita ndeleng dokumentasi kanggo ngunggah file:

Isi binar berkas banjur dipérang dadi bagéan. Kabeh bagean kudu ukuran sing padha ( bagean_ukuran ) lan syarat-syarat ing ngisor iki kudu dipenuhi:

  • part_size % 1024 = 0 (bisa dibagi 1 KB)
  • 524288 % part_size = 0 (512KB kudu bisa dibagi rata miturut ukuran_part)

Bagean pungkasan ora kudu nyukupi kondisi kasebut, yen ukurane kurang saka part_size.

Saben bagean kudu duwe nomer urutan, file_part, kanthi nilai saka 0 nganti 2,999.

Sawise file wis partisi sampeyan kudu milih cara kanggo nyimpen ing server. nggunakake upload.saveBigFilePart ing kasus ukuran lengkap file luwih saka 10 MB lan upload.saveFilePart kanggo file sing luwih cilik.
[...] salah siji saka kesalahan input data ing ngisor iki bisa dibalèkaké:

  • FILE_PARTS_INVALID - Jumlah bagean ora valid. Nilai ora antarane 1..3000

Apa ana sing ana ing skema kasebut? Apa bisa diungkapake kanthi cara TL? Ora. Nanging nyuwun pangapunten, malah Turbo Pascal lawas-gaya bisa njlèntrèhaké jinis diwenehi dening kisaran. Lan dheweke bisa nindakake siji liyane, saiki luwih dikenal minangka enum - jinis sing kasusun saka enumerasi nomer tetep (cilik) saka nilai. Ing basa kaya C - numerik, elinga, kita mung ngomong babagan jinis nganti saiki. nomer. Nanging ana uga array, strings ... contone, iku bakal becik kanggo njlèntrèhaké sing string iki mung bisa ngemot nomer telpon, tengen?

Ora ana sing ana ing TL. Nanging ana, contone, ing JSON Schema. Lan yen wong liya bisa mbantah babagan divisibilitas 512 KB sing isih kudu dicenthang ing kode kasebut, banjur priksa manawa klien mung ora bisa ngirim nomer metu saka jangkauan 1..3000 (lan kesalahan sing cocog ora bisa muncul) mesthi bisa, bener? ..

Miturut cara, babagan kesalahan lan nilai bali. Mripat kabur malah kanggo wong-wong sing wis kerjo karo TL - iku ora langsung esuke kita saben wong fungsi ing TL bener bisa bali ora mung jinis bali diterangake, nanging uga kesalahan. Nanging iki ora bisa disimpulake kanthi TL dhewe. Mesthi wae, bisa dingerteni lan nafig ora perlu ing praktik (sanajan nyatane, RPC bisa ditindakake kanthi cara sing beda-beda, kita bakal bali maneh) - nanging kepiye Kemurnian konsep Matematika Abstrak Jinis saka swarga. donya? .. Dicekel tug - supaya cocog.

Lan pungkasane, kepiye babagan maca? Inggih, ana, ing umum, aku pengin gambaran duwe bener ing skema (maneh, iku ing JSON skema), nanging yen wis tegang karo, banjur apa sisih praktis - paling iku trite kanggo nonton diffs sak nganyari? Waca dhewe ing conto nyata:

-channelFull#76af5481 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int = ChatFull;
+channelFull#1c87a71a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int = ChatFull;

utawa

-message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message;
+message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message;

Ana sing seneng, nanging GitHub, umpamane, ora gelem nyorot owah-owahan ing garis dawa kasebut. Game "golek 10 beda", lan apa otak langsung weruh iku wiwitan lan pungkasan padha ing loro conto, sampeyan kudu tediously maca nang endi wae ing tengah ... Ing mratelakake panemume, iki ora mung ing teori, nanging murni visual katon reged lan ora rapi.

Miturut cara, babagan kemurnian teori. Napa lapangan bit dibutuhake? Apa padha ora katon mambu ala saka sudut pandang teori jinis? Panjelasan bisa dideleng ing versi sadurunge skema. Ing kawitan, ya, iku supaya, jinis anyar digawe kanggo saben wahing. Rudiments iki isih ana ing wangun iki, contone:

storage.fileUnknown#aa963b05 = storage.FileType;
storage.filePartial#40bc6f52 = storage.FileType;
storage.fileJpeg#7efe0e = storage.FileType;
storage.fileGif#cae1aadf = storage.FileType;
storage.filePng#a4f63c0 = storage.FileType;
storage.filePdf#ae1e508d = storage.FileType;
storage.fileMp3#528a0677 = storage.FileType;
storage.fileMov#4b09ebbc = storage.FileType;
storage.fileMp4#b3cea0e4 = storage.FileType;
storage.fileWebp#1081464c = storage.FileType;

Nanging saiki bayangake yen sampeyan duwe 5 kolom opsional ing struktur sampeyan, mula sampeyan butuh 32 jinis kanggo kabeh opsi sing bisa ditindakake. bledosan kombinasi. Dadi kemurnian kristal saka teori TL maneh nabrak bokong wesi saka kasunyatan serialisasi sing atos.

Kajaba iku, ing panggonan iki wong lanang dhewe nglanggar ngetik dhewe. Contone, ing MTProto (bab sabanjure) respon bisa dikompres dening Gzip, kabeh iku wicaksana - kajaba kanggo nglanggar lapisan lan skema. Sawise, lan ora reap ing RpcResult dhewe, nanging isi. Lha kok ngene iki.. Aku kudu ngethok kruk supaya kompresi bisa ing endi wae.

Utawa conto liyane, kita nate nemokake kesalahan - dikirim InputPeerUser tinimbang InputUser. Utawa kosok baline. Nanging makarya! Tegese, server ora peduli babagan jinis kasebut. Kepiye carane bisa? Jawaban, mbok menawa, bakal dijaluk fragmen kode saka telegram-cli:

  if (tgl_get_peer_type (E->id) != TGL_PEER_CHANNEL || (C && (C->flags & TGLCHF_MEGAGROUP))) {
    out_int (CODE_messages_get_history);
    out_peer_id (TLS, E->id);
  } else {    
    out_int (CODE_channels_get_important_history);

    out_int (CODE_input_channel);
    out_int (tgl_get_peer_id (E->id));
    out_long (E->id.access_hash);
  }
  out_int (E->max_id);
  out_int (E->offset);
  out_int (E->limit);
  out_int (0);
  out_int (0);

Ing tembung liyane, kene serialization wis rampung MANUAL, ora digawe kode! Mungkin server dileksanakake kanthi cara sing padha? Apa dudu skema kasebut? Banjur kita nerusake menyang pitakonan sabanjure.

Versiing. Lapisan

Kenapa versi skema diarani lapisan mung bisa ditebak adhedhasar riwayat skema sing diterbitake. Ketoke, ing wiwitan, penulis katon manawa dhasar bisa ditindakake kanthi skema sing ora owah, lan mung yen perlu, nuduhake panjaluk tartamtu sing ditindakake miturut versi sing beda. Ing asas, malah idea apik - lan bakal anyar, kaya, "nyampur ing", lapisan ing lawas. Nanging ayo padha ndeleng carane iku rampung. Bener, ora bisa katon wiwit wiwitan - lucu, nanging skema lapisan dasar ora ana. Lapisan diwiwiti ing 2. Dokumentasi ngandhani babagan fitur TL khusus:

Yen klien ndhukung Layer 2, konstruktor ing ngisor iki kudu digunakake:

invokeWithLayer2#289dd1f6 {X:Type} query:!X = X;

Ing laku, iki tegese sadurunge saben telpon API, int karo nilai 0x289dd1f6 kudu ditambahake sadurunge nomer metode.

Muni OK. Nanging apa sing kedadeyan sabanjure? Banjur teka

invokeWithLayer3#b7475268 query:!X = X;

Dadi apa sabanjure? Minangka gampang kanggo guess

invokeWithLayer4#dea0d430 query:!X = X;

Kocak? Ora, lagi dini kanggo ngguyu, mikir apa saben request saka lapisan liyane kudu kebungkus ing jinis khusus - yen sampeyan duwe kabeh beda, carane liya kanggo mbedakake? Lan nambahake mung 4 bita ing ngarep minangka cara sing efisien. Dadi

invokeWithLayer5#417a57ae query:!X = X;

Nanging ketok yen sawise sawetara wektu bakal dadi sawetara bacchanalia. Lan solusi teka:

Update: Miwiti karo Layer 9, cara helper invokeWithLayerN bisa digunakake bebarengan karo initConnection

Hore! Sawise 9 versi, kita pungkasanipun teka apa wis rampung ing protokol Internet bali ing 80s - rembugan versi sapisan ing wiwitan sambungan!

Dadi apa sabanjure?..

invokeWithLayer10#39620c41 query:!X = X;
...
invokeWithLayer18#1c900537 query:!X = X;

Lan saiki sampeyan bisa ngguyu. Mung sawise lapisan 9 liyane, konstruktor universal kanthi nomer versi pungkasane ditambahake, sing kudu diarani mung sapisan ing wiwitan sambungan, lan makna ing lapisan kasebut katon ilang, saiki mung versi kondisional, kaya nang endi wae liya. Masalah ditanggulangi.

Bener?..

Vasily, [16.07.18/14/01 XNUMX:XNUMX PM] Ing dina Jumuah aku mikir:
Teleserver ngirim acara tanpa panjaluk. Panjaluk kudu dibungkus ing InvokeWithLayer. Server ora mbungkus nganyari, ora ana struktur kanggo mbungkus respon lan nganyari.

Sing. klien ora bisa nemtokake lapisan kang pengin nganyari

Vadim Goncharov, [16.07.18/14/02 XNUMX:XNUMX PM] Apa InvokeWithLayer ora kruk ing prinsip?

Vasily, [16.07.18/14/02 XNUMX:XNUMX PM] Iki mung cara

Vadim Goncharov, [16.07.18/14/02 XNUMX:XNUMX PM] sing tegese lapisan ing wiwitan sesi

Miturut cara, iki nderek saka downgrade klien ora kasedhiya

Nganyari, i.e. jinis Updates ing rencana, iki server ngirim menyang klien ora nanggepi panjalukan API, nanging ing dhewe nalika ana acara. Iki minangka topik rumit sing bakal dibahas ing kiriman liyane, nanging saiki penting kanggo ngerti manawa server nglumpukake Pembaruan sanajan klien offline.

Mangkono, nalika nolak kanggo mbungkus saben paket kanggo nuduhake versi, mula masalah sing bisa kedadeyan kanthi logis:

  • server ngirim nganyari kanggo klien sadurunge klien wis marang versi kang ndhukung
  • apa sing kudu ditindakake sawise nganyarke klien?
  • sing njaminsing mratelakake panemume server bab nomer lapisan ora bakal ngganti ing proses?

Apa sampeyan mikir iki minangka pamikiran teoretis, lan ing praktik iki ora bisa kedadeyan, amarga server ditulis kanthi bener (ing kasus apa wae, dites kanthi apik)? Ha! Ora ketompo carane!

Iki persis sing kita alami ing wulan Agustus. Ing tanggal 14 Agustus, pesen katon yen ana sing dianyari ing server Telegram ... banjur ing log:

2019-08-15 09:28:35.880640 MSK warn  main: ANON:87: unknown object type: 0x80d182d1 at TL/Object.pm line 213.
2019-08-15 09:28:35.751899 MSK warn  main: ANON:87: unknown object type: 0xb5223b0f at TL/Object.pm line 213.

banjur sawetara megabyte saka tumpukan tilak (uga, ing wektu sing padha, logging wis didandani). Sawise kabeh, yen ana sing ora diakoni ing TL sampeyan - iku binar kanthi teken, luwih ing stream KABEH dadi, dekoding bakal dadi mokal. Apa sing kudu ditindakake ing kahanan kaya ngono?

Inggih, bab pisanan sing ana ing pikirane sapa wae yaiku medhot lan nyoba maneh. Ora nulungi. Kita nggoleki CRC32 ing google - iki dadi obyek saka skema 73, sanajan kita nggarap skema 82. Kita kanthi ati-ati ndeleng log kasebut - ana pengenal saka rong skema sing beda!

Mungkin masalah kasebut mung ana ing klien ora resmi? Ora, kita mbukak Telegram Desktop 1.2.17 (versi sing diwenehake karo sawetara distribusi Linux), nulis menyang log Pengecualian: MTP Tipe id sing ora dikarepke #b5223b0f diwaca ing MTPMessageMedia…

Kritik babagan protokol lan pendekatan organisasi Telegram. Bagian 1, teknis: pengalaman nulis klien saka awal - TL, MT

Google nuduhake yen masalah sing padha wis kedadeyan ing salah sawijining klien ora resmi, nanging banjur nomer versi lan, mula, asumsi kasebut beda ...

Dadi apa sing kudu ditindakake? Vasily lan aku pisah: dheweke nyoba kanggo nganyari rencana kanggo 91, Aku mutusaké kanggo ngenteni sawetara dina lan nyoba kanggo 73. Loro cara makarya, nanging wiwit padha empiris, ora ana pangerten carane akeh versi sampeyan kudu mlumpat munggah. utawa mudhun, utawa suwene sampeyan kudu ngenteni.

Mengko, aku bisa ngasilake kahanan kasebut: kita miwiti klien, mateni, nyusun ulang skema menyang lapisan liyane, miwiti maneh, nyekel masalah maneh, bali menyang sing sadurunge - oops, ora ngganti skema lan miwiti maneh klien kanggo sawetara menit bakal mbantu. Sampeyan bakal nampa campuran struktur data saka macem-macem lapisan.

Panjelasan? Nalika sampeyan bisa guess saka macem-macem gejala ora langsung, server kasusun saka macem-macem jinis pangolahan ing mesin beda. Paling kamungkinan, siji saka server sing tanggung jawab kanggo "buffering" sijine ing antrian apa sing luwih dhuwur menehi, lan padha menehi ing rencana sing ana ing wektu generasi. Lan nganti antrian iki "bosok", ora ana sing bisa ditindakake.

Kajaba ... nanging iki crutch elek?!.. Ora, sadurunge mikir bab gagasan edan, ayo kang katon ing kode saka klien resmi. Ing versi Android, kita ora nemokake parser TL, nanging kita nemokake file sing gedhe banget (github ora gelem menehi warna) kanthi serialisasi (de). Punika cuplikan kode:

public static class TL_message_layer68 extends TL_message {
    public static int constructor = 0xc09be45f;
//...
//еще пачка подобных
//...
    public static class TL_message_layer47 extends TL_message {
        public static int constructor = 0xc992e15c;
        public static Message TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) {
            Message result = null;
            switch (constructor) {
                case 0x1d86f70e:
                    result = new TL_messageService_old2();
                    break;
                case 0xa7ab1991:
                    result = new TL_message_old3();
                    break;
                case 0xc3060325:
                    result = new TL_message_old4();
                    break;
                case 0x555555fa:
                    result = new TL_message_secret();
                    break;
                case 0x555555f9:
                    result = new TL_message_secret_layer72();
                    break;
                case 0x90dddc11:
                    result = new TL_message_layer72();
                    break;
                case 0xc09be45f:
                    result = new TL_message_layer68();
                    break;
                case 0xc992e15c:
                    result = new TL_message_layer47();
                    break;
                case 0x5ba66c13:
                    result = new TL_message_old7();
                    break;
                case 0xc06b9607:
                    result = new TL_messageService_layer48();
                    break;
                case 0x83e5de54:
                    result = new TL_messageEmpty();
                    break;
                case 0x2bebfa86:
                    result = new TL_message_old6();
                    break;
                case 0x44f9b43d:
                    result = new TL_message_layer104();
                    break;
                case 0x1c9b1027:
                    result = new TL_message_layer104_2();
                    break;
                case 0xa367e716:
                    result = new TL_messageForwarded_old2(); //custom
                    break;
                case 0x5f46804:
                    result = new TL_messageForwarded_old(); //custom
                    break;
                case 0x567699b3:
                    result = new TL_message_old2(); //custom
                    break;
                case 0x9f8d60bb:
                    result = new TL_messageService_old(); //custom
                    break;
                case 0x22eb6aba:
                    result = new TL_message_old(); //custom
                    break;
                case 0x555555F8:
                    result = new TL_message_secret_old(); //custom
                    break;
                case 0x9789dac4:
                    result = new TL_message_layer104_3();
                    break;

utawa

    boolean fixCaption = !TextUtils.isEmpty(message) &&
    (media instanceof TLRPC.TL_messageMediaPhoto_old ||
     media instanceof TLRPC.TL_messageMediaPhoto_layer68 ||
     media instanceof TLRPC.TL_messageMediaPhoto_layer74 ||
     media instanceof TLRPC.TL_messageMediaDocument_old ||
     media instanceof TLRPC.TL_messageMediaDocument_layer68 ||
     media instanceof TLRPC.TL_messageMediaDocument_layer74)
    && message.startsWith("-1");

Hmm... katon edan. Nanging, mbokmenawa, iki kode kui, banjur oke? .. Nanging mesthi ndhukung kabeh versi! Bener, ora jelas kenapa kabeh dicampur ing siji tumpukan, lan obrolan rahasia, lan liya-liyane _old7 piye wae ora padha karo generasi mesin ... Nanging, paling kabeh aku lunga perkakas saka

TL_message_layer104
TL_message_layer104_2
TL_message_layer104_3

Wong lanang, sampeyan ora bisa mutusake ing siji lapisan?! Inggih, oke, "loro", ayo ngomong, padha dirilis karo kesalahan, uga, mengkono, nanging TELU? .. Langsung maneh ing rake padha? Apa jenis pornografi iki, nuwun? ..

Ngomong-ngomong, kedadeyan sing padha ing sumber Telegram Desktop - yen mangkono, lan pirang-pirang komitmen ing skema kasebut ora ngganti nomer lapisan, nanging ndandani apa wae. Ing kahanan nalika ora ana sumber data resmi kanggo skema, ngendi aku bisa njaluk saka, kajaba kanggo sumber klien resmi? Lan sampeyan njupuk saka ing kono, sampeyan ora bisa yakin manawa skema kasebut bener nganti sampeyan nyoba kabeh cara.

Kepiye carane iki bisa dites? Muga-muga para penggemar unit, fungsional lan tes liyane bakal nuduhake ing komentar.

Oke, ayo goleki kode liyane:

public static class TL_folders_deleteFolder extends TLObject {
    public static int constructor = 0x1c295881;

    public int folder_id;

    public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) {
        return Updates.TLdeserialize(stream, constructor, exception);
    }

    public void serializeToStream(AbstractSerializedData stream) {
        stream.writeInt32(constructor);
        stream.writeInt32(folder_id);
    }
}

//manually created

//RichText start
public static abstract class RichText extends TLObject {
    public String url;
    public long webpage_id;
    public String email;
    public ArrayList<RichText> texts = new ArrayList<>();
    public RichText parentRichText;

    public static RichText TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) {
        RichText result = null;
        switch (constructor) {
            case 0x1ccb966a:
                result = new TL_textPhone();
                break;
            case 0xc7fb5e01:
                result = new TL_textSuperscript();
                break;

Sing "digawe kanthi manual" komentar kene tabet sing mung bagean saka file iki ditulis dening tangan (bisa mbayangno ngipi elek pangopènan?), Lan liyane iku mesin kui. Nanging, banjur ana pitakonan liyane - sumber kasedhiya ora rampung (a la blobs ing GPL ing kernel Linux), nanging iki wis topik kanggo bagean liya.

Nanging cukup. Ayo dadi pindhah menyang protokol ing ndhuwur kang kabeh serialization iki nguber.

MT Proto

Dadi ayo mbukak gambaran umum и gambaran rinci saka protokol lan bab pisanan kita kesandhung iku terminologi. Lan karo turah mbrawah saka kabeh. Umumé, iki misale jek dadi merek dagang Telegram - nelpon samubarang ing macem-macem panggonan kanthi cara sing beda-beda, utawa beda-beda ing siji tembung, utawa kosok balene (contone, ing API tingkat dhuwur yen sampeyan ndeleng paket stiker - iki ora kaya sing sampeyan pikirake).

Contone, "pesen" (pesen) lan "sesi" (sesi) - ing kene tegese beda tinimbang ing antarmuka klien Telegram sing biasa. Ya, kabeh wis jelas karo pesen kasebut, bisa diinterpretasikake babagan OOP, utawa mung diarani tembung "paket" - iki minangka tingkat transportasi sing kurang, ora ana pesen sing padha karo antarmuka, ana akeh. saka layanan. Nanging sesi ... nanging pisanan iku pisanan.

lapisan transportasi

Sing pertama yaiku transportasi. Kita bakal diwenehi babagan 5 opsi:

  • TCP
  • websocket
  • Websocket liwat HTTPS
  • HTTP
  • https

Vasily, [15.06.18/15/04 XNUMX:XNUMX PM] Lan ana uga transportasi UDP, nanging ora didokumentasikan

Lan TCP ing telung varian

Pisanan padha karo UDP liwat TCP, saben paket kalebu nomer urutan lan crc
Napa lara banget maca dermaga ing kreta?

Inggih wonten saiki TCP wis ana 4 varian:

  • Diwasa
  • penengah
  • empuk penengah
  • Full

Oke, Padded intermediate kanggo MTProxy, iki banjur ditambahake amarga ana acara sing dikenal. Nanging kok loro versi liyane (telu total), nalika siji bisa nindakake? Kabeh papat mung beda-beda ing cara nyetel dawa lan muatan saka MTProto utama, sing bakal dibahas luwih lanjut:

  • ing Abridged iku 1 utawa 4 bait nanging ora 0xef banjur awak
  • ing Intermediate iki 4 bita dawa lan lapangan, lan pisanan klien kudu ngirim 0xeeeeeeee kanggo nunjukake yen iku Penengah
  • ing Full, paling gawe ketagihan, saka titik tampilan saka networker a: dawa, nomer urutan, lan ora ONE sing Sejatine MTProto, awak, CRC32. Ya, kabeh iki liwat TCP. Kang menehi kita transportasi dipercaya ing wangun stream serial byte, ora urutan sing dibutuhake, utamané checksums. Oke, saiki aku bakal mbantah manawa TCP duwe checksum 16-bit, mula kedadeyan korupsi data. Apik banget, kajaba kita duwe protokol kriptografi kanthi hash luwih saka 16 bita, kabeh kesalahan kasebut - lan luwih akeh - bakal kejiret yen ora cocog karo SHA ing tingkat sing luwih dhuwur. Ora ana titik ing CRC32 babagan iki.

Ayo mbandhingake Abridged, ing ngendi siji bait dawa bisa, karo Intermediate, sing mbenerake "Yen perlu alignment data 4-byte", sing cukup omong kosong. Apa, dipercaya manawa programer Telegram kikuk banget nganti ora bisa maca data saka soket menyang buffer sing selaras? Sampeyan isih kudu nindakake iki, amarga maca bisa ngasilake jumlah bita (lan ana uga server proxy, contone ...). Utawa, ing sisih liya, kenapa repot karo Abridged yen kita isih duwe bantalan sing akeh banget saka 16 bait ing ndhuwur - simpen 3 bait kadhangkala ?

Siji entuk kesan yen Nikolai Durov seneng banget nyipta sepeda, kalebu protokol jaringan, tanpa perlu praktis.

Pilihan transportasi liyane, kalebu. Web lan MTProxy, kita ora bakal nimbang saiki, bisa uga ing kirim liyane, yen ana panjalukan. Kita mung bakal kelingan saiki babagan MTProxy iki sing ora suwe sawise diluncurake ing 2018, panyedhiya cepet sinau kanggo mblokir persis, sing dimaksudake kanggo mblokir bypassmiturut ukuran paket! Lan uga kasunyatan manawa server MTProxy sing ditulis (maneh Waltman) ing C ora perlu disambungake menyang spesifik Linux, sanajan ora dibutuhake (Phil Kulin bakal konfirmasi), lan server sing padha ing Go utawa ing Node.js pas kurang saka satus baris.

Nanging kita bakal nggawe kesimpulan babagan literasi teknis wong kasebut ing pungkasan bagean kasebut, sawise nimbang masalah liyane. Saiki, ayo pindhah menyang lapisan OSI kaping 5, sesi - sing diselehake ing sesi MTProto.

Tombol, pesen, sesi, Diffie-Hellman

Padha sijine iku ana ora tanggung bener ... A sesi ora padha sesi sing katon ing antarmuka ing Sesi aktif. Nanging supaya.

Kritik babagan protokol lan pendekatan organisasi Telegram. Bagian 1, teknis: pengalaman nulis klien saka awal - TL, MT

Kene kita wis nampa senar saka bita dikenal dawa saka lapisan transportasi. Iki minangka pesen utawa plaintext sing dienkripsi - yen kita isih ana ing tahap negosiasi utama lan bener-bener nindakake. Kang saka Bunch saka konsep disebut "kunci" kita ngomong bab? Ayo njlentrehake masalah iki kanggo tim Telegram dhewe (Nyuwun pangapunten amarga nerjemahake dokumentasiku dhewe saka basa Inggris menyang otak sing kesel jam 4 esuk, luwih gampang ninggalake sawetara frasa kaya ngono):

Ana rong entitas sing diarani sesi - siji ing UI klien resmi ing "sesi saiki", ing ngendi saben sesi cocog karo kabeh piranti / OS.
Kapindho - Sesi MTProto, sing nduweni nomer urutan pesen (ing pangertèn tingkat kurang) ing, lan kang bisa uga ana ing antarane sambungan TCP sing beda. Sawetara sesi MTProto bisa disiyapake bebarengan, contone, kanggo nyepetake download file.

Antarane loro iki mau punika konsep wewenang. Ing kasus degenerate, siji bisa ngomong sing sesi UI iku padha karo wewenangNanging sayangé, iku rumit. Kita katon:

  • Pangguna ing piranti anyar pisanan ngasilake kunci_auth lan mbatesi akun kasebut, contone, kanthi SMS - mulane wewenang
  • Iku kedaden nang pisanan Sesi MTProto, kang wis session_id nang awakmu.
  • Ing langkah iki, kombinasi wewenang и session_id bisa dijenengi conto - tembung iki ditemokake ing dokumentasi lan kode sawetara klien
  • Banjur, klien bisa mbukak sawetara Sesi MTProto miturut padha kunci_auth - menyang DC padha.
  • Banjur ing sawijining dina klien kudu njaluk file saka DC liyane - lan kanggo DC iki bakal digawe anyar kunci_auth !
  • Kanggo marang sistem sing iki ora pangguna anyar ndhaftar, nanging padha wewenang (sesi UI), klien nggunakake panggilan API auth.exportAuthorization ing omah DC auth.importAuthorization ing DC anyar.
  • Kabeh padha, bisa uga ana sawetara sing mbukak Sesi MTProto (saben wong duwe dhewe session_id) menyang DC anyar iki, ing kang kunci_auth.
  • Pungkasan, klien bisa uga pengin Secret Forward Secrecy. Saben kunci_auth ana Permanen tombol - saben DC - lan klien bisa nelpon auth.bindTempAuthKey kanggo nggunakake sawetoro wektu kunci_auth - lan maneh, mung siji temp_auth_key saben DC, umum kanggo kabeh Sesi MTProto menyang DC iki.

kabar, sing uyah (lan uyah mangsa) uga siji ing kunci_auth sing. dibagi antarane kabeh Sesi MTProto menyang DC padha.

Apa tegese "antarane sambungan TCP sing beda"? Iku tegese iki soko kaya cookie wewenang ing situs web - tetep (slamet) akeh sambungan TCP menyang server iki, nanging ing sawijining dina bakal dadi ala. Mung ora kaya HTTP, ing MTProto, ing sesi kasebut, pesen kasebut kanthi urut nomer lan dikonfirmasi, padha mlebu trowongan, sambungan kasebut rusak - sawise nggawe sambungan anyar, server bakal ngirim kabeh sing ora dikirim ing sesi iki. sambungan TCP sadurungé.

Nanging, informasi ing ndhuwur minangka remet sawise pirang-pirang wulan pengadilan. Ing sawetoro wektu, apa kita ngleksanakake klien saka awal? - ayo bali menyang wiwitan.

Dadi kita ngasilake auth_key ing versi Diffie-Hellman saka Telegram. Ayo coba ngerti dokumentasi ...

Vasily, [19.06.18/20/05 1:255] data_with_hash: = SHAXNUMX (data) + data + (sembarang bita acak); supaya dawane padha karo XNUMX bait;
encrypted_data := RSA(data_with_hash, server_public_key); nomer dawa 255-byte (endian amba) diunggahake menyang daya requisite liwat modulus requisite, lan asil disimpen minangka nomer 256-bait.

Dheweke entuk DH obat bius

Ora katon kaya DH wong sehat
Ora ana rong kunci umum ing dx

Inggih, ing pungkasan, kita nemtokake, nanging endapan tetep - bukti karya wis rampung dening klien sing bisa kanggo faktor nomer. Jinis pangayoman marang serangan DoS. Lan tombol RSA mung digunakake sapisan ing siji arah, ateges kanggo enkripsi new_nonce. Nanging nalika operasi sing katon prasaja iki sukses, apa sing kudu sampeyan lakoni?

Vasily, [20.06.18/00/26 XNUMX:XNUMX] Aku durung tekan panjaluk appid

Aku ngirim panjaluk menyang DH

Na, ing dock ing transportasi ditulis sing bisa njawab karo 4 bait kode kesalahan. Lan iku

Inggih, piyambakipun dhateng kula -404, banjur apa?

Punika kula kanggo wong: "nyekel efigna Panjenengan ndhelik karo tombol server karo sidik driji saka kuwi lan kuwi, Aku pengin DH", lan nanggapi stupidly 404

Apa sampeyan mikir babagan respon server kasebut? Apa sing kudu ditindakake? Ora ana sing takon (nanging luwih akeh babagan ing bagean kapindho).

Kene kabeh kapentingan ing dock kanggo nindakake

Aku ora duwe apa-apa liyane apa, Aku mung ngimpi Ngonversi nomer bali lan kasebut

Loro nomer 32 bit. Aku dikempalken kaya wong liya

Nanging ora, iku loro sing perlu pisanan ing baris minangka BE

Vadim Goncharov, [20.06.18/15/49 404:XNUMX PM] lan amarga iki XNUMX?

Vasily, [20.06.18/15/49 XNUMX:XNUMX PM] YA!

Vadim Goncharov, [20.06.18/15/50 XNUMX:XNUMX PM] dadi aku ora ngerti apa sing bisa "ora ditemokake"

Vasily, [20.06.18 15:50] kurang luwih

Aku ora nemokake dekomposisi kasebut dadi pembagi sing prasaja%)

Malah nglaporake kesalahan ora dikuasai

Vasily, [20.06.18/20/18 5:XNUMX] Oh, ana uga MDXNUMX. Wis telung hash beda

Sidik jari kunci diitung kaya ing ngisor iki:

digest = md5(key + iv)
fingerprint = substr(digest, 0, 4) XOR substr(digest, 4, 4)

SHA1 lan sha2

Dadi ayo sijine auth_key 2048 bit ing ukuran kita entuk miturut Diffie-Hellman. Apa sabanjure? Banjur kita ngerteni manawa 1024 bit kunci iki ora digunakake kanthi cara apa wae ... nanging ayo dipikirake saiki. Ing langkah iki, kita duwe rahasia bareng karo server. Analog saka sesi TLS wis ditetepake, prosedur sing larang banget. Nanging server ora ngerti apa-apa bab sing kita durung! Durung, bener wewenang. Sing. yen sampeyan mikir babagan "login-sandi", kaya sing digunakake ing ICQ, utawa paling ora "login-tombol", kaya ing SSH (contone, ing sawetara gitlab / github). We entuk anonim. Lan yen server njawab kita "nomer telpon iki dilayani dening DC liyane"? Utawa malah "nomer telpon sampeyan dilarang"? Sing paling apik sing bisa ditindakake yaiku nyimpen kunci kasebut kanthi pangarep-arep isih migunani lan ora bosok.

Miturut cara, kita "nampa" karo leladen. Contone, apa kita dipercaya server? Apa dheweke palsu? Kita butuh pemeriksaan kriptografi:

Vasily, [21.06.18/17/53 2:XNUMX PM] Dheweke nawakake klien seluler kanggo mriksa nomer XNUMXkbit kanggo kesederhanaan%)

Nanging ora cetha kabeh, nafeijoa

Vasily, [21.06.18/18/02 XNUMX:XNUMX] Dock ora ngomong apa sing kudu ditindakake yen ternyata ora gampang.

Ora ngandika. Ayo ndeleng apa sing ditindakake klien resmi kanggo Android ing kasus iki? A iku apa (lan ya, kabeh file menarik ing kana) - kaya sing diomongake, aku mung bakal ninggalake kene:

278     static const char *goodPrime = "c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c3720fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f642477fe96bb2a941d5bcd1d4ac8cc49880708fa9b378e3c4f3a9060bee67cf9a4a4a695811051907e162753b56b0f6b410dba74d8a84b2a14b3144e0ef1284754fd17ed950d5965b4b9dd46582db1178d169c6bc465b0d6ff9ca3928fef5b9ae4e418fc15e83ebea0f87fa9ff5eed70050ded2849f47bf959d956850ce929851f0d8115f635b105ee2e4e15d04b2454bf6f4fadf034b10403119cd8e3b92fcc5b";
279   if (!strcasecmp(prime, goodPrime)) {

Ora, mesthi ana sawetara ana mriksa kanggo gamblang saka nomer, nanging pribadi aku maneh duwe kawruh cukup ing matématika.

Oke, kita entuk kunci master. Kanggo mlebu, i.e. ngirim panjalukan, iku perlu kanggo nindakake enkripsi luwih, wis nggunakake AES.

Tombol pesen ditetepake minangka 128 bit tengah saka SHA256 awak pesen (kalebu sesi, ID pesen, etc.), kalebu bait padding, prepended dening 32 bait dijupuk saka tombol wewenang.

Vasily, [22.06.18/14/08 XNUMX:XNUMX PM] Rata-rata sundel

Sampeyan entuk auth_key. Kabeh. Luwih maneh ... ora jelas saka dermaga. Bebas bae kanggo sinau kode open source.

Elinga yen MTProto 2.0 mbutuhake padding saka 12 nganti 1024 bait, isih tundhuk karo syarat yen dawa pesen sing diasilake bisa dibagi 16 bait.

Dadi pinten padding sing kudu dilebokake?

Lan ya, ing kene uga, 404 yen ana kesalahan

Yen ana sing nyinaoni diagram lan teks dokumentasi kasebut kanthi teliti, dheweke weruh yen ora ana MAC. Lan AES digunakake ing sawetara mode IGE sing ora digunakake ing ngendi wae. Dheweke, mesthi, nulis babagan iki ing FAQ ... Ing kene, kaya, tombol pesen dhewe ing wektu sing padha hash SHA saka data dekripsi sing digunakake kanggo mriksa integritas - lan yen ana mismatch, dokumentasi kanggo sawetara alesan nyaranake meneng nglirwakake wong (nanging apa keamanan, dumadakan break kita?).

Aku dudu cryptographer, bisa uga ing mode iki ing kasus iki ora ana sing salah saka sudut pandang teoretis. Nanging aku mesthi bisa menehi jeneng masalah praktis, nggunakake conto Telegram Desktop. Iku encrypts cache lokal (kabeh D877F783D5D3EF8C iki) ing cara sing padha pesen ing MTProto (mung ing kasus iki, versi 1.0), i.e. pisanan tombol pesen, banjur data dhewe (lan nang endi wae aside utama auth_key 256 bita, tanpa kang msg_key ora ana guna). Dadi, masalah kasebut katon ing file gedhe. Yaiku, sampeyan kudu nyimpen rong salinan data - ndhelik lan dekripsi. Lan yen ana megabyte, utawa streaming video, contone? .. Skema klasik karo MAC sawise ciphertext ngidini sampeyan maca streaming, langsung nransfer. Lan karo MTProto sampeyan kudu ing wiwitan encrypt utawa decrypt kabeh pesen, mung banjur transfer menyang jaringan utawa disk. Mulane, ing versi paling anyar saka Telegram Desktop ing cache ing user_data format liyane wis digunakake - karo AES ing mode CTR.

Vasily, [21.06.18/01/27 20:XNUMX AM] Oh, aku ngerti apa IGE: IGE minangka upaya pisanan ing "mode enkripsi otentikasi," asline kanggo Kerberos. Iki minangka upaya sing gagal (ora menehi proteksi integritas), lan kudu dicopot. Iki minangka wiwitan saka pencarian XNUMX taun kanggo mode enkripsi otentikasi sing bisa digunakake, sing bubar rampung ing mode kaya OCB lan GCM.

Lan saiki argumen saka sisih cart:

Tim konco Telegram, dipimpin déning Nikolai Durov, kasusun saka enem juara ACM, setengah saka wong-wong mau Ph.Ds ing math. Butuh udakara rong taun kanggo ngluncurake versi MTProto saiki.

Apa sing lucu. Rong taun menyang tingkat ngisor

Utawa kita mung bisa njupuk tls

Oke, ayo ngomong yen kita wis nindakake enkripsi lan nuansa liyane. Apa kita bisa ngirim panjalukan TL-serialized lan deserialize respon? Dadi apa sing kudu dikirim lan kepiye carane? Punika cara initConnectionMungkin iki?

Vasily, [25.06.18/18/46 XNUMX:XNUMX PM] Miwiti sambungan lan nyimpen informasi ing piranti lan aplikasi pangguna.

Iku nampa app_id, device_model, system_version, app_version lan lang_code.

Lan sawetara pitakon

Dokumentasi kaya biasane. Bebas bae kanggo sinau open source

Yen kabeh iku kira-kira cetha karo invokeWithLayer, banjur apa iku? Pranyata yen kita duwe - klien wis ana sing takon marang server - ana panjaluk sing arep dikirim:

Vasily, [25.06.18/19/13 XNUMX:XNUMX] Ditilik dening kode, telpon pisanan kebungkus ing sampah iki, lan sampah dhewe ing invokewithlayer

Napa initConnection ora bisa dadi telpon sing kapisah, nanging kudu dadi bungkus? Ya, ternyata, kudu ditindakake saben-saben ing wiwitan saben sesi, lan ora mung siji-sijine, kaya tombol utama. Nanging! Ora bisa diarani pangguna sing ora sah! Ing kene kita wis tekan tataran sing ditrapake Iki kaca dokumentasi - lan ngandhani yen ...

Mung bagean cilik saka metode API sing kasedhiya kanggo pangguna sing ora sah:

  • auth.sendCode
  • auth.resendCode
  • akun.getPassword
  • auth.checkPassword
  • auth.checkPhone
  • auth.signUp
  • auth.signIn
  • wewenang.importAuthorization
  • help.getConfig
  • help.getNearestDc
  • help.getAppUpdate
  • help.getCdnConfig
  • langpack.getLangPack
  • langpack.getStrings
  • langpack.getDifference
  • langpack.getLanguages
  • langpack.getLanguage

Pisanan saka wong-wong mau auth.sendCode, lan ana panjalukan pisanan sing paling berharga sing bakal dikirim api_id lan api_hash, lan sawise iku kita nampa SMS kanthi kode. Lan yen kita entuk DC salah (nomer telpon negara iki dilayani dening liyane, contone), banjur kita bakal nampa kesalahan karo nomer DC dikarepake. Kanggo ngerteni alamat IP sing kudu disambungake karo nomer DC, kita bakal dibantu help.getConfig. Sawise mung ana 5 entri, nanging sawise acara kondhang taun 2018, jumlah kasebut saya tambah akeh.

Saiki ayo elinga yen kita entuk ing tahap iki ing server anonim. Apa ora larang banget kanggo entuk alamat IP? Napa ora nindakake iki, lan operasi liyane, ing bagean MTProto sing ora dienkripsi? Aku krungu bantahan: "kepiye carane sampeyan bisa mesthekake yen dudu RKN sing bakal nanggapi alamat palsu?". Iki kita kelingan sing, ing kasunyatan, ing klien resmi tombol RSA ditempelake, i.e. sampeyan mung bisa tandha informasi iki. Bener, iki wis rampung kanggo informasi babagan nglewati kunci sing ditampa klien liwat saluran liya (logis yen iki ora bisa ditindakake ing MTProto dhewe, amarga sampeyan isih kudu ngerti ngendi nyambungake).

OK. Ing tahap wewenang klien iki, kita durung sah lan durung ndhaptar aplikasi kita. Kita mung pengin ndeleng apa server nanggapi cara sing kasedhiya kanggo pangguna sing ora sah. Lan kene…

Vasily, [10.07.18 14:45] https://core.telegram.org/method/help.getConfig

config#7dae33e0 [...] = Config;
help.getConfig#c4f9186b = Config;

https://core.telegram.org/api/datacenter

config#232d5905 [...] = Config;
help.getConfig#c4f9186b = Config;

Ing skema, sing pisanan, sing kapindho teka

Ing skema tdesktop, nilai katelu yaiku

Ya, wiwit iku, mesthi, dokumentasi wis dianyari. Sanajan ora suwe, bisa uga ora relevan maneh. Lan kepiye pangembang pemula ngerti? Mungkin yen sampeyan ndhaptar aplikasi sampeyan, dheweke bakal ngandhani sampeyan? Vasily nindakake iki, nanging sayangé, ora ana sing dikirim menyang dheweke (maneh, kita bakal ngomong babagan iki ing bagean kapindho).

... Sampeyan ngeweruhi sing kita wis piye wae dipindhah menyang API, i.e. menyang tingkat sabanjuré lan ora kejawab soko ing tema MTProto? Ora ana sing nggumunake:

Vasily, [28.06.18/02/04 2:XNUMX AM] Mm, lagi nggolek sawetara algoritma ing eXNUMXe

Mtproto nemtokake algoritma lan kunci enkripsi kanggo loro domain, uga minangka struktur bungkus

Nanging dheweke terus-terusan nyampur tingkat tumpukan sing beda-beda, mula ora mesthi jelas ing ngendi mtproto rampung lan level sabanjure diwiwiti.

Carane padha dicampur? Ya, iki kunci sementara sing padha kanggo PFS, umpamane (kanthi cara, Telegram Desktop ora ngerti carane nindakake). Iki dieksekusi kanthi panyuwunan API auth.bindTempAuthKey, i.e. saka tingkat ndhuwur. Nanging ing wektu sing padha, ngganggu enkripsi ing tingkat ngisor - sawise iku, contone, sampeyan kudu nindakake maneh. initConnection etc., iki ora mung panjalukan normal. Dhewe, uga ngirim sing bisa duwe mung ONE tombol sauntara ing DC, sanajan lapangan auth_key_id ing saben pesen ngidini sampeyan ngganti tombol paling sethithik saben pesen, lan server duwe hak kanggo "lali" tombol sauntara kapan wae - apa sing kudu ditindakake ing kasus iki, dokumentasi ora ujar ... ora mungkin duwe sawetara tombol, kaya karo set uyah sing bakal teka, nanging ?..

Ana sawetara bab liyane sing kudu digatekake ing tema MTProto.

Pesen pesen, msg_id, msg_seqno, pangakuan, ping ing arah sing salah lan idiosyncrasies liyane

Napa sampeyan kudu ngerti babagan dheweke? Amarga padha "bocor" siji tingkat sing luwih dhuwur, lan sampeyan kudu ngerti babagan nalika nggarap API. Upaminipun kita ora kasengsem ing msg_key, tingkat ngisor decrypted kabeh kanggo kita. Nanging ing jero data sing didekripsi, kita duwe kolom ing ngisor iki (uga dawane data kanggo ngerti papan padding, nanging iki ora penting):

  • uyah-int64
  • session_id - int64
  • pesen_id - int64
  • seq_no-int32

Elinga yen uyah iku siji kanggo kabeh DC. Kenapa ngerti babagan dheweke? Ora mung amarga ana panjaluk get_future_salts, sing ngandhani interval sing bakal bener, nanging uga amarga yen uyah sampeyan "busuk", banjur pesen (panyuwunan) mung bakal ilang. Server mesthi bakal nglaporake uyah anyar kanthi ngetokake new_session_created - nanging karo lawas sampeyan kudu piye wae ngirim ulang, contone,. Lan pitakonan iki mengaruhi arsitektur aplikasi.

Server diijini nyelehake sesi kabeh lan nanggapi kanthi cara iki amarga akeh alasan. Bener, apa sesi MTProto saka sisih klien? Iki nomer loro session_id и seq_no pesen ing sesi iki. Inggih, lan sambungan TCP ndasari, mesthi. Ayo ngomong klien kita isih ora ngerti carane nindakake akeh perkara, pedhot, nyambung maneh. Yen kedadeyan kasebut kanthi cepet - sesi lawas terus ing sambungan TCP anyar, tambah seq_no luwih. Yen njupuk wektu dawa, server bisa mbusak, amarga ing sisih iku uga antrian, kita ketemu.

Apa kudu seq_no? Oh, iku pitakonan angel. Coba ngerti kanthi jujur ​​apa tegese:

Pesen sing gegandhengan karo isi

Pesen sing mbutuhake pengakuan sing jelas. Iki kalebu kabeh pangguna lan akeh pesen layanan, meh kabeh kajaba wadhah lan pangakuan.

Nomer Urutan Pesen (msg_seqno)

Nomer 32-bit padha karo kaping pindho jumlah pesen "isi" (sing mbutuhake pangenalan, lan utamane sing dudu wadhah) digawe dening pangirim sadurunge pesen iki lan banjur ditambah siji yen pesen saiki pesen sing gegandhengan karo isi. Wadhah tansah digawe sawise kabeh isi; mula, nomer urutane luwih gedhe tinimbang utawa padha karo nomer urutan pesen sing ana ing kono.

Apa jenis sirkus iki karo nambah 1, lan banjur liyane 2? .. Aku curiga yen makna asli "dicokot kurang kanggo ACK, liyane iku nomer", nanging asil ora cukup tengen - utamané, pranyata bisa dikirim sawetara konfirmasi sing duwe padha seq_no! kepriye? Contone, server ngirim soko, ngirim, lan awake dhewe meneng, mung mangsuli pesen konfirmasi layanan babagan nampa pesen kasebut. Ing kasus iki, konfirmasi metu kita bakal duwe nomer metu padha. Yen sampeyan wis kenal karo TCP lan mikir manawa iki kaya edan, nanging kayane ora liar, amarga ing TCP seq_no ora ngganti, lan konfirmasi menyang seq_no sisih liyane - banjur aku cepet-cepet upset. Konfirmasi teka ing MTProto Ora ing seq_no, kaya ing TCP, nanging msg_id !

Apa iki msg_id, sing paling penting saka lapangan iki? ID unik saka pesen, minangka jeneng tabet. Ditetepake minangka nomer 64-dicokot, bit paling pinunjul kang maneh duwe sihir server-ora-server, lan liyane iku timestamp Unix, kalebu bagean pecahan, pindah 32 bit ngiwa. Sing. timestamp saben se (lan pesen kanthi wektu sing beda banget bakal ditolak dening server). Saka iki ternyata, umume, iki minangka pengenal sing global kanggo klien. Nalika - elinga session_id - kita dijamin: Ing kahanan apa wae pesen sing dimaksud kanggo siji sesi ora bisa dikirim menyang sesi sing beda. Tegese, pranyata wis ana telu level — sesi, nomer sesi, id pesen. Kenapa overcomplication, misteri iki gedhe banget.

Supaya msg_id dibutuhake kanggo…

RPC: panjalukan, respon, kesalahan. Konfirmasi.

Nalika sampeyan wis ngeweruhi, ora ana jinis utawa fungsi khusus "nggawe panjalukan RPC" ing ngendi wae ing skema, sanajan ana jawaban. Sawise kabeh, kita duwe pesen sing gegandhengan karo konten! Iku, punapa mawon pesen bisa request! Utawa ora dadi. Sawise kabeh, saben ana msg_id. Lan iki jawabane:

rpc_result#f35c6d01 req_msg_id:long result:Object = RpcResult;

Iki ngendi iku dituduhake kanggo kang pesen iki respon. Mulane, ing tingkat ndhuwur API, sampeyan kudu ngelingi nomer panjalukan sampeyan - Aku ora perlu nerangake manawa karya kasebut ora sinkron, lan bisa uga ana sawetara panjalukan sekaligus, jawaban sing bisa bali ing sembarang urutan? Ing asas, saka iki, lan pesen kesalahan kaya ora buruh, arsitektur konco iki bisa dilacak: server sing njaga sambungan TCP karo sampeyan iku balancer ngarep-mburi, iku ngarahake panjalukan kanggo backends lan ngumpulake maneh bebarengan. message_id. Kabeh katon jelas, logis lan apik ing kene.

Ya?.. Lan yen sampeyan mikir babagan? Sawise kabeh, respon RPC dhewe uga duwe lapangan msg_id! Apa kita kudu bengok-bengok ing server "sampeyan ora nanggapi jawabanku!"? Lan ya, apa ana babagan konfirmasi? Babagan kaca pesen babagan pesen ngandhani apa

msgs_ack#62d6b459 msg_ids:Vector long = MsgsAck;

lan saben sisih kudu nindakake. Nanging ora tansah! Yen sampeyan nampa RpcResult, iku serves minangka pengakuan dhewe. Yaiku, server bisa nanggapi panjaluk sampeyan nganggo MsgsAck - kaya, "Aku nampa." Bisa langsung njawab RpcResult. Bisa uga loro.

Lan ya, sampeyan isih kudu mangsuli jawaban! Konfirmasi. Yen ora, server bakal nganggep ora dikirim lan mbuwang maneh. Malah sawise sambungan maneh. Nanging ing kene, mesthi, pitakonan babagan wektu entek bakal muncul. Ayo ndeleng wong-wong mau mengko.

Ing sawetoro wektu, ayo nimbang kemungkinan kesalahan ing eksekusi query.

rpc_error#2144ca19 error_code:int error_message:string = RpcError;

Oh, ana sing bakal nguwuh, iki format sing luwih manungsa - ana garis! Njupuk wektu. kene dhaptar kasalahannanging mesthi ora lengkap. Saka iku kita sinau sing kode punika - soko kaya Kesalahan HTTP (uga, mesthine, semantik saka respon ora dihormati, ing sawetara panggonan disebarake kanthi kode kanthi acak), lan senar kasebut katon kaya CAPITAL_LETTERS_AND_NUMBERS. Contone, PHONE_NUMBER_OCCUPIED utawa FILE_PART_X_MISSING. Inggih, punika, sampeyan isih kudu baris iki ngurai. Contone, FLOOD_WAIT_3600 bakal tegese sampeyan kudu ngenteni jam, lan PHONE_MIGRATE_5sing nomer telpon karo ater-ater iki kudu kedhaftar ing 5th DC. Kita duwe basa jinis, ta? Kita ora butuh argumentasi saka senar, ekspresi reguler bakal ditindakake, cho.

Maneh, iki ora ana ing kaca pesen layanan, nanging, kaya sing wis biasa karo proyek iki, informasi bisa ditemokake ing kaca dokumentasi liyane. Utawa nuwuhake rasa curiga. Pisanan, deleng, nglanggar ngetik / lapisan - RpcError bisa nandur modal ing RpcResult. Apa ora ing njaba? Apa wis kita ora dijupuk menyang akun?.. Patut, ngendi jaminan sing RpcError bisa uga ora nandur modal ing RpcResult, nanging langsung utawa nested ing jinis liyane? iku kurang req_msg_id ? ..

Nanging ayo terus babagan pesen layanan. Klien bisa uga nganggep manawa server wis mikir suwe, lan njaluk panjaluk sing apik banget:

rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;

Ana telung jawaban sing bisa, maneh intersecting karo mekanisme konfirmasi, kanggo nyoba kanggo ngerti apa padha kudu (lan apa dhaftar jinis sing ora mbutuhake konfirmasi ing umum), maca ditinggalake minangka peer (cathetan: ing informasi ing sumber Telegram Desktop ora lengkap).

Kecanduan: Status Posting Pesen

Umume, akeh panggonan ing TL, MTProto lan Telegram umume ninggalake rasa nekad, nanging amarga sopan santun, wicaksana lan liya-liyane. alus skills kita sopan katahan bisu babagan, lan saru ing dialog padha censored. Nanging, panggonan ikiОpaling kaca babagan pesen babagan pesen nimbulaké kejut malah kanggo kula, sing wis digunakake karo protokol jaringan kanggo dangu lan wis katon pit saka macem-macem derajat lengkungan.

Diwiwiti kanthi aman, kanthi konfirmasi. Sabanjure, kita marang bab

bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int = BadMsgNotification;
bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long = BadMsgNotification;

Ya, saben wong sing miwiti nggarap MTProto kudu ngadhepi, ing siklus "didandani - dikompilasi - diluncurake", entuk kesalahan nomer utawa uyah sing rusak sajrone suntingan minangka perkara umum. Nanging, ana rong titik ing kene:

  1. Iku nderek sing pesen asli ilang. We kudu pager sawetara antrian, kita bakal nimbang iki mengko.
  2. Apa nomer kesalahan aneh kasebut? 16, 17, 18, 19, 20, 32, 33, 34, 35, 48, 64… endi nomer liyane, Tommy?

Dokumentasi kasebut nyatakake:

Tujuane yaiku yen nilai error_code diklompokaké (error_code >> 4): contone, kode 0x40 - 0x4f cocog karo kesalahan ing dekomposisi wadhah.

nanging, pisanan, shift ing arah liyane, lan sareh, iku ora Matter ngendi liyane saka kode? Ing sirah penulis?.. Nanging, iki trifles.

Kecanduan diwiwiti ing pesen status kirim lan ngirim salinan:

  • Panjaluk Informasi Status Pesen
    Yen salah siji pihak durung nampa informasi babagan status pesen sing metu kanggo sawetara wektu, bisa uga njaluk kanthi jelas saka pihak liya:
    msgs_state_req#da69fb52 msg_ids:Vector long = MsgsStateReq;
  • Pesen Informasi babagan Status Pesen
    msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
    Kene, info minangka string sing ngemot persis siji bait status pesen kanggo saben pesen saka dhaptar msg_ids sing mlebu:

    • 1 = ora ana sing ngerti babagan pesen kasebut (msg_id kurang banget, pihak liya bisa uga lali)
    • 2 = pesen ora ditampa (msg_id kalebu ing sawetara pengenal sing disimpen; Nanging, pihak liyane mesthi ora nampa pesen kaya ngono)
    • 3 = pesen ora ditampa (msg_id dhuwur banget; nanging pihak liyane mesthi durung nampa)
    • 4 = pesen sing ditampa (cathetan yen respon iki uga minangka pangakuan kuitansi)
    • +8 = pesen wis diakoni
    • +16 = pesen ora mbutuhake diakoni
    • +32 = Pitakonan RPC sing ana ing pesen sing diproses utawa diproses wis rampung
    • +64 = respon sing gegandhengan karo isi kanggo pesen sing wis digawe
    • +128 = pihak liya ngerti yen pesen wis ditampa
      Tanggapan iki ora mbutuhake pengakuan. Iki minangka pengakuan saka msgs_state_req sing cocog, ing dhewe.
      Elinga yen tiba-tiba pihak liya ora duwe pesen sing katon kaya wis dikirim, pesen kasebut mung bisa dikirim maneh. Sanajan pihak liya kudu nampa rong salinan pesen ing wektu sing padha, duplikat kasebut bakal diabaikan. (Yen kakehan wektu wis liwati, lan msg_id asli wis ora bener maneh, pesen kudu kebungkus ing msg_copy).
  • Komunikasi Sukarela Status Pesen
    Salah siji pihak bisa kanthi sukarela ngandhani pihak liya babagan status pesen sing dikirim dening pihak liya.
    msgs_all_info#8cc0d131 msg_ids:Vector long info:string = MsgsAllInfo
  • Ekstensi Komunikasi Sukarela Status Siji Pesen
    ...
    msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
    msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
  • Panyuwunan Eksplisit kanggo Kirim maneh Pesen
    msg_resend_req#7d861a08 msg_ids:Vector long = MsgResendReq;
    Pihak remot langsung nanggapi kanthi ngirim maneh pesen sing dijaluk [...]
  • Panyuwunan Eksplisit kanggo Kirim maneh Jawaban
    msg_resend_ans_req#8610baeb msg_ids:Vector long = MsgResendReq;
    Pihak remot langsung nanggapi kanthi ngirim maneh jawaban menyang pesen sing dijaluk [...]
  • Salinan Pesen
    Ing sawetara kahanan, pesen lawas kanthi msg_id sing ora valid kudu dikirim maneh. Banjur, dibungkus ing wadhah salinan:
    msg_copy#e06046b2 orig_message:Message = MessageCopy;
    Sawise ditampa, pesen kasebut diproses kaya bungkuse ora ana. Nanging, yen wis dingerteni manawa pesen orig_message.msg_id ditampa, mula pesen anyar ora diproses (nalika ing wektu sing padha, lan orig_message.msg_id diakoni). Nilai orig_message.msg_id kudu luwih murah tinimbang msg_id wadhah.

Ayo kita malah tetep bisu babagan kasunyatan sing ing msgs_state_info maneh, kuping saka TL durung rampung kelet metu (kita needed vektor bita, lan ing ngisor rong bit enum, lan ing panji bit lawas). Intine ana liyane. Apa ana sing ngerti sebabe kabeh iki ditindakake ing klien nyata perlu?.. Kanthi kangelan, nanging sampeyan bisa mbayangno sawetara entuk manfaat yen wong melu debugging, lan ing mode interaktif - takon server apa lan carane. Nanging panjalukan diterangake ing kene mudik.

Saka iki, saben sisih kudu ora mung encrypt lan ngirim pesen, nanging uga nyimpen data babagan, babagan jawaban, lan wektu sing ora dingerteni. Dokumentasi ora nggambarake wektu utawa aplikasi praktis fitur kasebut. ora ana cara. Sing paling nggumunake yaiku digunakake ing kode klien resmi! Ketoke, dheweke dikandhani babagan sing ora kalebu ing dokumentasi mbukak. Ngerti saka kode kenapa, ora ana maneh kaya prasaja ing kasus TL - iki dudu bagean sing diisolasi kanthi logis, nanging potongan sing ana gandhengane karo arsitektur aplikasi, yaiku. mbutuhake wektu luwih akeh kanggo mangerteni kode aplikasi.

Ping lan timing. antrian.

Saka kabeh, yen sampeyan ngelingi guess babagan arsitektur server (distribusi panjalukan ing backends), ana bab sing rada kusam - sanajan kabeh jaminan pangiriman ing TCP (salah siji data wis dikirim, utawa sampeyan bakal dilaporake babagan break, nanging data bakal dikirim nganti wayahe masalah), sing konfirmasi ing MTProto dhewe - ora njamin. Server bisa gampang ilang utawa mbuwang pesen sampeyan, lan ora ana sing bisa ditindakake, mung kanggo pager kruk saka macem-macem jinis.

Lan pisanan kabeh - antrian pesen. Sepisanan, kabeh wis jelas wiwit wiwitan - pesen sing durung dikonfirmasi kudu disimpen lan diremehake. Lan sawise wektu apa? Lan jester ngerti dheweke. Mbok menawa pesen layanan ketagihan bisa ngatasi masalah iki kanthi crutches, umpamane, ing Telegram Desktop ana sekitar 4 antrian sing cocog karo wong-wong mau (bisa uga luwih akeh, kaya sing wis kasebut, mula sampeyan kudu nyelidiki kode lan arsitektur kanthi luwih serius; ing wektu sing padha. wektu, kita ngerti sing ora bisa dijupuk minangka sampel, nomer tartamtu saka jinis saka rencana MTProto ora digunakake ing).

Yagene iki kedadeyan? Mbokmenawa, programer server ora bisa njamin linuwih ing kluster, utawa paling ora malah buffering ing balancer ngarep, lan ngalih masalah iki kanggo klien. Saka desperation, Vasily nyoba kanggo ngleksanakake pilihan alternatif, mung loro antrian, nggunakake algoritma saka TCP - ngukur RTT kanggo server lan nyetel ukuran "jendhela" (ing pesen) gumantung saka jumlah panjalukan sing ora diakoni. Yaiku, heuristik sing kasar kanggo ngira beban server - pira panjaluk kita bisa dikunyah bebarengan lan ora ilang.

Nah, iku, sampeyan ngerti, ta? Yen sampeyan kudu ngleksanakake TCP maneh ing ndhuwur protokol sing dianggo liwat TCP, iki nuduhake protokol sing dirancang banget.

Oh ya, kenapa luwih saka siji antrian dibutuhake, lan umume, apa tegese kanggo wong sing nggarap API tingkat dhuwur? Deleng, sampeyan nggawe panjaluk, sampeyan nggawe serial, nanging asring ora bisa dikirim langsung. Kenging punapa? Amarga jawabane bakal msg_id, sing sementaraаAku label, janjian sing luwih apik kanggo nundha paling pungkasan - dumadakan server bakal nolak amarga ora cocog wektu antarane kita lan (mesthi, kita bisa nggawe crutch sing ngganti wektu kita saka saiki. menyang wektu server kanthi nambahake delta sing diwilang saka respon server - klien resmi nindakake iki, nanging cara iki kasar lan ora akurat amarga buffering). Dadi yen sampeyan nggawe panjalukan kanthi nelpon fungsi lokal saka perpustakaan, pesen kasebut ngliwati tahapan ing ngisor iki:

  1. Dumunung ing antrian sing padha lan ngenteni enkripsi.
  2. ditunjuk msg_id lan pesen menyang antrian liyane - bisa nerusake; ngirim menyang soket.
  3. a) Server mangsuli MsgsAck - pesen dikirim, kita mbusak saka "antrian liyane".
    b) Utawa kosok balene, dheweke ora seneng, dheweke mangsuli badmsg - kita ngirim maneh saka "antrian liyane"
    c) Boten dikenal, iku perlu kanggo resend pesen saka antrian liyane - nanging ora ngerti persis nalika.
  4. Server pungkasane mangsuli RpcResult - respon nyata (utawa kesalahan) - ora mung dikirim, nanging uga diproses.

Mbokmenawa, panggunaan wadhah bisa ngatasi masalah kasebut. Iki nalika akeh pesen dikempalken dadi siji, lan server nanggapi kanthi pangakuan kabeh bebarengan, kanthi siji. msg_id. Nanging dheweke uga bakal nolak paket iki, yen ana sing salah, uga kabeh.

Lan ing wektu iki, pertimbangan non-teknis bisa ditindakake. Saka pengalaman, kita wis ndeleng akeh crutches, lan ing Kajaba iku, saiki kita bakal weruh liyane conto saran ala lan arsitektur - ing kahanan kaya mengkono iku worth dipercaya lan nggawe keputusan kuwi? Pitakonan iku retorika (mesthi ora).

Apa kita ngomong babagan? Yen ing topik "pesen pecandu babagan pesen" sampeyan isih bisa spekulasi kanthi bantahan kaya "sampeyan bodho, sampeyan ora ngerti ide sing apik!" (dadi pisanan nulis dokumentasi, minangka wong normal kudu, karo alesan lan conto ijol-ijolan paket, banjur kita bakal ngomong), banjur wektu / wektu entek masalah sejatine sifate praktis lan tartamtu, kabeh wis dawa dikenal kene. Nanging apa sing dicritakake dokumentasi babagan wektu entek?

Server biasane ngakoni panrimo pesen saka klien (biasane, pitakon RPC) nggunakake respon RPC. Yen respon wis suwe, server bisa ngirim pangakuan resi, lan mengko, respon RPC dhewe.

Klien biasane ngakoni panrimo pesen saka server (biasane, respon RPC) kanthi nambahake pangakuan menyang pitakon RPC sabanjure yen ora dikirim kasep (yen digawe, ucapake, 60-120 detik sawise ditampa. pesen saka server). Nanging, yen kanggo wektu sing suwe ora ana alesan kanggo ngirim pesen menyang server utawa yen ana akeh pesen sing ora diakoni saka server (ngomongake, luwih saka 16), klien ngirimake pengakuan sing mandiri.

... Aku nerjemahake: awake dhewe ora ngerti pira lan butuhe, wah, ayo padha ngira-ira supaya kaya mangkene.

Lan babagan ping:

Pesen Ping (PING/PONG)

ping#7abe77ec ping_id:long = Pong;

Tanggepan biasane bali menyang sambungan sing padha:

pong#347773c5 msg_id:long ping_id:long = Pong;

Pesen kasebut ora mbutuhake pangakuan. A pong ditularaké mung kanggo nanggepi ping nalika ping bisa diwiwiti dening salah siji sisih.

Panutup Sambungan Tundha + PING

ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong;

Kerjane kaya ping. Kajaba iku, sawise iki ditampa, server miwiti timer kang bakal nutup sambungan saiki disconnect_delay detik mengko kajaba iku nampa pesen anyar saka jinis padha kang otomatis ngreset kabeh timer sadurungé. Yen klien ngirim ping iki sapisan saben 60 detik, contone, bisa nyetel disconnect_delay padha karo 75 detik.

Apa sampeyan wis ora atine?! Ing 60 detik, sepur bakal mlebu stasiun, nyelehake lan njupuk penumpang, lan maneh kelangan sambungan ing trowongan. Ing 120 detik, nalika sampeyan lagi ngubengi, dheweke bakal teka ing liyane, lan sambungan kasebut bakal rusak. Inggih, cetha saka ngendi sikil tuwuh - "Aku krungu muni, nanging aku ora ngerti ngendi iku", ana algoritma Nagle lan pilihan TCP_NODELAY, sing dimaksudake kanggo karya interaktif. Nanging, nuwun sewu, tundha nilai standar - 200 Millidetik. Yen pancene pengin nggambarake sing padha lan ngirit pasangan paket - uga, mateni, paling ora 5 detik, utawa apa wae wektu entek pesen "Panganggo ngetik ..." saiki padha karo. Nanging ora luwih.

Lan pungkasane, ping. Yaiku, mriksa liveliness sambungan TCP. Iku lucu, nanging udakara 10 taun kepungkur aku nulis teks kritis babagan utusan hostel fakultas kita - ana penulis uga ping server saka klien, lan ora kosok balene. Nanging siswa taun katelu iku siji, lan kantor internasional liyane, ta? ..

Kaping pisanan, program pendidikan cilik. Sambungan TCP, yen ora ana ijol-ijolan paket, bisa urip nganti pirang-pirang minggu. Iki apik lan ala, gumantung saka tujuane. Ya, yen sampeyan duwe sambungan SSH menyang server mbukak, sampeyan tangi saka komputer, reboot router daya, bali menyang panggonan sampeyan - sesi liwat server iki ora rusak (ora ngetik apa-apa, ora ana paket), trep. Iku ala yen ana ewu klien ing server, saben wong njupuk sumber daya (halo Postgres!), Lan host klien bisa uga wis rebooted dangu - nanging kita ora bakal ngerti.

Sistem Chat / IM kagungane kasus kapindho kanggo liyane, alesan tambahan - status online. Yen pangguna "ambruk", perlu kanggo ngandhani interlocutors babagan iki. Yen ora, bakal ana kesalahan sing digawe dening pencipta Jabber (lan didandani sajrone 20 taun) - pangguna pedhot, nanging terus nulis pesen marang dheweke, percaya yen dheweke online (sing uga ilang ing sawetara menit sadurunge. istirahat ditemokake). Ora, pilihan TCP_KEEPALIVE, sing akeh wong sing ora ngerti cara kerja timer TCP, muncul ing endi wae (kanthi nyetel nilai liar kaya puluhan detik), ora bakal mbantu ing kene - sampeyan kudu mesthekake yen ora mung kernel OS. mesin pangguna urip, nanging uga fungsi normal, ing bisa njawab, lan aplikasi dhewe (apa sampeyan mikir iku ora bisa beku? Telegram Desktop ing Ubuntu 18.04 wis tabrakan kanggo kula bola-bali).

Mulane sampeyan kudu ping server klien, lan ora kosok balene - yen klien nindakake iki, nalika sambungan rusak, ping ora bakal dikirim, goal ora entuk.

Lan apa sing kita deleng ing Telegram? Kabeh iku persis ngelawan! Inggih, i.e. formal, mesthi, loro-lorone bisa ping saben liyane. Ing laku, klien nggunakake kruk ping_delay_disconnect, kang jago timer ing server. Nuwun sewu, dudu urusan klien kanggo nemtokake suwene dheweke pengin manggon ing kana tanpa ping. Server, adhedhasar muatane, luwih ngerti. Nanging, mesthine, yen sampeyan ora melas kanggo sumber daya, banjur Pinokio ala dhewe, lan crutch bakal mudhun ...

Carane ngirim wis dirancang?

Aku percaya yen kasunyatan ing ndhuwur cukup jelas nuduhake kompetensi sing ora dhuwur banget saka tim Telegram / VKontakte ing bidang transportasi (lan ngisor) tingkat jaringan komputer lan kualifikasi sing kurang ing masalah sing relevan.

Napa dadi rumit, lan kepiye arsitek Telegram bisa mbantah? Kasunyatan manawa dheweke nyoba nggawe sesi sing bisa urip ing sambungan TCP, yaiku, apa sing ora dikirim saiki, bakal dikirim mengko. Dheweke uga nyoba nggawe transportasi UDP, sanajan dheweke ngalami kesulitan lan ditinggal (mulane dokumentasi kosong - ora ana sing kudu dibanggakake). Nanging amarga kurang pangerten babagan cara jaringan umume lan TCP khusus, ing ngendi sampeyan bisa ngandelake, lan ing ngendi sampeyan kudu nindakake dhewe (lan kepiye), lan nyoba nggabungake iki karo kriptografi "siji dijupuk saka loro. manuk kanthi watu siji" - jenazah kaya ngono.

Carane kudu wis? Adhedhasar kasunyatan sing msg_id iku timestamp sing cryptographically perlu kanggo nyegah serangan muter maneh, iku kesalahan kanggo masang fungsi pengenal unik. Mula, tanpa ngganti arsitektur saiki kanthi drastis (nalika Utas Pembaruan dibentuk, iki minangka topik API tingkat dhuwur kanggo bagean liyane saka seri kiriman iki), siji kudu:

  1. Server sing nyekel sambungan TCP menyang klien njupuk tanggung jawab - yen sampeyan nyuda soket, mangga ngakoni, ngolah utawa ngasilake kesalahan, ora rugi. Banjur konfirmasi ora vektor saka id kang, nanging mung "seq_no pungkasan ditampa" - mung nomer, kaya ing TCP (loro nomer - seq dhewe lan dikonfirmasi). Kita mesthi ana ing sesi, ta?
  2. Stempel wektu kanggo nyegah serangan muter maneh dadi lapangan kapisah, a la nonce. Dipriksa, nanging ora ana sing kena pengaruh. Cukup lan uint32 - yen uyah kita diganti paling sethithik saben setengah dina, kita bisa nyedhiakke 16 bit kanggo bit ngisor saka bagéan integer wektu saiki, liyane - kanggo bagean pecahan saka detik (kaya saiki).
  3. ditarik maneh msg_id ing kabeh - saka sudut pandang mbedakake panjalukan ing backends, ana, pisanan, id klien, lan kapindho, id sesi, lan concatenate wong. Mulane, minangka pengenal panjalukan, mung siji sing cukup seq_no.

Uga dudu pilihan sing paling apik, acak lengkap bisa dadi pengenal - iki wis rampung ing API tingkat dhuwur nalika ngirim pesen, kanthi cara. Iku bakal luwih apik kanggo remake arsitektur saka relatif kanggo Absolute kabeh, nanging iki topik kanggo bagean liyane, ora kirim iki.

API?

Ta-dah! Dadi, sawise ngliwati dalan sing kebak rasa lara lan kruk, pungkasane bisa ngirim panjalukan menyang server lan nampa jawaban apa wae, uga nampa nganyari saka server (ora nanggepi panjaluk, nanging iku ngirim kita dhewe, kayata PUSH, yen wong dadi luwih cetha).

Manungsa waé, saiki bakal ana siji-sijine conto Perl ing artikel kasebut! (kanggo sing ora kenal karo sintaks, argumen pisanan kanggo mberkahi yaiku struktur data obyek, sing nomer loro yaiku kelas):

2019.10.24 12:00:51 $1 = {
'cb' => 'TeleUpd::__ANON__',
'out' => bless( {
'filter' => bless( {}, 'Telegram::ChannelMessagesFilterEmpty' ),
'channel' => bless( {
'access_hash' => '-6698103710539760874',
'channel_id' => '1380524958'
}, 'Telegram::InputPeerChannel' ),
'pts' => '158503',
'flags' => 0,
'limit' => 0
}, 'Telegram::Updates::GetChannelDifference' ),
'req_id' => '6751291954012037292'
};
2019.10.24 12:00:51 $1 = {
'in' => bless( {
'req_msg_id' => '6751291954012037292',
'result' => bless( {
'pts' => 158508,
'flags' => 3,
'final' => 1,
'new_messages' => [],
'users' => [],
'chats' => [
bless( {
'title' => 'Хулиномика',
'username' => 'hoolinomics',
'flags' => 8288,
'id' => 1380524958,
'access_hash' => '-6698103710539760874',
'broadcast' => 1,
'version' => 0,
'photo' => bless( {
'photo_small' => bless( {
'volume_id' => 246933270,
'file_reference' => '
'secret' => '1854156056801727328',
'local_id' => 228648,
'dc_id' => 2
}, 'Telegram::FileLocation' ),
'photo_big' => bless( {
'dc_id' => 2,
'local_id' => 228650,
'file_reference' => '
'secret' => '1275570353387113110',
'volume_id' => 246933270
}, 'Telegram::FileLocation' )
}, 'Telegram::ChatPhoto' ),
'date' => 1531221081
}, 'Telegram::Channel' )
],
'timeout' => 300,
'other_updates' => [
bless( {
'pts_count' => 0,
'message' => bless( {
'post' => 1,
'id' => 852,
'flags' => 50368,
'views' => 8013,
'entities' => [
bless( {
'length' => 20,
'offset' => 0
}, 'Telegram::MessageEntityBold' ),
bless( {
'length' => 18,
'offset' => 480,
'url' => 'https://alexeymarkov.livejournal.com/[url_вырезан].html'
}, 'Telegram::MessageEntityTextUrl' )
],
'reply_markup' => bless( {
'rows' => [
bless( {
'buttons' => [
bless( {
'text' => '???? 165',
'data' => 'send_reaction_0'
}, 'Telegram::KeyboardButtonCallback' ),
bless( {
'data' => 'send_reaction_1',
'text' => '???? 9'
}, 'Telegram::KeyboardButtonCallback' )
]
}, 'Telegram::KeyboardButtonRow' )
]
}, 'Telegram::ReplyInlineMarkup' ),
'message' => 'А вот и новая книга! 
// [текст сообщения вырезан чтоб не нарушать правил Хабра о рекламе]
напечатаю.',
'to_id' => bless( {
'channel_id' => 1380524958
}, 'Telegram::PeerChannel' ),
'date' => 1571724559,
'edit_date' => 1571907562
}, 'Telegram::Message' ),
'pts' => 158508
}, 'Telegram::UpdateEditChannelMessage' ),
bless( {
'pts' => 158508,
'message' => bless( {
'edit_date' => 1571907589,
'to_id' => bless( {
'channel_id' => 1380524958
}, 'Telegram::PeerChannel' ),
'date' => 1571807301,
'message' => 'Почему Вы считаете Facebook плохой компанией? Можете прокомментировать? По-моему, это шикарная компания. Без долгов, с хорошей прибылью, а если решат дивы платить, то и еще могут нехило подорожать.
Для меня ответ совершенно очевиден: потому что Facebook делает ужасный по качеству продукт. Да, у него монопольное положение и да, им пользуется огромное количество людей. Но мир не стоит на месте. Когда-то владельцам Нокии было смешно от первого Айфона. Они думали, что лучше Нокии ничего быть не может и она навсегда останется самым удобным, красивым и твёрдым телефоном - и доля рынка это красноречиво демонстрировала. Теперь им не смешно.
Конечно, рептилоиды сопротивляются напору молодых гениев: так Цукербергом был пожран Whatsapp, потом Instagram. Но всё им не пожрать, Паша Дуров не продаётся!
Так будет и с Фейсбуком. Нельзя всё время делать говно. Кто-то когда-то сделает хороший продукт, куда всё и уйдут.
#соцсети #facebook #акции #рептилоиды',
'reply_markup' => bless( {
'rows' => [
bless( {
'buttons' => [
bless( {
'data' => 'send_reaction_0',
'text' => '???? 452'
}, 'Telegram::KeyboardButtonCallback' ),
bless( {
'text' => '???? 21',
'data' => 'send_reaction_1'
}, 'Telegram::KeyboardButtonCallback' )
]
}, 'Telegram::KeyboardButtonRow' )
]
}, 'Telegram::ReplyInlineMarkup' ),
'entities' => [
bless( {
'length' => 199,
'offset' => 0
}, 'Telegram::MessageEntityBold' ),
bless( {
'length' => 8,
'offset' => 919
}, 'Telegram::MessageEntityHashtag' ),
bless( {
'offset' => 928,
'length' => 9
}, 'Telegram::MessageEntityHashtag' ),
bless( {
'length' => 6,
'offset' => 938
}, 'Telegram::MessageEntityHashtag' ),
bless( {
'length' => 11,
'offset' => 945
}, 'Telegram::MessageEntityHashtag' )
],
'views' => 6964,
'flags' => 50368,
'id' => 854,
'post' => 1
}, 'Telegram::Message' ),
'pts_count' => 0
}, 'Telegram::UpdateEditChannelMessage' ),
bless( {
'message' => bless( {
'reply_markup' => bless( {
'rows' => [
bless( {
'buttons' => [
bless( {
'data' => 'send_reaction_0',
'text' => '???? 213'
}, 'Telegram::KeyboardButtonCallback' ),
bless( {
'data' => 'send_reaction_1',
'text' => '???? 8'
}, 'Telegram::KeyboardButtonCallback' )
]
}, 'Telegram::KeyboardButtonRow' )
]
}, 'Telegram::ReplyInlineMarkup' ),
'views' => 2940,
'entities' => [
bless( {
'length' => 609,
'offset' => 348
}, 'Telegram::MessageEntityItalic' )
],
'flags' => 50368,
'post' => 1,
'id' => 857,
'edit_date' => 1571907636,
'date' => 1571902479,
'to_id' => bless( {
'channel_id' => 1380524958
}, 'Telegram::PeerChannel' ),
'message' => 'Пост про 1С вызвал бурную полемику. Человек 10 (видимо, 1с-программистов) единодушно написали:
// [текст сообщения вырезан чтоб не нарушать правил Хабра о рекламе]
Я бы добавил, что блестящая у 1С дистрибуция, а маркетинг... ну, такое.'
}, 'Telegram::Message' ),
'pts_count' => 0,
'pts' => 158508
}, 'Telegram::UpdateEditChannelMessage' ),
bless( {
'pts' => 158508,
'pts_count' => 0,
'message' => bless( {
'message' => 'Здравствуйте, расскажите, пожалуйста, чем вредит экономике 1С?
// [текст сообщения вырезан чтоб не нарушать правил Хабра о рекламе]
#софт #it #экономика',
'edit_date' => 1571907650,
'date' => 1571893707,
'to_id' => bless( {
'channel_id' => 1380524958
}, 'Telegram::PeerChannel' ),
'flags' => 50368,
'post' => 1,
'id' => 856,
'reply_markup' => bless( {
'rows' => [
bless( {
'buttons' => [
bless( {
'data' => 'send_reaction_0',
'text' => '???? 360'
}, 'Telegram::KeyboardButtonCallback' ),
bless( {
'data' => 'send_reaction_1',
'text' => '???? 32'
}, 'Telegram::KeyboardButtonCallback' )
]
}, 'Telegram::KeyboardButtonRow' )
]
}, 'Telegram::ReplyInlineMarkup' ),
'views' => 4416,
'entities' => [
bless( {
'offset' => 0,
'length' => 64
}, 'Telegram::MessageEntityBold' ),
bless( {
'offset' => 1551,
'length' => 5
}, 'Telegram::MessageEntityHashtag' ),
bless( {
'length' => 3,
'offset' => 1557
}, 'Telegram::MessageEntityHashtag' ),
bless( {
'offset' => 1561,
'length' => 10
}, 'Telegram::MessageEntityHashtag' )
]
}, 'Telegram::Message' )
}, 'Telegram::UpdateEditChannelMessage' )
]
}, 'Telegram::Updates::ChannelDifference' )
}, 'MTProto::RpcResult' )
};
2019.10.24 12:00:51 $1 = {
'in' => bless( {
'update' => bless( {
'user_id' => 2507460,
'status' => bless( {
'was_online' => 1571907651
}, 'Telegram::UserStatusOffline' )
}, 'Telegram::UpdateUserStatus' ),
'date' => 1571907650
}, 'Telegram::UpdateShort' )
};
2019.10.24 12:05:46 $1 = {
'in' => bless( {
'chats' => [],
'date' => 1571907946,
'seq' => 0,
'updates' => [
bless( {
'max_id' => 141719,
'channel_id' => 1295963795
}, 'Telegram::UpdateReadChannelInbox' )
],
'users' => []
}, 'Telegram::Updates' )
};
2019.10.24 13:01:23 $1 = {
'in' => bless( {
'server_salt' => '4914425622822907323',
'unique_id' => '5297282355827493819',
'first_msg_id' => '6751307555044380692'
}, 'MTProto::NewSessionCreated' )
};
2019.10.24 13:24:21 $1 = {
'in' => bless( {
'chats' => [
bless( {
'username' => 'freebsd_ru',
'version' => 0,
'flags' => 5440,
'title' => 'freebsd_ru',
'min' => 1,
'photo' => bless( {
'photo_small' => bless( {
'local_id' => 328733,
'volume_id' => 235140688,
'dc_id' => 2,
'file_reference' => '
'secret' => '4426006807282303416'
}, 'Telegram::FileLocation' ),
'photo_big' => bless( {
'dc_id' => 2,
'file_reference' => '
'volume_id' => 235140688,
'local_id' => 328735,
'secret' => '71251192991540083'
}, 'Telegram::FileLocation' )
}, 'Telegram::ChatPhoto' ),
'date' => 1461248502,
'id' => 1038300508,
'democracy' => 1,
'megagroup' => 1
}, 'Telegram::Channel' )
],
'users' => [
bless( {
'last_name' => 'Panov',
'flags' => 1048646,
'min' => 1,
'id' => 82234609,
'status' => bless( {}, 'Telegram::UserStatusRecently' ),
'first_name' => 'Dima'
}, 'Telegram::User' )
],
'seq' => 0,
'date' => 1571912647,
'updates' => [
bless( {
'pts' => 137596,
'message' => bless( {
'flags' => 256,
'message' => 'Создать джейл с именем покороче ??',
'to_id' => bless( {
'channel_id' => 1038300508
}, 'Telegram::PeerChannel' ),
'id' => 119634,
'date' => 1571912647,
'from_id' => 82234609
}, 'Telegram::Message' ),
'pts_count' => 1
}, 'Telegram::UpdateNewChannelMessage' )
]
}, 'Telegram::Updates' )
};

Ya, khusus ora ana ing spoiler - yen sampeyan durung maca, bukak lan lakoni!

Oh, wai~~... kaya apa? Mungkin iki struktur data saka API Web khas ing JSON, kajaba kelas sing dipasang ing obyek?

Dadi ternyata ... Apa iku, comrades? .. Dadi akeh gaweyan - lan kita mandheg kanggo ngaso ngendi programer Web mung miwiti?.. Apa ora mung JSON liwat HTTPS luwih gampang?! Lan apa sing kita entuk ijol-ijolan? Iki efforts worth iku?

Ayo evaluasi apa sing diwenehake TL+MTProto lan alternatif apa sing bisa ditindakake. Inggih, HTTP request-response punika pas, nanging paling soko ing ndhuwur TLS?

serialization kompak. Ningali struktur data iki, padha karo JSON, dieling-eling yen ana varian binar. Ayo menehi tandha MsgPack minangka ora cukup extensible, nanging ana, contone, CBOR - dening cara, standar diterangake ing RFC 7049. Iku kacathet kanggo kasunyatan sing nemtokake tags, minangka mekanisme extension, lan antarane wis standar ana:

  • 25 + 256 - ngganti garis duplikat kanthi referensi nomer baris, cara kompresi sing murah
  • 26 - obyek Perl serialized karo jeneng kelas lan bantahan konstruktor
  • 27 - obyek bebas basa serial kanthi jeneng jinis lan argumen konstruktor

Inggih, Aku nyoba kanggo serialize data padha ing TL lan CBOR karo packing saka strings lan obyek aktif. Asil wiwit beda kanggo CBOR nang endi wae saka megabyte:

cborlen=1039673 tl_len=1095092

Supaya kesimpulan: Ana format substansial prasaja sing ora tundhuk Gagal sinkronisasi utawa masalah pengenal dingerteni, karo efficiency iso dibandhingke.

Panyiapan sambungan cepet. Iki tegese nul RTT sawise reconnection (nalika tombol wis digawe sapisan) - ditrapake saka pesen MTProto pisanan, nanging karo sawetara leladen - padha njaluk menyang uyah padha, sesi ora bosok, etc. Apa sing ditawakake TLS kanggo kita? kutipan sing gegandhengan:

Nalika nggunakake PFS ing TLS, tiket sesi TLS (RFC 5077) kanggo nerusake sesi sing dienkripsi tanpa negosiasi ulang tombol lan tanpa nyimpen informasi kunci ing server. Nalika mbukak sambungan pisanan lan tombol ngasilaken, server encrypts negara sambungan lan dikirim menyang klien (ing wangun tiket sesi). Mulane, nalika sambungan diterusake, klien ngirim tiket sesi sing ngemot, antara liya, tombol sesi bali menyang server. Tiket kasebut dhewe dienkripsi nganggo kunci sementara (kunci tiket sesi), sing disimpen ing server lan kudu disebarake menyang kabeh server frontend sing nangani SSL ing solusi clustered.[10]. Mangkono, introduksi tiket sesi bisa nglanggar PFS yen tombol server sauntara dikompromi, contone, nalika disimpen kanggo dangu (OpenSSL, nginx, Apache minangka standar nyimpen kanggo kabeh wektu program mlaku; situs populer nggunakake tombol kanggo sawetara jam, nganti dina).

Ing kene RTT ora nol, sampeyan kudu ngganti paling ora ClientHello lan ServerHello, sawise iku, bebarengan karo Rampung, klien wis bisa ngirim data. Nanging ing kene kudu eling yen kita ora duwe Web, kanthi akeh sambungan sing mentas dibukak, nanging utusan, sambungan sing asring siji lan luwih utawa kurang dawa, panjalukan sing relatif cendhak kanggo kaca Web - kabeh iku multiplexed nang. Yaiku, cukup ditrima, yen kita ora nemoni bagean subway sing ala banget.

Kelalen liyane? Tulis ing komentar.

Diterusake!

Ing bagean liya saka seri kiriman iki, kita bakal nimbang masalah organisasi tinimbang teknis - pendekatan, ideologi, antarmuka, sikap marang pangguna, lsp. Nanging adhedhasar informasi teknis sing diwenehake ing kene.

Bagian katelu bakal nerusake analisis komponen teknis / pengalaman pangembangan. Sampeyan bakal sinau utamane:

  • terusan saka pandemonium karo macem-macem TL-jinis
  • bab sing ora dingerteni babagan saluran lan supergroup
  • tinimbang dialog luwih elek tinimbang daptar tugas
  • babagan alamat pesen absolut vs relatif
  • apa bedane foto lan gambar
  • carane emoji ngganggu teks miring

lan crutches liyane! Tetep dirungokake!

Source: www.habr.com

Add a comment