Kritik kana protokol sareng pendekatan organisasi Telegram. Bagian 1, teknis: pangalaman nulis klien ti scratch - TL, MT

Anyar-anyar ieu, tulisan ngeunaan kumaha alusna Telegram, kumaha cemerlang sareng ngalaman baraya Durov dina ngawangun sistem jaringan, sareng sajabana parantos sering muncul dina Habré. Dina waktu nu sarua, saeutik pisan jalma geus bener immersed sorangan dina alat teknis - paling, aranjeunna ngagunakeun cukup basajan (jeung rada béda ti MTProto) Bot API dumasar kana JSON, sarta biasana ngan narima. dina iman sagala pujian jeung PR nu revolve sabudeureun utusan. Ampir satengah taun ka tukang, batur sapagawean kuring di LSM Eshelon Vasily (hanjakalna, akunna dina Habré dipupus sareng draf) mimiti nyerat klien Telegram na nyalira ti mimiti di Perl, sareng engké panulis garis ieu ngagabung. Naha Perl, sababaraha bakal geuwat nanya? Kusabab proyék-proyék sapertos kitu parantos aya dina basa sanés. perpustakaan siap-dijieun, sarta sasuai pangarang kudu indit kabeh jalan ti mimiti. Sumawona, kriptografi mangrupikeun masalah kapercayaan, tapi pariksa. Kalayan produk anu ditujukeun pikeun kaamanan, anjeun moal tiasa ngan ukur ngandelkeun perpustakaan anu siap-dijieun ti produsén sareng percanten ka ambing (tapi, ieu mangrupikeun topik pikeun bagian kadua). Di momen, perpustakaan jalan cukup ogé dina tingkat "rata" (ngamungkinkeun anjeun nyieun sagala requests API).

Nanging, moal aya seueur kriptografi atanapi matematika dina séri tulisan ieu. Tapi bakal aya loba rinci teknis sejen tur crutches arsitéktur (ogé mangpaat pikeun maranéhanana anu moal nulis ti scratch, tapi bakal ngagunakeun perpustakaan dina basa naon). Ku kituna, tujuan utama nya éta pikeun nyobaan pikeun nerapkeun klien ti scratch nurutkeun dokuméntasi resmi. Nyaéta, hayu urang nganggap yén kode sumber klien resmi ditutup (deui, dina bagian kadua urang bakal nutupan leuwih jéntré topik kanyataan yén ieu téh bener. kajadianana Janten), tapi, sapertos jaman baheula, contona, aya standar sapertos RFC - naha mungkin nyerat klien dumasar kana spésifikasi nyalira, "tanpa ningali" dina kode sumber, janten resmi (Telegram Desktop, mobile), atanapi Telethon teu resmi?

Catetan:

Dokuméntasi ... éta aya, kan? Naha leres?..

Fragmen catetan pikeun artikel ieu mimiti dikumpulkeun usum panas panungtungan. Sadaya waktos ieu dina ramatloka resmi https://core.telegram.org Dokuméntasi éta salaku Lapisan 23, i.e. macet wae di 2014 (inget, malah teu aya saluran deui?). Tangtosna, dina téori, ieu kedah ngamungkinkeun urang pikeun nerapkeun klien anu fungsional dina waktos éta dina 2014. Tapi sanajan dina kaayaan ieu, dokuméntasi éta, firstly, lengkep, sarta kadua, di tempat eta contradicted sorangan. Ngan leuwih sabulan katukang, dina Séptémber 2019, éta ngahaja Ieu kapanggih yén aya update badag tina dokuméntasi dina loka, pikeun Lapisan lengkep panganyarna 105, kalawan catetan anu kiwari sagalana kudu dibaca deui. Mémang, seueur artikel anu dirévisi, tapi seueur anu tetep teu robih. Ku alatan éta, nalika maca kritik di handap ngeunaan dokuméntasi, Anjeun kudu tetep dina pikiran nu sababaraha hal ieu geus euweuh relevan, tapi sababaraha masih cukup. Barina ogé, 5 taun di dunya modern teu ngan lila, tapi pisan seueur. Saprak jaman éta (utamana lamun teu tumut kana akun situs geochat dipiceun na revived saprak lajeng), jumlah métode API dina skéma geus tumuwuh ti saratus nepi ka leuwih ti dua ratus lima puluh!

Dimana ngamimitian salaku pangarang ngora?

Henteu masalah naha anjeun nulis ti scratch atawa ngagunakeun, contona, perpustakaan siap-dijieun kawas Telethon pikeun Python atawa Madeline pikeun PHP, dina sagala hal, anjeun bakal butuh heula ngadaptar aplikasi Anjeun - meunang parameter api_id и api_hash (Jalma anu parantos damel sareng API VKontakte langsung ngartos) dimana server bakal ngaidentipikasi aplikasina. Ieu kedahna ngalakukeun eta alesan légal, tapi urang bakal ngobrol ngeunaan naha pangarang perpustakaan teu bisa nyebarkeun eta dina bagian kadua. Anjeun bisa jadi wareg jeung nilai test, sanajan maranehna pohara kawates - kanyataan yén ayeuna anjeun bisa ngadaptar ngan hiji aplikasi, jadi ulah rurusuhan headlong kana eta.

Ayeuna, tina sudut pandang téknis, urang kedah museurkeun kanyataan yén saatos pendaptaran urang kedah nampi béwara ti Telegram ngeunaan apdet kana dokuméntasi, protokol, jsb. Nyaéta, urang tiasa nganggap yén situs kalayan darmaga ngan saukur ditinggalkeun sareng teras-terasan damel khusus sareng anu mimiti ngadamel klien, sabab leuwih gampang. Tapi henteu, teu aya anu diperhatikeun, henteu aya inpormasi anu sumping.

Sareng upami anjeun nyerat ti mimiti, teras nganggo parameter anu dicandak saleresna masih jauh. Sanajan https://core.telegram.org/ jeung ceramah ngeunaan éta dina Ngamimitian mimiti sagala, kanyataanna, Anjeun mimitina kudu nerapkeun Protokol MTProto - tapi lamun percaya perenah nurutkeun model OSI dina tungtung kaca pikeun pedaran umum ngeunaan protokol, lajeng éta sagemblengna dina hawa.

Nyatana, sateuacan sareng saatos MTProto, dina sababaraha tingkat sakaligus (sapertos jaringan asing anu damel di kernel OS nyarios, palanggaran lapisan), topik anu ageung, nyeri sareng pikasieuneun bakal ngahalangan ...

Serialisasi binér: TL (Type Language) sareng skéma na, sareng lapisan, sareng seueur kecap pikasieuneun sanés

Topik ieu, kanyataanna, mangrupikeun konci pikeun masalah Telegram. Jeung bakal aya loba kecap dahsyat lamun nyoba delve kana eta.

Janten, ieu diagramna. Lamun kecap ieu datang ka pikiran anjeun, sebutkeun, Skéma JSON, Anjeun panginten leres. Tujuanana sami: sababaraha basa pikeun ngajelaskeun sakumpulan data anu dikirimkeun. Ieu dimana kamiripan tungtung. Lamun ti kaca Protokol MTProto, atanapi tina tangkal sumber klien resmi, urang bakal nyobian muka sababaraha skéma, urang bakal ningali sapertos kieu:

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;

Jalma anu ningali ieu pertama kalina sacara intuitif bakal tiasa ngenal ngan ukur bagian tina naon anu ditulis - nya, ieu tétéla strukturna (sanaos dimana nami, di kénca atanapi ka katuhu?), Aya sawah di jerona, sanggeus éta jenis kieu sanggeus titik dua ... meureun. Di dieu dina kurung sudut meureun aya témplat sapertos dina C ++ (saleresna, henteu cekap). Sareng naon hartosna sadaya simbol anu sanés, tanda tanya, tanda seru, persentase, tanda hash (sareng écés hartosna hal anu béda-béda di tempat anu béda), sakapeung aya sareng kadang henteu, nomer héksadesimal - sareng anu paling penting, kumaha carana kéngingkeun ieu. anu leres (nu moal ditolak ku server) stream bait? Anjeun kedah maca dokuméntasi (Leres, aya tautan kana skéma dina versi JSON caket dieu - tapi éta henteu ngajantenkeun langkung jelas).

Buka kaca Serialisasi Data binér sarta teuleum kana dunya gaib suung jeung matematika diskrit, hal sarupa matan dina taun ka-4. Alfabét, jenis, nilai, combinator, combinator fungsional, formulir normal, tipe komposit, tipe polymorphic ... tur éta sakabéh ngan kaca munggaran! Salajengna ngantosan anjeun Basa TL, nu, sanajan geus ngandung conto pamundut trivial jeung respon, teu nyadiakeun jawaban pisan kana kasus nu leuwih umum, nu hartina anjeun kudu Wade ngaliwatan retelling matematika ditarjamahkeun tina Rusia kana basa Inggris dina dalapan séjén embedded. kaca!

Pamiarsa wawuh jeung basa fungsional jeung inferensi tipe otomatis bakal, tangtosna, ningali basa déskripsi dina basa ieu, sanajan tina conto, salaku leuwih akrab, sarta bisa disebutkeun yen ieu sabenerna mah goréng prinsipna. Bantahan pikeun ieu nyaéta:

  • enya, tujuanna disada alus, tapi Alas, manéhna teu kahontal
  • Atikan di paguron luhur Rusia variasina malah diantara specialties IT - teu sadaya jelema geus nyokot tangtu pakait
  • Tungtungna, sakumaha bakal urang tingali, dina prakna éta Ieu henteu diperlukeun, Kusabab ngan sawaréh kawates malah TL anu dijelaskeun dipaké

Sakumaha ceuk LeoNerd dina saluran #perl dina jaringan FreeNode IRC, anu nyobian ngalaksanakeun gerbang ti Telegram ka Matrix (tarjamahan kutipan henteu akurat tina mémori):

Asa kawas batur diwanohkeun kana téori ngetik pikeun kahiji kalina, meunang bungah, sarta mimiti nyobian maén sabudeureun kalawan eta, teu bener paduli naha éta diperlukeun dina prakna.

Tingali pikeun diri anjeun, upami peryogi jinis bulistir (int, panjang, jsb.) salaku hal dasar henteu ngangkat patarosan - tungtungna aranjeunna kedah dilaksanakeun sacara manual - contona, hayu urang nyobian turunan ti aranjeunna. vektor. Nyaéta, kanyataanna, rarangkén, lamun nelepon hal anu dihasilkeun ku ngaran ditangtoskeun maranéhanana.

Tapi saméméh

Katerangan pondok tina sawaréh tina sintaksis TL pikeun anu henteu maca dokuméntasi 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;

Harti salawasna dimimitian konstruktor, nu satutasna optionally (dina praktekna - salawasna) ngaliwatan simbol # kuduna CRC32 ti string pedaran dinormalisasi tina tipe ieu. Salajengna aya pedaran ngeunaan widang; upami aya, jinisna tiasa kosong. Ieu kabeh ditungtungan make tanda sarua, nami tipe nu constructor ieu - nyaeta, kanyataanna, subtype nu - milik. Lalaki ka katuhu tina tanda sarua nyaéta polimorfik - nyaeta, sababaraha jenis husus bisa pakait jeung eta.

Lamun harti lumangsung sanggeus garis ---functions---, teras sintaksis bakal tetep sami, tapi hartosna bakal béda: konstruktor bakal janten nami fungsi RPC, sawah bakal janten parameter (nyaéta, éta bakal tetep persis struktur anu sami, sakumaha anu dijelaskeun di handap ieu. , ieu ngan saukur jadi hartos anu ditugaskeun), sareng "tipe polimorf" - jinis hasil anu dipulangkeun. Leres, éta bakal tetep polimorfik - ngan ditetepkeun dina bagian éta ---types---, Tapi constructor ieu bakal "moal dianggap". Overloading jenis disebut fungsi ku argumen maranéhanana, i.e. Kanggo sababaraha alesan, sababaraha fungsi sareng nami anu sami tapi tanda tangan anu béda, sapertos dina C ++, henteu disayogikeun dina TL.

Naha "konstruktor" sareng "polimorfik" upami sanés OOP? Nya, kanyataanna, bakal langkung gampang pikeun jalma mikir ngeunaan ieu dina istilah OOP - jinis polimorfik salaku kelas abstrak, sareng konstruktor mangrupikeun kelas turunan langsung, sareng final dina terminologi sababaraha basa. Kanyataanna, tangtu, di dieu wungkul sasaruaan kalawan métode konstruktor overloaded nyata dina basa programming OO. Kusabab di dieu téh ngan struktur data, euweuh métode (sanajan pedaran fungsi jeung métode salajengna cukup sanggup nyieun kabingungan dina sirah yén maranéhna aya, tapi éta masalah béda) - Anjeun bisa mikir constructor salaku nilai tina anu keur diwangun ngetik nalika maca aliran bait.

Kumaha ieu kajadian? The deserializer, nu salawasna maca 4 bait, nilik nilai 0xcrc32 - sareng ngartos naon anu bakal kajadian salajengna field1 kalawan tipe int, i.e. maca persis 4 bait, dina ieu widang overlying kalawan tipe nu PolymorType maca. Nilik 0x2crc32 sarta understands nu aya dua widang salajengna, kahiji long, nu hartina urang maca 8 bait. Lajeng deui tipe kompléks, nu deserialized dina cara nu sarua. Salaku conto, Type3 bisa dinyatakeun dina sirkuit pas dua constructors, masing-masing, lajeng maranéhna kudu papanggih boh 0x12abcd34, sanggeus nu kudu maca 4 bait deui intatawa 0x6789cdef, sanggeus éta moal aya nanaon. Lain nanaon - Anjeun kudu buang iwal. Atoh, sanggeus ieu urang balik deui ka maca 4 bait int huma field_c в constructorTwo sareng éta urang réngsé maca urang PolymorType.

Tungtungna, lamun meunang bray 0xdeadcrc keur constructorThree, teras sadayana janten langkung rumit. widang munggaran urang téh bit_flags_of_what_really_present kalawan tipe # - kanyataanna, ieu ngan hiji landian pikeun jenis nat, hartina "nomer alam". Nyaéta, kanyataanna, unsigned int nyaéta, ku jalan kitu, hiji-hijina kasus nalika angka unsigned lumangsung dina sirkuit nyata. Janten, salajengna nyaéta konstruksi kalayan tanda tanya, hartosna yén médan ieu - éta bakal aya dina kawat ngan upami bit anu cocog disetél dina widang anu dimaksud (kira-kira sapertos operator ternary). Ku kituna, hayu urang nganggap yén bit ieu diatur, nu hartina salajengna urang kudu maca widang kawas Type, anu dina conto urang ngagaduhan 2 konstruktor. Hiji kosong (ngan ukur diwangun ku identifier), anu sanésna gaduh sawah ids kalawan tipe ids:Vector<long>.

Anjeun panginten tiasa nyangka yén témplat sareng generik aya dina pro atanapi Java. Tapi henteu. méh-méhan. Ieu ngan bisi ngagunakeun kurung sudut dina sirkuit nyata, sarta dipaké ngan pikeun Véktor. Dina aliran bait, ieu bakal 4 CRC32 bait pikeun tipe Véktor sorangan, salawasna sarua, lajeng 4 bait - jumlah elemen Asép Sunandar Sunarya, lajeng elemen ieu sorangan.

Tambihkeun kana kanyataan yén serialisasi sok aya dina kecap 4 bait, sadaya jinis mangrupikeun lilipetan - jinis anu diwangun ogé dijelaskeun. bytes и string kalawan serialization manual panjang tur alignment ieu ku 4 - ogé, sigana disada normal komo rélatif éféktif? Sanaos TL diklaim janten serialisasi binér anu épéktip, naraka sareng aranjeunna, kalayan ékspansi naon waé, bahkan nilai Boolean sareng senar karakter tunggal dugi ka 4 bait, naha JSON masih bakal langkung kandel? Tingali, malah widang teu perlu bisa skipped kalawan bit umbul, sagalana geus cukup alus, komo extensible pikeun mangsa nu bakal datang, jadi naha henteu nambahkeun widang pilihan anyar pikeun constructor nu engké?

Tapi euweuh, lamun maca teu pedaran ringkes kuring, tapi dokuméntasi pinuh, sarta mikir ngeunaan palaksanaan. Anu mimiti, CRC32 konstruktor diitung dumasar kana garis anu dinormalisasi tina pedaran téks skéma (ngahapus spasi tambahan, jsb.) - janten upami sawah énggal ditambah, garis pedaran jinis bakal robih, sareng ku kituna CRC32 na sareng , akibatna, serialization. Sareng naon anu bakal dilakukeun ku klien anu lami upami anjeunna nampi sawah anu dipasang umbul énggal, sareng anjeunna henteu terang naon anu kudu dilakukeun sareng aranjeunna salajengna?

Bréh, hayu urang émut CRC32, nu dipaké di dieu dasarna salaku fungsi hash pikeun sacara unik nangtukeun jinis naon anu (de) diserikeun. Di dieu urang Nyanghareupan masalah tabrakan - na euweuh, probabiliti teu salah dina 232, tapi leuwih gede. Saha anu émut yén CRC32 dirancang pikeun ngadeteksi (sareng ngabenerkeun) kasalahan dina saluran komunikasi, sareng sasuai ningkatkeun sipat ieu pikeun ngarugikeun batur? Salaku conto, éta henteu paduli ngeunaan nyusun ulang bait: upami anjeun ngitung CRC32 tina dua garis, dina kadua anjeun ngagentos 4 bait munggaran sareng 4 bait salajengna - éta bakal sami. Nalika asupan kami nyaéta string téks tina alfabét Latin (sareng tanda baca sakedik), sareng nami-nami ieu henteu sacara acak, kamungkinan susunan ulang sapertos kitu ningkat pisan.

Ku jalan kitu, anu dipariksa naon aya? bener CRC32? Salah sahiji sumber awal (malah sateuacan Waltman) ngagaduhan fungsi hash anu ngalikeun unggal karakter ku jumlah 239, anu dipikacinta ku jalma-jalma ieu, ha ha!

Tungtungna, oke, urang sadar yén konstruktor kalayan jenis widang Vector<int> и Vector<PolymorType> bakal boga CRC32 béda. Kumaha upami kinerja online? Jeung tina sudut pandang teoritis, naha ieu janten bagian tina jinis? Hayu urang nyebutkeun urang lulus hiji Asép Sunandar Sunarya sapuluh rébu angka, ogé kalawan Vector<int> sagalana jelas, panjang sarta séjén 40000 bait. Kumaha lamun ieu Vector<Type2>, nu diwangun ku ngan hiji widang int sareng éta nyalira dina jinisna - naha urang kedah ngulang 10000xabcdef0 34 kali teras 4 bait int, atawa basa nu bisa MERDEKA eta pikeun urang ti constructor fixedVec na tinimbang 80000 bait, mindahkeun deui ngan 40000?

Ieu sanés patarosan téoritis anu dianggurkeun - bayangkeun anjeun nampi daptar pangguna grup, anu masing-masing gaduh id, nami hareup, nami tukang - bédana dina jumlah data anu ditransfer ngaliwatan sambungan sélulér tiasa signifikan. Justru efektivitas serialisasi Telegram anu diémbarkeun ka kami.

Jadi…

Véktor, anu henteu kantos dileupaskeun

Lamun nyoba Wade ngaliwatan kaca pedaran combinators jeung saterusna, anjeun bakal nempo yén vektor (jeung malah matrix a) sacara resmi nyoba kaluaran ngaliwatan tuples sababaraha lembar. Tapi tungtungna aranjeunna hilap, léngkah ahir dilewati, sareng definisi vektor ngan saukur dipasihkeun, anu henteu acan dihijikeun kana jinis. Naon masalahna? Dina basa pamrograman, utamana fungsional, éta rada has pikeun ngajelaskeun struktur recursively - compiler kalawan evaluasi puguh na bakal ngartos tur ngalakukeun sagalana sorangan. Dina basa serialization data naon anu diperlukeun nyaéta EFISIENSI: cukup ngan saukur ngajelaskeun daptar, i.e. struktur dua elemen - anu kahiji nyaéta unsur data, anu kadua nyaéta struktur anu sami atanapi rohangan kosong pikeun buntut (pak). (cons) dina Lisp). Tapi ieu écés bakal merlukeun masing-masing unsur spends tambahan 4 bait (CRC32 dina kasus di TL) pikeun ngajelaskeun jenis na. Hiji Asép Sunandar Sunarya ogé bisa gampang digambarkeun ukuran tetep, Tapi dina kasus hiji Asép Sunandar Sunarya panjang kanyahoan sateuacanna, urang megatkeun kaluar.

Ku alatan éta, saprak TL henteu ngijinkeun kaluaran vektor, éta kedah ditambihan di sisi. Tungtungna dokuméntasi nyarios:

Serialization salawasna ngagunakeun constructor sarua "vektor" (const 0x1cb5c415 = crc32 ("vektor t: Tipe # [t] = Véktor t") nu teu gumantung kana nilai husus tina variabel tipe t.

Nilai tina parameter pilihan t teu aub dina serialization sabab diturunkeun tina tipe hasilna (salawasna dipikawanoh saméméh deserialization).

Tingali langkung caket: vector {t:Type} # [ t ] = Vector t - tapi dimana wae Definisi ieu sorangan henteu nyarios yén jumlah kahiji kedah sami sareng panjang vektor! Jeung teu datang ti mana. Ieu mangrupikeun hal anu kedah diémutan sareng dilaksanakeun ku panangan anjeun. Di tempat sanés, dokuméntasi malah jujur ​​​​nyebatkeun yén jinisna henteu nyata:

Pseudotipe polimorfik Véktor t nyaéta "tipe" anu niléyna mangrupa runtuyan niléy tina sagala jinis t, boh kotak atawa bulistir.

... tapi teu fokus kana eta. Nalika anjeun bosen ngalangkungan manjang matematika (panginten bahkan anjeun terang ti kursus universitas), mutuskeun nyerah sareng leres-leres ningali kumaha cara damelna dina praktékna, kesan anu tinggaleun dina sirah anjeun nyaéta yén ieu Serius. Matematika dina inti, éta jelas diciptakeun ku Cool People (dua ahli matematika - juara ACM), sareng sanés ngan ukur saha waé. Tujuan - pamer - geus kahontal.

Ku jalan kitu, ngeunaan jumlah. Hayu urang ngingetan yén # éta sinonim nat, wilangan alam:

Aya jenis éksprési (tipe-expr) jeung éksprési numerik (nat-expr). Nanging, aranjeunna ditetepkeun ku cara anu sami.

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

tapi dina grammar aranjeunna digambarkeun dina cara nu sarua, i.e. Bédana ieu kedah diémutan deui sareng dilaksanakeun sacara manual.

Nya, leres, jinis template (vector<int>, vector<User>) boga idéntifikasi umum (#1cb5c415), nyaéta. lamun nyaho yén panggero diumumkeun salaku

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

teras anjeun henteu deui ngantosan ngan ukur vektor, tapi vektor pangguna. Leuwih tepatna, kuduna antosan - dina kode nyata, unggal unsur, lamun teu tipe bulistir, bakal boga constructor a, sarta dina cara nu sae dina palaksanaan bakal diperlukeun pikeun pariksa - tapi kami dikirim persis dina unggal unsur vektor ieu. tipe éta? Kumaha lamun éta sababaraha jenis PHP, nu hiji Asép Sunandar Sunarya bisa ngandung tipena béda dina elemen béda?

Dina titik ieu anjeun mimiti mikir - naha TL sapertos kitu diperyogikeun? Meureun pikeun karanjang bakal mungkin ngagunakeun serializer manusa, protobuf sarua nu geus aya lajeng? Éta téori, hayu urang tingali prakna.

Palaksanaan TL anu aya dina kode

TL lahir di jero VKontakte bahkan sateuacan kajadian anu kasohor kalayan penjualan saham Durov sareng (pasti), malah sateuacan ngembangkeun Telegram dimimitian. Sareng dina open source kode sumber palaksanaan munggaran Anjeun tiasa manggihan loba crutches lucu. Sareng basa sorangan dilaksanakeun di dinya langkung lengkep tibatan ayeuna di Telegram. Salaku conto, hashes henteu dianggo dina skéma (hartina pseudotype anu diwangun (sapertos vektor) kalayan paripolah anu nyimpang). Atawa

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

tapi hayu urang tempo, demi completeness, pikeun ngalacak, jadi mun nyarita, évolusi tina Buta Pikiran.

#define ZHUKOV_BYTES_HACK

#ifdef ZHUKOV_BYTES_HACK

/* dirty hack for Zhukov request */

Atawa ieu geulis:

    static const char *reserved_words_polymorhic[] = {

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

      };

Fragmén ieu ngeunaan témplat sapertos:

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

Ieu mangrupikeun definisi jinis template hashmap salaku véktor pasangan int - Type. Dina C ++ eta bakal kasampak kawas kieu:

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

jadi, alpha - kecap konci! Tapi ngan dina C ++ anjeun bisa nulis T, tapi anjeun kudu nulis alfa, béta ... Tapi teu leuwih ti 8 parameter, éta tempat lamunan ends. Sigana di St. Petersburg, sababaraha dialog sapertos kieu lumangsung:

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

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

Tapi ieu ngeunaan palaksanaan munggaran diterbitkeun TL "sacara umum". Hayu urang teraskeun mertimbangkeun palaksanaan dina klien Telegram sorangan.

Kecap pikeun Vasily:

Vasily, [09.10.18 17:07] Paling sadaya, burit téh panas sabab dijieun kebat abstraksi, lajeng hammered baud on aranjeunna, sarta nutupan generator kode jeung crutches.
Hasilna, mimiti ti darmaga pilot.jpg
Lajeng tina kode dzhekichan.webp

Tangtu, ti jalma akrab jeung algoritma jeung matématika, urang bisa ngaharepkeun yén maranéhna geus maca Aho, Ullmann, sarta wawuh jeung parabot nu geus jadi standar de facto di industri leuwih dekade pikeun nulis compiler DSL maranéhanana, bener?

Ku telegram-cli nyaeta Vitaly Valtman, sakumaha bisa dipikaharti tina lumangsungna format TLO di luar na (cli) wates, hiji anggota tim - ayeuna perpustakaan pikeun TL parsing geus disadiakeun. papisah, kumaha kesan nya TL parser? ..

16.12 04:18 Vasily: Jigana aya nu teu ngawasa lex+yacc
16.12 04:18 Vasily: Abdi teu tiasa ngajelaskeun eta disebutkeun
16.12 04:18 Vasily: sumur, atanapi aranjeunna dibayar pikeun jumlah garis dina VK
16.12 04:19 Vasily: 3k+ garis jsb<censored> tinimbang parser a

Meureun iwal? Hayu urang tingali kumaha ngalakukeun Ieu mangrupikeun 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 dina Python, sababaraha éksprési biasa + kasus khusus sapertos véktor, anu, tangtosna, dinyatakeun dina skéma sakumaha anu kedahna dumasar kana sintaksis TL, tapi aranjeunna ngandelkeun sintaksis ieu pikeun ngémutan éta ... Timbul patarosan, naha éta sadayana mujijat?иÉta langkung lapis upami teu aya anu badé ngémutan éta dumasar kana dokuméntasi waé?!

Ku jalan kitu ... Inget urang ngobrol ngeunaan CRC32 mariksa? Janten, dina generator kode Telegram Desktop aya daptar pengecualian pikeun jinis-jinis anu diitung CRC32. teu cocog jeung nu dituduhkeun dina diagram!

Vasily, [18.12/22 49:XNUMX] sareng di dieu kuring bakal mikir naha TL sapertos kitu diperyogikeun
lamun kuring hayang mess kalawan palaksanaan alternatif, abdi bakal ngamimitian inserting garis putus, satengah tina parsers bakal megatkeun on multi-garis harti
tdesktop, kumaha oge, teuing

Inget titik ngeunaan hiji-liner, urang bakal balik deui ka dinya saeutik engké.

Oke, telegram-cli henteu resmi, Telegram Desktop resmi, tapi kumaha upami anu sanés? Saha anu terang?.. Dina kode klien Android henteu aya parser skéma pisan (anu ngangkat patarosan ngeunaan open source, tapi ieu kanggo bagian kadua), tapi aya sababaraha potongan kode anu lucu, tapi langkung seueur ngeunaan éta dina subsection handap.

Naon patarosan sejenna anu dibangkitkeun serialization dina prakna? Salaku conto, aranjeunna ngalakukeun seueur hal, tangtosna, kalayan widang bit sareng widang kondisional:

Vasily: flags.0? true
hartina widang nu hadir tur sarua leres lamun bandéra disetel

Vasily: flags.1? int
hartosna yén sawah aya sareng peryogi deserialized

Vasily: Ass, ulah salempang ngeunaan naon anu anjeun lakukeun!
Vasily: Aya disebatkeun dimana waé dina doc yén leres mangrupikeun jinis panjang nol, tapi mustahil pikeun ngumpul naon waé tina dokumenna.
Vasily: Dina palaksanaan open source ieu sanés masalahna, tapi aya sakumpulan crutches sareng dukungan.

Kumaha upami Telethon? Ningali payun kana topik MTProto, conto - dina dokuméntasi aya potongan sapertos kitu, tapi tandana % eta didadarkeun ngan salaku "cocog jeung hiji masihan bulistir-tipe", i.e. dina conto di handap ieu aya kasalahan atawa hal undocumented:

Vasily, [22.06.18 18:38] Dina hiji tempat:

msg_container#73f1f8dc messages:vector message = MessageContainer;

Dina béda:

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

Sareng ieu dua bédana ageung, dina kahirupan nyata sababaraha jinis vektor taranjang asalna

Kuring geus teu katempo harti vektor bulistir sarta teu datang di sakuliah

Analisis ditulis ku leungeun dina telethon

Dina diagram na definisi ieu commented kaluar msg_container

Sakali deui, patarosan tetep ngeunaan%. Teu dijelaskeun.

Vadim Goncharov, [22.06.18 19:22] sarta di tdesktop?

Vasily, [22.06.18 19:23] Tapi parser TL maranéhanana dina mesin biasa paling dipikaresep moal dahar ieu boh

// parsed manually

TL mangrupikeun abstraksi anu saé, teu aya anu ngalaksanakeunana lengkep

Jeung % teu aya dina versi maranéhanana skéma

Tapi di dieu dokuméntasi contradicts sorangan, jadi idk

Ieu kapanggih dina grammar, aranjeunna saukur bisa poho pikeun ngajelaskeun semantik

Anjeun ningal dokumén dina TL, anjeun moal tiasa terang tanpa satengah liter

"Muhun, hayu urang nyebutkeun," maca sejen bakal ngomong, "anjeun ngritik hiji hal, jadi nunjukkeun kuring kumaha eta kudu dipigawé."

Vasily ngawaler: "Ngeunaan parser, abdi resep hal kawas

    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 ) ); }
            ;

kumaha bae resep eta leuwih hade tinimbang

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;
}

atawa

        # 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)

Ieu mangrupikeun lexer SELURUH:

    ---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];

jelema. leuwih basajan nyaéta nempatkeun éta hampang."

Sacara umum, salaku hasilna, parser jeung kode generator keur sawaréh sabenerna dipaké TL pas kana kira 100 garis tina grammar jeung ~ 300 garis generator (cacah sadayana). print's kode dihasilkeun), kaasup tipe informasi buns pikeun introspeksi di unggal kelas. Unggal tipe polymorphic kabukti kana hiji kelas basa abstrak kosong, sarta konstruktor inherit ti dinya tur mibanda métode pikeun serialization na deserialization.

Kurangna jenis dina basa tipe

Ngetik anu kuat mangrupikeun hal anu saé, sanés? Henteu, ieu sanés holivar (sanaos kuring resep basa dinamis), tapi postulat dina kerangka TL. Dumasar kana éta, basa kedah nyayogikeun sagala rupa cek pikeun urang. Muhun, oke, meureun moal anjeunna sorangan, tapi palaksanaan, tapi sahenteuna kudu ngajelaskeun aranjeunna. Sareng kasempetan naon anu urang pikahoyong?

Anu mimiti, konstrain. Di dieu urang tingali dina dokuméntasi pikeun unggah file:

Eusi binér file lajeng dibagi jadi sababaraha bagian. Sadaya bagian kedah gaduh ukuran anu sami ( bagian_ukuran ) sareng sarat di handap ieu kedah dicumponan:

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

Bagian panungtungan teu kudu nyugemakeun kaayaan ieu, disadiakeun ukuranana kirang ti part_size.

Unggal bagian kedah gaduh nomer urut, file_bagian, kalawan nilai mimitian ti 0 nepi ka 2,999.

Saatos file dipisahkeun, anjeun kedah milih metodeu pikeun nyimpen dina server. Paké upload.saveBigFilePart bisi ukuran pinuh file leuwih ti 10 MB jeung upload.saveFilePart pikeun file leutik.
[...] salah sahiji kasalahan input data di handap ieu tiasa dipulangkeun:

  • FILE_PARTS_INVALID - Jumlah bagian teu valid. Nilai henteu antara 1..3000

Aya wae ieu dina diagram? Ieu kumaha bae expressible maké TL? No. Tapi hapunten, bahkan akina Turbo Pascal tiasa ngajelaskeun jinis anu dijelaskeun rentang-rentang. Sareng anjeunna terang hiji hal deui, ayeuna langkung dikenal salaku enum - tipe nu diwangun ku hiji enumeration tina jumlah tetep (leutik) nilai. Dina basa sapertos C - numerik, perhatikeun yén dugi ka ayeuna urang ngan ukur ngobrol ngeunaan jinis nomer. Tapi aya ogé arrays, string ... contona, eta bakal hade ka ngajelaskeun yen string ieu ngan bisa ngandung nomer telepon, katuhu?

Henteu aya ieu dina TL. Tapi aya, contona, dina JSON Schema. Tur upami batur bisa ngajawab ngeunaan divisibility of 512 KB, yén ieu masih perlu dipariksa di kode, teras pastikeun yén klien saukur teu tiasa ngirim nomer kaluar jangkauan 1..3000 (sareng kasalahan anu saluyu teu tiasa timbul) éta bakal mungkin, leres? ..

Ku jalan kitu, ngeunaan kasalahan jeung nilai balik. Malah anu parantos damel sareng TL kabur panonna - éta henteu langsung terang ka urang masing-masing hiji fungsi dina TL sabenerna bisa balik teu ukur tipe balik digambarkeun, tapi ogé kasalahan. Tapi ieu teu tiasa disimpulkeun ku cara naon waé nganggo TL sorangan. Tangtosna, éta parantos écés sareng henteu peryogi naon waé dina prakna (sanaos kanyataanna, RPC tiasa dilakukeun ku cara anu béda-béda, urang bakal uih deui engké) - tapi kumaha upami Kamurnian konsép Matematika Tipe Abstrak ti dunya sawarga?.. Kuring nyokot tug - jadi cocog eta.

Sarta pamustunganana, kumaha upami readability? Nya, aya, sacara umum, kuring hoyong gambaran gaduh éta leres dina skéma (dina skéma JSON, deui, éta), tapi upami anjeun parantos tapis sareng éta, teras kumaha sisi praktis - sahenteuna sakedik ningali bédana salami apdet? Tempo sorangan di 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;

atawa

-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;

Éta gumantung ka sadayana, tapi GitHub, contona, nolak nyorot parobahan dina garis panjang sapertos kitu. Kaulinan "manggihan 10 Bedana", sarta naon otak geuwat ningali éta awal jeung tungtung dina duanana conto anu sarua, Anjeun kudu tediously maca wae di tengah ... Dina pamadegan mah, ieu mah sakadar dina teori. tapi murni visual kotor jeung beca.

Ku jalan kitu, ngeunaan purity tina teori. Naha urang peryogi widang bit? Éta henteu sigana aranjeunna bau goréng tina sudut pandang téori tipe? Kateranganna tiasa ditingali dina versi diagram samemehna. Mimitina, enya, éta kumaha éta, pikeun unggal beresin jinis énggal diciptakeun. Rudiments ieu masih aya dina formulir ieu, contona:

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;

Tapi ayeuna bayangkeun, upami anjeun ngagaduhan 5 widang pilihan dina struktur anjeun, maka anjeun peryogi 32 jinis pikeun sadaya pilihan anu mungkin. ledakan combinatorial. Ku kituna, purity kristal tina téori TL sakali deui beulah ngalawan burit matak-beusi tina realitas kasar serialization.

Sajaba ti éta, di sababaraha tempat guys ieu sorangan ngalanggar tipologi sorangan. Salaku conto, dina MTProto (bab salajengna) résponna tiasa dikomprés ku Gzip, sadayana henteu kunanaon - kecuali lapisan sareng sirkuit dilanggar. Sakali deui, lain RpcResult sorangan anu dipanén, tapi eusina. Nah, naha ngalakukeun ieu?.. Kuring kungsi motong kana kruk supados komprési bakal dianggo dimana wae.

Atanapi conto anu sanés, urang sakali mendakan kasalahan - éta dikirim InputPeerUser tibatan InputUser. Atawa sabalikna. Tapi éta hasil! Hartina, server henteu paduli ngeunaan jinisna. Kumaha ieu tiasa? Jawabanna tiasa dipasihkeun ka kami ku fragmen kode tina 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);

Dina basa sejen, ieu mangrupa tempat serialization rengse MANUAL, teu dihasilkeun kode! Panginten server dilaksanakeun dina cara anu sami?.. Sacara prinsip, ieu bakal tiasa dianggo upami dilakukeun sakali, tapi kumaha éta tiasa dirojong engké nalika pembaruan? Ieu naha skéma ieu nimukeun? Sareng di dieu urang teraskeun kana patarosan salajengna.

Versioning. Lapisan

Naha versi schematic disebut lapisan ngan bisa ngaduga dumasar kana sajarah schematics diterbitkeun. Tétéla, mimitina pangarang ngira yén hal dasar bisa dipigawé maké skéma unchanged, sarta ngan lamun perlu, pikeun requests husus, nunjukkeun yén maranéhna keur dipigawé maké versi béda. Sacara prinsip, malah mangrupakeun ide nu sae - jeung nu anyar bakal, sakumaha anu kasebut, "campuran", layered on luhureun heubeul. Tapi hayu urang tingali kumaha éta dilakukeun. Leres, kuring henteu tiasa ningali éta ti mimiti - éta lucu, tapi diagram lapisan dasar ngan saukur teu aya. Lapisan dimimitian ku 2. Dokuméntasi ngabejaan urang ngeunaan fitur TL husus:

Upami klien ngadukung Lapisan 2, maka konstruktor ieu kedah dianggo:

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

Dina prakték, ieu ngandung harti yén saméméh unggal panggero API, hiji int kalawan nilai 0x289dd1f6 kudu ditambahkeun saméméh nomer métode.

Sora biasa. Tapi naon anu lumangsung saterusna? Lajeng mucunghul

invokeWithLayer3#b7475268 query:!X = X;

Janten naon salajengna? Sakumaha anjeun tiasa duga,

invokeWithLayer4#dea0d430 query:!X = X;

Lucu? Henteu, mimiti teuing pikeun seuri, pikirkeun kanyataan éta unggal Paménta ti lapisan anu sanés kedah dibungkus ku jinis khusus - upami aranjeunna sadayana béda pikeun anjeun, kumaha anjeun tiasa ngabédakeunana? Sareng nambihan ngan 4 bait di payun mangrupikeun metode anu cekap. Janten,

invokeWithLayer5#417a57ae query:!X = X;

Tapi écés yén saatos sababaraha waktos ieu bakal janten jinis bacchanalia. Sareng solusina sumping:

Update: Dimimitian ku Lapisan 9, métode helper invokeWithLayerN ngan bisa dipaké babarengan jeung initConnection

Horeeee! Saatos 9 vérsi, kami tungtungna sumping ka naon anu dilakukeun dina protokol Internét dina taun 80an - satuju kana versi sakali dina awal sambungan!

Janten naon salajengna? ..

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

Tapi ayeuna anjeun masih bisa seuri. Ngan saatos 9 lapisan anu sanés, konstruktor universal kalayan nomer vérsi tungtungna parantos nambihan, anu kedah disebat ukur sakali dina awal sambungan, sareng hartos lapisan sigana ngaleungit, ayeuna éta ngan ukur versi kondisional, sapertos. di mana-mana. Masalah direngsekeun.

Leres?..

Vasily, [16.07.18 14:01] Malah dina Jumaah kuring mikir:
Teleserver ngirim acara tanpa pamundut. Requests kudu dibungkus dina InvokeWithLayer. Server henteu mungkus apdet; teu aya struktur pikeun ngabungkus réspon sareng apdet.

Jelema. klien nu teu bisa nangtukeun lapisan nu manehna hayang apdet

Vadim Goncharov, [16.07.18 14:02] teu InvokeWithLayer a crutch prinsipna?

Vasily, [16.07.18 14:02] Ieu hiji-hijina jalan

Vadim Goncharov, [16.07.18 14:02] anu dasarna kedah hartosna satuju kana lapisan dina awal sési

Ku jalan kitu, éta nuturkeun yén downgrade klien henteu disayogikeun

Pembaruan, nyaéta. ngetik Updates dina skéma, ieu téh naon server ngirimkeun ka klien nu teu di respon kana pamundut API, tapi bebas lamun hiji acara lumangsung. Ieu topik kompléks nu bakal dibahas dina pos sejen, tapi pikeun ayeuna hal anu penting pikeun nyaho yén server ngaheéat Apdet sanajan klien offline.

Ku kituna, lamun nolak mungkus masing-masing pakét pikeun nunjukkeun versi na, ieu sacara logis ngabalukarkeun masalah anu mungkin:

  • server ngirimkeun apdet ka klien malah saméméh klien geus informed versi mana eta ngarojong
  • naon anu kudu dipigawé sanggeus ningkatkeun klien nu?
  • saha ngajaminyén pamadegan server ngeunaan jumlah lapisan moal robah salila prosés?

Naha anjeun pikir ieu téh spekulasi téoritis murni, sarta dina prakna ieu teu bisa lumangsung, sabab server ditulis leres (sahenteuna, éta diuji ogé)? Ha! Henteu masalah kumaha éta!

Ieu persis naon urang lumpat kana dina Agustus. Dina 14 Agustus, aya pesen yén aya anu diropéa dina server Telegram ... teras dina 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.

lajeng sababaraha megabytes ngambah tumpukan (sumur, dina waktos anu sareng logging ieu dibereskeun). Barina ogé, upami aya anu henteu dikenal dina TL anjeun, éta binér ku tanda tangan, langkung handap garis KABEH jalan, decoding bakal jadi teu mungkin. Naon anu anjeun kedah laksanakeun dina kaayaan sapertos kitu?

Nya, hal kahiji anu aya dina pikiran saha waé nyaéta pegatkeun sambungan sareng cobian deui. Teu mantuan. Urang google CRC32 - ieu tétéla jadi objék tina skéma 73, sanajan urang dikeureuyeuh 82. Urang kasampak taliti dina log - aya identifiers tina dua schemes béda!

Meureun masalahna murni dina klien teu resmi urang? Henteu, kami ngaluncurkeun Telegram Desktop 1.2.17 (versi anu disayogikeun dina sajumlah distribusi Linux), éta nyerat kana Log Pangecualian: MTP Jenis anu teu kaduga id #b5223b0f dibaca dina MTPMessageMedia…

Kritik kana protokol sareng pendekatan organisasi Telegram. Bagian 1, teknis: pangalaman nulis klien ti scratch - TL, MT

Google nunjukkeun yén masalah anu sami parantos kajantenan ka salah sahiji klien teu resmi, tapi teras nomer versi sareng, sasuai, asumsi éta béda ...

Janten naon anu kedah urang pigawé? Vasily na I dibeulah nepi: anjeunna diusahakeun ngamutahirkeun sirkuit ka 91, Kuring mutuskeun antosan sababaraha poé sarta coba on 73. Duanana métode digawé, Tapi saprak aranjeunna empiris, teu ngarti sabaraha versi kaluhur atanapi kahandap nu peryogi. luncat, atanapi sabaraha lila anjeun kedah ngantosan.

Engké kuring bisa baranahan kaayaan: urang ngajalankeun klien nu, mareuman eta, recompile sirkuit ka lapisan sejen, balikan deui, nyekel masalah deui, balik deui ka saméméhna - oops, euweuh jumlah switching circuit sarta klien restarts pikeun a sababaraha menit bakal nulungan. Anjeun bakal nampi campuran struktur data tina lapisan anu béda.

Katerangan? Salaku bisa nebak tina sagala rupa gejala teu langsung, server diwangun ku loba prosés tipena béda dina mesin béda. Paling dipikaresep, server anu jawab "buffering" nempatkeun kana antrian naon atasan na masihan eta, sarta aranjeunna masihan eta dina skéma anu di tempat dina waktu generasi. Sarta nepi ka antrian ieu "busuk", nanaon bisa dipigawé ngeunaan eta.

Panginten ... tapi ieu mangrupikeun tongkat anu dahsyat?!.. Henteu, sateuacan mikiran ideu gélo, hayu urang tingali kodeu klien resmi. Dina versi Android kami henteu mendakan parser TL, tapi kami mendakan file anu ageung (GitHub nolak nyabak) kalayan (de) serialisasi. Ieu snippét 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;

atawa

    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... katingal liar. Tapi, meureun, ieu dihasilkeun kode, lajeng oke?.. Tapi pasti ngarojong sagala versi! Leres, henteu écés naha sadayana dicampur, obrolan rusiah, sareng sagala rupa _old7 kumaha bae teu kasampak kawas generasi mesin ... Sanajan kitu, paling sadaya I ieu ditiup jauh ku

TL_message_layer104
TL_message_layer104_2
TL_message_layer104_3

Lalaki, anjeun moal tiasa mutuskeun naon anu aya di jero hiji lapisan?! Muhun, oke, hayu urang nyebutkeun "dua" dirilis kalawan kasalahan, nah, eta kajadian, tapi TILU?.. Langsung, rake sarua deui? Naon jenis pornografi ieu, punten?..

Dina kode sumber Telegram Desktop, ku jalan kitu, hal anu sami kajadian - upami kitu, sababaraha commits sakaligus kana skéma henteu ngarobih nomer lapisanna, tapi ngalereskeun hiji hal. Dina kaayaan dimana teu aya sumber resmi data pikeun skéma, dimana eta bisa dimeunangkeun, iwal kodeu sumber tina klien resmi? Sareng upami anjeun nyandak ti dinya, anjeun moal tiasa mastikeun yén skéma éta leres-leres dugi ka anjeun nguji sadaya metode.

Kumaha ieu malah tiasa diuji? Abdi ngarepkeun peminat unit, uji fungsional sareng anu sanés bakal dibagikeun dina koméntar.

Oké, hayu urang nempo sapotong kode sejen:

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;

Komentar ieu "dijieun sacara manual" nunjukkeun yén ngan ukur bagian tina file ieu ditulis sacara manual (tiasa anjeun ngabayangkeun sadayana ngimpina pangropéa?), Sareng sésana didamel ku mesin. Nanging, teras patarosan sanésna timbul - yén sumberna sayogi teu sagemblengna (a la GPL blobs dina kernel Linux Ubuntu), tapi ieu geus jadi topik pikeun bagian kadua.

Tapi cukup. Hayu urang ngaléngkah ka protokol di luhur nu sagala serialization ieu dijalankeun.

MT Proto

Ku kituna, hayu urang buka pedaran umum и pedaran lengkep ngeunaan protokol sarta hal kahiji urang titajong leuwih mangrupa terminologi. Tur kalawan kaayaanana sagalana. Sacara umum, ieu sigana fitur proprietary Telegram - nelepon hal béda dina tempat béda, atawa hal béda kalawan hiji kecap, atawa sabalikna (contona, dina API tingkat luhur, lamun ningali hiji pak stiker, éta teu naon anu anjeun pikirkeun).

Salaku conto, "pesen" sareng "sesi" hartosna anu béda di dieu tibatan dina antarmuka klien Telegram biasa. Nya, sadayana jelas sareng pesenna, éta tiasa diinterpretasi dina istilah OOP, atanapi ngan saukur disebut kecap "pakét" - ieu mangrupikeun tingkat angkutan anu rendah, henteu aya pesen anu sami sareng antarmuka, seueur pesen jasa. . Tapi sési ... tapi hal kahiji kahiji.

lapisan angkutan

Hal kahiji nyaéta angkutan. Aranjeunna bakal ngabejaan urang ngeunaan 5 pilihan:

  • TCP
  • Websocket
  • Websocket leuwih HTTPS
  • HTTP
  • HTTPS

Vasily, [15.06.18 15:04] Aya ogé angkutan UDP, tapi teu documented

Jeung TCP dina tilu varian

Anu kahiji sami sareng UDP dina TCP, unggal pakét kalebet nomer sekuen sareng crc
Naha maca dokumén dina karanjang nyeri pisan?

Tah, ayeuna aya TCP geus aya 4 varian:

  • Disingkat
  • tengah-tengah
  • Padded panengah
  • pinuh

Muhun, ok, Padded panengah pikeun MTProxy, ieu engké ditambahkeun alatan acara well-dipikawanoh. Tapi naha dua versi deui (tilu total) lamun anjeun bisa meunang ku hiji? Opat dasarna ngan ukur béda dina cara nyetél panjang sareng muatan tina MTProto utama, anu bakal dibahas salajengna:

  • di Abridged éta 1 atawa 4 bait, tapi teu 0xef, lajeng awak
  • di panengah ieu 4 bait panjangna sarta sawah, sarta kahiji kalina klien kudu ngirim 0xeeeeeeee pikeun nunjukkeun yén éta panengah
  • dina Full paling adiktif, ti sudut pandang tina networker a: panjangna, nomer runtuyan, sarta NOT hiji anu utamana MTProto, awak, CRC32. Leres, sadayana ieu aya di luhur TCP. Anu nyayogikeun transportasi anu tiasa dipercaya dina bentuk aliran bait berurutan; henteu aya urutan anu diperyogikeun, khususna checksum. Oke, ayeuna aya anu bakal ngabantah kuring yén TCP gaduh checksum 16-bit, janten korupsi data kajantenan. Hébat, tapi urang saleresna gaduh protokol kriptografi sareng hashes langkung panjang ti 16 bait, sadaya kasalahan ieu - komo deui - bakal katangkep ku SHA mismatch dina tingkat anu langkung luhur. Henteu aya titik dina CRC32 di luhur ieu.

Hayu urang ngabandingkeun Abridged, nu hiji bait panjangna mungkin, jeung panengah, nu justifies "Dina hal 4-bait alignment data diperlukeun," nu rada omong kosong. Naon, éta dipercaya yén programer Telegram henteu mampuh yén aranjeunna henteu tiasa maca data tina stop kontak kana panyangga anu sajajar? Anjeun masih kedah ngalakukeun ieu, sabab maca tiasa ngabalikeun anjeun sajumlah bait (sareng aya ogé server proxy, contona ...). Atawa di sisi séjén, naha meungpeuk Abridged lamun urang masih bakal boga padding hefty di luhur 16 bait - simpen 3 bait sakapeung ?

Hiji meunang gambaran yén Nikolai Durov bener diaku reinvent roda, kaasup protokol jaringan, tanpa perlu praktis nyata.

pilihan angkutan séjén, kaasup. Web sareng MTProxy, urang moal nganggap ayeuna, panginten dina tulisan anu sanés, upami aya pamundut. Ngeunaan MTProxy anu sami ieu, hayu urang émut ayeuna yén teu lami saatos dileupaskeun taun 2018, panyadia gancang diajar pikeun meungpeuk éta, anu dimaksudkeun pikeun ngahalangan bypassku ukuran pakét! Sarta ogé kanyataan yén server MTProxy ditulis (deui ku Waltman) dina C ieu overly dihijikeun ka specifics Linux Ubuntu, sanajan ieu teu diperlukeun pisan (Phil Kulin bakal mastikeun), sarta yén server sarupa boh dina Go atawa Node.js ngalakukeunana. pas dina kirang ti saratus garis.

Tapi urang bakal narik kacindekan ngeunaan literasi téknis jalma-jalma ieu dina tungtung bagian, saatos nganggap masalah anu sanés. Pikeun ayeuna, hayu urang ngaléngkah ka lapisan OSI 5, sési - dimana aranjeunna nempatkeun sési MTProto.

Konci, pesen, sesi, Diffie-Hellman

Aranjeunna nempatkeun eta aya teu sagemblengna bener ... A sési teu sési sarua nu katingali dina panganteur dina sesi Active. Tapi dina urutan.

Kritik kana protokol sareng pendekatan organisasi Telegram. Bagian 1, teknis: pangalaman nulis klien ti scratch - TL, MT

Ku kituna kami nampi string bait tina panjangna dipikawanoh tina lapisan angkutan. Ieu mangrupikeun pesen énkripsi atanapi plaintext - upami urang masih dina tahap perjanjian konci sareng leres-leres ngalakukeunana. Mana tina kebat konsep disebut "konci" urang ngobrol ngeunaan? Hayu urang netelakeun masalah ieu pikeun tim Telegram sorangan (Hapunten pikeun narjamahkeun dokuméntasi kuring sorangan tina basa Inggris kalayan otak capé tabuh 4 énjing, langkung gampang ngantunkeun sababaraha frasa sapertos kieu):

Aya dua éntitas disebut sidang - hiji dina UI klien resmi dina "sesi ayeuna", dimana unggal sési pakait sareng sadaya alat / OS.
Kadua- sési MTProto, nu boga nomer runtuyan pesen (dina rasa-tingkat low) di jerona, jeung nu bisa lepas antara sambungan TCP béda. Sababaraha sesi MTProto tiasa dipasang dina waktos anu sami, contona, pikeun nyepetkeun undeuran file.

Antara dua ieu sesi aya konsép otorisasi. Dina kasus degenerate, urang tiasa nyarios kitu sési UI sarua jeung otorisasi, Tapi Alas, sagalana pajeulit. Hayu urang tingali:

  • Pamaké dina alat anyar mimiti ngahasilkeun auth_key sareng ngawatesan kana akun, contona via SMS - éta sababna otorisasi
  • Kajadian di jero mimiti sési MTProto, anu ngagaduhan session_id jero diri.
  • Dina hambalan ieu, kombinasi otorisasi и session_id bisa disebut conto - kecap ieu nembongan dina dokuméntasi jeung kode sababaraha klien
  • Lajeng, klien bisa muka sababaraha sesi MTProto handapeun sarua auth_key - ka DC sarua.
  • Lajeng, hiji poé klien bakal perlu menta file ti DC sejen - sarta pikeun DC ieu bakal dihasilkeun anyar auth_key !
  • Pikeun nginpokeun ka sistem yén éta téh lain pamaké anyar anu ngadaptarkeun, tapi sarua otorisasi (sési UI), klien ngagunakeun panggero API auth.exportAuthorization di imah DC auth.importAuthorization dina DC anyar.
  • Sadayana sami, sababaraha tiasa kabuka sesi MTProto (masing-masing boga sorangan session_id) kana DC anyar ieu, sahandapeun na auth_key.
  • Tungtungna, klien meureun hoyong Sampurna Maju Rasiah. Unggal auth_key ieu tetep konci - per DC - sarta klien nu bisa nelepon auth.bindTempAuthKey pikeun pamakéan saheulaanan auth_key - jeung deui, ngan hiji temp_auth_key per DC, umum pikeun sakabéh sesi MTProto ka DC ieu.

bewara, éta uyah (jeung uyah kahareup) oge hiji on auth_key jelema. dibagikeun antara dulur sesi MTProto ka DC sarua.

Naon hartosna "antara sambungan TCP anu béda"? Tah ieu hartina hal kawas cookie otorisasi dina ramatloka a - eta persists (salamet) loba sambungan TCP ka server dibikeun, tapi hiji poe eta jadi goréng. Ngan teu sapertos HTTP, dina seratan MTProto dina sési sacara berurutan sareng dikonfirmasi; upami aranjeunna lebet kana torowongan, sambunganna pegat - saatos ngadamel sambungan énggal, server bakal bageur naroskeun sadayana dina sési ieu anu henteu dikirimkeun dina sateuacana. sambungan TCP.

Tapi, inpormasi di luhur diringkeskeun saatos sababaraha bulan panalungtikan. Samentawis éta, naha urang ngalaksanakeun klien kami ti mimiti? - hayu urang balik ka awal.

Ku kituna hayu urang ngahasilkeun auth_key on Vérsi Diffie-Hellman tina Telegram. Hayu urang coba ngartos dokuméntasi ...

Vasily, [19.06.18 20:05] data_with_hash: = SHA1 (data) + data + (sagala bait acak); sahingga panjangna sarua 255 bait;
encrypted_data: = RSA(data_with_hash, server_public_key); jumlah panjang 255-bait (endian badag) diangkat kana kakuatan requisite leuwih modulus requisite, sarta hasilna disimpen salaku angka 256-bait.

Aranjeunna mibanda sababaraha dope DH

Teu siga DH jalma sehat
Henteu aya dua konci umum dina dx

Nya, tungtungna ieu diurutkeun, tapi sésa-sésa tetep - buktina pagawéan dilakukeun ku klien yén anjeunna tiasa ngitung jumlahna. Jenis panyalindungan ngalawan serangan DoS. Sareng konci RSA ngan ukur dianggo sakali dina hiji arah, dasarna pikeun énkripsi new_nonce. Tapi bari operasi sigana basajan ieu bakal sukses, naon anu anjeun kudu nyanghareupan?

Vasily, [20.06.18/00/26 XNUMX:XNUMX] Abdi henteu acan dugi kana pamundut appid

Kuring ngirim pamundut ieu ka DH

Na, dina darmaga angkutan nyebutkeun yen eta bisa ngabales ku 4 bait kode kasalahan. Éta hungkul

Nya, anjeunna nyarios ka kuring -404, janten kumaha?

Janten kuring nyarios ka anjeunna: "Nyekel omong kosong anjeun énkripsi nganggo konci server kalayan sidik sapertos kieu, kuring hoyong DH," sareng éta diréspon ku 404 bodo.

Naon anu anjeun pikir ngeunaan respon server ieu? Naon anu kedah dilakukeun? Teu aya anu naroskeun (tapi langkung seueur ngeunaan éta dina bagian kadua).

Di dieu sagala kapentingan dipigawé dina darmaga

Kuring boga nanaon sejenna do a, Kuring ngan ngimpi ngarobah angka deui mudik

Dua angka 32 bit. Kuring dipak aranjeunna kawas dulur sejenna

Tapi henteu, dua ieu kedah ditambihan heula kana jalur janten BE

Vadim Goncharov, [20.06.18 15:49] sarta kusabab ieu 404?

Vasily, [20.06.18 15:49] Enya!

Vadim Goncharov, [20.06.18 15:50] janten kuring henteu ngartos naon anu anjeunna tiasa "henteu mendakan"

Vasily, [20.06.18 15:50] kira-kira

Abdi henteu tiasa mendakan dékomposisi sapertos kana faktor prima%)

Urang malah teu ngatur ngalaporkeun kasalahan

Vasily, [20.06.18 20:18] Oh, aya ogé MD5. Geus tilu hashes béda

Sidik konci diitung saperti kieu:

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

SHA1 sareng sha2

Ku kituna hayu urang nempatkeun eta auth_key kami nampi 2048 bit dina ukuran ngagunakeun Diffie-Hellman. Naon salajengna? Salajengna urang manggihan yén handap 1024 bit konci ieu teu dipaké dina sagala cara ... tapi hayu urang mikir ngeunaan ieu ayeuna. Dina hambalan ieu, urang boga rusiah dibagikeun kalawan server. Analog tina sési TLS parantos diadegkeun, anu mangrupikeun prosedur anu mahal pisan. Tapi server masih teu nyaho nanaon tentang saha kami! Henteu acan, sabenerna. otorisasi. Jelema. mun anjeun pikir dina watesan "login-sandi", sakumaha sakali anjeun lakukeun di ICQ, atawa sahanteuna "login-konci", sakumaha dina SSH (Contona, dina sababaraha gitlab / github). Kami nampi anu anonim. Kumaha lamun server ngabejaan urang "nomer telepon ieu dilayanan ku DC sejen"? Atawa malah "nomer telepon anjeun dilarang"? Anu panghadéna anu tiasa urang laksanakeun nyaéta ngajaga konci éta kalayan harepan éta bakal aya mangpaatna sareng moal janten busuk.

Ku jalan kitu, urang "nampi" eta kalawan reservations. Contona, urang percanten ka server? Kumaha upami éta palsu? cék kriptografi bakal diperlukeun:

Vasily, [21.06.18 17:53] Aranjeunna nawiskeun klien mobile pikeun pariksa nomer 2kbit pikeun primalitas%)

Tapi teu jelas pisan, nafeijoa

Vasily, [21.06.18 18:02] Dokumén éta henteu nyarios naon anu kedah dilakukeun upami tétéla henteu saderhana.

Teu cenah. Hayu urang tingali naon anu dilakukeun ku klien Android resmi dina hal ieu? A éta naon (jeung enya, sakabeh file metot) - sakumaha maranéhna ngomong, kuring ngan bakal ninggalkeun ieu di dieu:

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

Henteu, tangtosna masih aya sababaraha Aya tés pikeun primalitas hiji angka, tapi sacara pribadi kuring henteu gaduh deui pangaweruh matematika anu cukup.

Oké, urang ngagaduhan konci master. Pikeun log in, i.e. ngirim requests, Anjeun kudu nedunan enkripsi salajengna, ngagunakeun AES.

Konci pesen dihartikeun salaku 128 bit tengah SHA256 tina badan pesen (kaasup sesi, ID pesen, jsb), kaasup bait padding, prepended ku 32 bait dicokot tina konci otorisasina.

Vasily, [22.06.18 14:08] Rata-rata, bangsat, bit

Ditampi auth_key. Sadayana. Saluareun aranjeunna ... teu jelas ti dokumén. Ngarasa Luncat ka diajar kodeu open source.

Catet yén MTProto 2.0 merlukeun padding ti 12 nepi ka 1024 bait, masih tunduk kana kaayaan yén panjang pesen anu dihasilkeun bisa dibagi ku 16 bait.

Janten sabaraha padding anjeun kedah nambihan?

Sareng enya, aya ogé 404 upami aya kasalahan

Upami aya anu taliti diajar diagram sareng téks dokuméntasi, aranjeunna perhatikeun yén teu aya MAC di dinya. Sareng yén AES dianggo dina modeu IGE anu tangtu anu henteu dianggo dimana waé. Aranjeunna, tangtosna, nulis ngeunaan ieu dina FAQ maranéhanana ... Di dieu, kawas, konci pesen sorangan oge hash SHA tina data decrypted, dipaké pikeun mariksa integritas - sarta dina kasus mismatch a, dokuméntasi pikeun sababaraha alesan. nyarankeun cicingeun teu malire aranjeunna (tapi kumaha upami kaamanan, kumaha upami aranjeunna megatkeun kami?).

Kaula mah cryptographer a, meureun aya nu lepat sareng mode ieu dina hal ieu ti sudut pandang teoritis. Tapi kuring jelas tiasa namikeun masalah praktis, nganggo Telegram Desktop sabagé conto. Ieu encrypts cache lokal (sadayana D877F783D5D3EF8C ieu) dina cara nu sarua salaku pesen di MTProto (ngan dina hal ieu versi 1.0), i.e. mimitina konci pesen, teras datana nyalira (sareng dimana waé anu ageung auth_key 256 bait, tanpa nu msg_key gunana). Janten, masalahna janten katingali dina file ageung. Nyaéta, anjeun kedah nyimpen dua salinan data - énkripsi sareng dékripsi. Tur upami aya megabytes, atawa video streaming, contona? Tapi kalayan MTProto anjeun kedah mimitina encrypt atanapi ngadekrip sakabéh pesen, ngan lajeng mindahkeun kana jaringan atawa disk. Ku alatan éta, dina versi panganyarna tina Telegram Desktop dina cache di user_data Format sanésna ogé dianggo - sareng AES dina modeu CTR.

Vasily, [21.06.18 01:27] Oh, kuring terang naon IGE: IGE mangrupikeun usaha munggaran dina "mode enkripsi oténtikasi," asalna pikeun Kerberos. Ieu usaha gagal (teu nyadiakeun panyalindungan integritas), sarta kudu dihapus. Éta mangrupikeun awal usaha 20 taun pikeun modeu enkripsi oténtikasi anu tiasa dianggo, anu nembé dipuncak dina modeu sapertos OCB sareng GCM.

Sareng ayeuna argumen ti sisi karanjang:

Tim balik Telegram, dipingpin ku Nikolai Durov, diwangun ku genep juara ACM, satengahna Ph.Ds dina math. Butuh waktu kira-kira dua taun pikeun ngaluncurkeun versi MTProto ayeuna.

Éta lucu. Dua taun di tingkat handap

Atanapi anjeun tiasa nyandak tls

Oké, hayu urang nyebutkeun urang geus rengse enkripsi sarta nuansa séjén. Naha tungtungna tiasa ngirim pamundut sérial dina TL sareng deserialize réspon? Janten naon sareng kumaha anjeun kedah ngirim? Di dieu, hayu urang nyebutkeun, metoda initConnection, ieu meureun nya?

Vasily, [25.06.18 18:46] Initializes sambungan tur nyimpen informasi dina alat pamaké sarta aplikasi.

Éta nampi app_id, device_model, system_version, app_version sareng lang_code.

Sareng sababaraha patarosan

Dokuméntasi sakumaha salawasna. Ngarasa Luncat ka diajar open source

Upami sadayana kirang jelas sareng invokeWithLayer, teras naon anu salah di dieu? Tétéla, anggap urang gaduh - klien parantos ngagaduhan anu naroskeun ka server - aya pamundut anu urang hoyong kirimkeun:

Vasily, [25.06.18 19:13] Ditilik ku kode, panggero kahiji dibungkus dina crap ieu, jeung crap sorangan dibungkus dina invokewithlayer

Naha initConnection henteu tiasa janten telepon anu misah, tapi kedah janten bungkus? Leres, sakumaha tétéla, éta kedah dilakukeun unggal waktos di awal unggal sési, sareng henteu sakali, sapertos konci utama. Tapi! Éta henteu tiasa ditelepon ku pangguna anu henteu sah! Ayeuna urang parantos ngahontal tahap dimana éta tiasa dianggo Anu ieu halaman dokuméntasi - sareng éta nyarioskeun ka urang yén ...

Ngan sabagian leutik metode API anu sayogi pikeun pangguna anu henteu sah:

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

Anu pangheulana di antarana, auth.sendCode, sarta aya nu cherished pamundut munggaran nu urang ngirim api_id na api_hash, sarta sanggeus éta kami nampi SMS kalayan kode a. Sareng upami urang salah DC (nomer telepon di nagara ieu dilayanan ku anu sanés, contona), maka urang bakal nampi kasalahan sareng nomer DC anu dipikahoyong. Pikeun manggihan alamat IP mana ku nomer DC anjeun kudu nyambung ka, mantuan kami help.getConfig. Dina hiji waktu ngan aya 5 éntri, tapi sanggeus acara kawentar 2018, jumlahna geus ngaronjat sacara signifikan.

Ayeuna hayu urang émut yén urang dugi ka tahap ieu dina server sacara anonim. Naha teu mahal teuing ngan ukur kéngingkeun alamat IP? Naha henteu ngalakukeun ieu, sareng operasi anu sanés, dina bagian anu teu énkripsi tina MTProto? Kuring ngadangu bantahan: "kumaha urang tiasa mastikeun yén sanés RKN anu bakal ngabales alamat palsu?" Ka ieu kami inget yen, sacara umum, klien resmi Konci RSA dipasang, i.e. tiasa anjeun ngan tanda inpo ieu. Sabenerna, ieu parantos dilakukeun pikeun inpormasi ngeunaan ngahalangan blokir anu ditampi ku klien ngaliwatan saluran anu sanés (sacara logis, ieu henteu tiasa dilakukeun dina MTProto sorangan; anjeun ogé kedah terang dimana nyambungkeun).

OKÉ. Dina tahap otorisasi klien ieu, kami henteu acan otorisasi sareng henteu ngadaptarkeun aplikasi kami. Kami ngan ukur hoyong ningali naon anu direspon ku server kana metode anu sayogi pikeun pangguna anu henteu sah. Sareng di dieu…

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;

Dina skéma, kahiji datang kadua

Dina schema tdesktop nilai katilu nyaeta

Leres, ti saprak éta, tangtosna, dokuméntasi parantos diropéa. Sanajan bisa geura-giru jadi teu relevan deui. Kumaha kedah pamekar novice terang? Panginten upami anjeun ngadaptarkeun aplikasi anjeun, aranjeunna bakal nginpokeun ka anjeun? Vasily ngalakukeun ieu, tapi sayangna, aranjeunna henteu ngirimkeun nanaon ka anjeunna (deui, urang bakal ngobrol ngeunaan ieu dina bagian kadua).

... Anjeun noticed nu urang geus kumaha bae ngalih ka API, i.e. ka tingkat salajengna, sarta lasut hal dina topik MTProto? Teu heran:

Vasily, [28.06.18 02:04] Mm, aranjeunna nuju milarian sababaraha algoritma dina e2e

Mtproto netepkeun algoritma enkripsi sareng konci pikeun duanana domain, ogé sakedik struktur bungkus.

Tapi aranjeunna terus-terusan nyampur tingkat tumpukan anu béda-béda, janten henteu écés dimana mtproto réngsé sareng tingkat salajengna dimimitian.

Kumaha aranjeunna nyampur? Nya, ieu konci samentawis anu sami pikeun PFS, contona (ku jalan kitu, Telegram Desktop henteu tiasa ngalakukeunana). Hal ieu dieksekusi ku hiji pamundut API auth.bindTempAuthKey, i.e. ti tingkat luhur. Tapi dina waktos anu sami ngaganggu enkripsi di tingkat handap - saatosna, contona, anjeun kedah ngalakukeun deui. initConnection jsb, ieu mah adil pamundut normal. Anu khusus ogé nyaéta anjeun ngan ukur tiasa gaduh SATU konci samentawis per DC, sanaos lapangan auth_key_id dina unggal pesen ngamungkinkeun anjeun ngarobih konci sahenteuna unggal pesen, sareng yén server ngagaduhan hak "poho" konci samentawis iraha waé - dokuméntasi henteu nyarios naon anu kudu dilakukeun dina hal ieu ... Naha anjeun gaduh sababaraha konci, sapertos sakumpulan uyah ka hareup, sareng ?..

Aya sababaraha hal anu kedah diperhatoskeun ngeunaan téma MTProto.

Talatah talatah, msg_id, msg_seqno, konfirmasi, pings dina arah nu salah jeung idiosyncrasies séjén

Naha anjeun kedah terang ngeunaan aranjeunna? Kusabab aranjeunna "bocor" ka tingkat anu langkung luhur, sareng anjeun kedah waspada nalika damel sareng API. Anggap urang henteu resep kana msg_key; tingkat handap parantos ngadekrip sadayana pikeun urang. Tapi di jero data anu didekripsi kami ngagaduhan widang di handap ieu (ogé panjang data, janten urang terang dimana paddingna, tapi éta henteu penting):

  • uyah - int64
  • session_id - int64
  • message_id - int64
  • seq_no - int32

Hayu urang ngingetan yén ngan aya hiji uyah pikeun sakabéh DC. Naha terang ngeunaan anjeunna? Henteu ngan kusabab aya pamundut get_future_salts, nu ngabejaan Anjeun nu interval bakal valid, tapi ogé sabab lamun uyah anjeun "busuk", pesen (pamundut) bakal saukur leungit. Server bakal, tangtosna, ngalaporkeun uyah anyar ku ngaluarkeun new_session_created - tapi kalayan anu lami anjeun kedah ngirimkeun deui kumaha waé, contona. Sareng masalah ieu mangaruhan arsitéktur aplikasi.

Server diidinan leupaskeun sési sadayana sareng ngabales ku cara ieu kusabab seueur alesan. Sabenerna, naon sési MTProto ti sisi klien? Ieu dua angka session_id и seq_no pesen dina sési ieu. Nya, sareng sambungan TCP dasarna, tangtosna. Hayu urang nyebutkeun klien kami masih teu nyaho kumaha carana ngalakukeun loba hal, anjeunna dipegatkeun sarta nyambungkeun deui. Upami ieu kajantenan gancang - sési lami diteruskeun dina sambungan TCP énggal, ningkat seq_no salajengna. Lamun nyokot lila, server bisa ngahapus eta, sabab di sisi na eta oge antrian, sakumaha urang kapanggih kaluar.

Naon kuduna seq_no? Oh, éta patarosan tricky. Coba ngartos naon anu dimaksud:

Pesen nu patali eusi

Pesen anu meryogikeun pangakuan eksplisit. Ieu kalebet sadaya pangguna sareng seueur pesen jasa, ampir sadayana kecuali wadah sareng pangakuan.

Nomer Urutan Pesen (msg_seqno)

Jumlah 32-bit sarua jeung dua kali jumlah pesen "nu patali jeung eusi" (anu merlukeun pangakuan, sarta hususna nu teu wadahna) dijieun ku pangirim saméméh pesen ieu sarta salajengna incremented ku hiji lamun pesen ayeuna mangrupa pesen patali eusi. Hiji wadah salawasna dihasilkeun sanggeus sakabéh eusina; kituna, jumlah runtuyan na leuwih gede atawa sarua jeung nomer runtuyan pesen anu dikandung dina eta.

Naon jenis sirkus ieu kalawan increment ku 1, lajeng sejen ku 2? .. Kuring curiga yén mimitina maranéhna dimaksudkan "bit sahenteuna signifikan pikeun ACK, sésana mangrupa angka ", tapi hasilna teu cukup sarua - hususna, datang kaluar, bisa dikirim sababaraha confirmations ngabogaan sarua seq_no! Kumaha? Nya, contona, server ngirimkeun hiji hal, ngirimkeunana, sareng urang sorangan tetep jempé, ngan ukur ngabales pesen jasa anu mastikeun nampi pesenna. Dina hal ieu, konfirmasi kaluar urang bakal boga angka kaluar sarua. Mun anjeun wawuh jeung TCP jeung ngira yén ieu disada kumaha bae liar, tapi sigana teu pisan liar, sabab di TCP seq_no teu robah, tapi konfirmasi mana anu ka seq_no di sisi séjén, abdi bakal hasten ngaganggu anjeun. Konfirmasi disadiakeun dina MTProto NOT on seq_no, sakumaha dina TCP, tapi ku msg_id !

Naon ini msg_id, anu paling penting tina widang ieu? A identifier pesen unik, sakumaha ngaranna nunjukkeun. Ieu dihartikeun salaku angka 64-bit, bit panghandapna nu deui boga magic "server-not-server", sarta sésana mangrupa timestamp Unix, kaasup bagian fractional, bergeser 32 bit ka kénca. Jelema. timestamp per se (sareng seratan sareng waktos anu béda-béda teuing bakal ditolak ku server). Ti ieu tétéla yén sacara umum ieu mangrupa identifier nu global pikeun klien nu. Kumargi kitu - hayu urang émut session_id - kami dijamin: Dina kaayaan naon waé pesen anu dimaksud pikeun hiji sési tiasa dikirim ka sési anu béda. Hartina, tétéla geus aya tilu tingkat - sési, nomer sési, id pesen. Naha overcomplication misalna, misteri ieu pisan hébat.

Ku kituna, msg_id diperlukeun pikeun ...

RPC: requests, réspon, kasalahan. Konfirmasi.

Salaku mungkin geus noticed, euweuh husus "nyieun hiji pamundut RPC" tipe atawa fungsi mana dina diagram, sanajan aya waleran. Barina ogé, urang boga pesen nu patali eusi! nyaeta, naon waé pesen bisa jadi pamundut! Atawa teu jadi. Barina ogé, masing-masing nyaeta msg_id. Tapi aya waleran:

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

Ieu dimana eta dituduhkeun nu pesen ieu respon kana. Ku alatan éta, di tingkat luhur API, anjeun bakal kudu apal naon jumlah pamundut anjeun éta - Jigana teu perlu ngajelaskeun yén karya téh Asynchronous, sarta bisa aya sababaraha requests lumangsung dina waktos anu sareng, jawaban nu bisa balik dina urutan naon? Sacara prinsip, tina ieu sareng pesen kasalahan sapertos henteu aya pagawé, arsitéktur di tukangeun ieu tiasa dilacak: server anu ngajaga sambungan TCP sareng anjeun mangrupikeun pangimbang hareup-tungtung, éta ngirimkeun pamundut ka tukang sareng ngumpulkeun deui via message_id. Sigana mah sagalana di dieu jelas, logis tur alus.

Enya?.. Jeung lamun mikir ngeunaan eta? Barina ogé, réspon RPC sorangan ogé ngagaduhan lapangan msg_id! Naha urang kedah ngagorowok dina server "Anjeun henteu ngawalon jawaban kuring!"? Sareng enya, naon anu aya ngeunaan konfirmasi? Ngeunaan kaca pesen ngeunaan pesen ngabejaan urang naon

msgs_ack#62d6b459 msg_ids:Vector long = MsgsAck;

sarta eta kudu dipigawé ku unggal sisi. Tapi teu salawasna! Upami anjeun nampi RpcResult, éta nyalira janten konfirmasi. Hartina, server bisa ngabales pamundut anjeun kalawan MsgsAck - kawas, "Kuring narima éta." RpcResult tiasa ngabales langsung. Bisa jadi duanana.

Jeung enya, anjeun masih kudu ngajawab jawaban! Konfirmasi. Upami teu kitu, server bakal nganggap éta undeliverable sarta ngirimkeunana deui ka anjeun deui. Malah sanggeus reconnection. Tapi di dieu, tangtosna, masalah timeout timbul. Hayu urang tingali aranjeunna engké.

Samentawis éta, hayu urang tingali kamungkinan kasalahan palaksanaan query.

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

Oh, batur bakal ngagero, ieu mangrupikeun format anu langkung manusiawi - aya garis! Santai waé. Ieuh daptar kasalahan, tapi tangtu teu lengkep. Ti dinya urang diajar yén kode téh hal kawas Kasalahan HTTP (ogé, tangtosna, semantik tina réspon henteu dihargaan, di sababaraha tempat aranjeunna disebarkeun sacara acak diantara kodeu), sareng jalurna sapertos CAPITAL_LETTERS_AND_NUMBERS. Contona, PHONE_NUMBER_OCCUPIED atawa FILE_PART_Х_MISSING. Nya, nyaéta, anjeun masih peryogi jalur ieu parse. Salaku conto FLOOD_WAIT_3600 bakal hartosna yén anjeun kudu antosan sajam, jeung PHONE_MIGRATE_5, yén nomer telepon kalayan awalan ieu kudu didaptarkeun dina 5th DC. Urang boga basa tipe, bener? Kami henteu peryogi argumen tina senar, anu biasa bakal dilakukeun, oke.

Sakali deui, ieu sanés dina halaman pesen jasa, tapi, sakumaha anu biasa sareng proyék ieu, inpormasi tiasa dipendakan dina kaca dokuméntasi séjén. Atanapi matak curiga. Mimiti, tingali, pelanggaran ngetik / lapisan - RpcError bisa nested di RpcResult. Naha henteu di luar? Naon urang teu tumut kana akun?.. Sasuai, dimana jaminan éta RpcError moal tiasa dilebetkeun kana RpcResult, tapi jadi langsung atawa nested dina tipe séjén? .. Jeung lamun teu bisa, naha teu di tingkat luhur, i.e. éta leungit req_msg_id ? ..

Tapi hayu urang teruskeun ngeunaan pesen jasa. Klién panginten panginten yén server panginten lami sareng ngadamel pamundut anu saé ieu:

rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;

Aya tilu jawaban anu mungkin pikeun patarosan ieu, deui intersecting sareng mékanisme konfirmasi; nyobian ngartos naon anu kedahna (sareng naon daptar umum jinis anu henteu meryogikeun konfirmasi) ditinggalkeun ka pamaca salaku PR (catetan: inpormasi dina kode sumber Telegram Desktop henteu lengkep).

Kecanduan narkoba: status pesen

Sacara umum, loba tempat di TL, MTProto jeung Telegram umumna ninggalkeun rasa nekad, tapi kaluar tina sopan santun, kawijaksanaan jeung sajabana soft skill Urang sopan tetep jempé ngeunaan eta, sarta censored nu obscenities dina dialog. Sanajan kitu, tempat ieuОlolobana kaca ngeunaan pesen ngeunaan pesen Ieu ngareureuwas malah keur kuring, anu geus gawé bareng protokol jaringan pikeun lila sarta geus katempo bicycles of varying derajat crookedness.

Ieu dimimitian innocuously, kalawan confirmations. Salajengna aranjeunna ngabejaan urang ngeunaan

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;

Nya, sadayana anu ngamimitian damel sareng MTProto kedah ngurus aranjeunna; dina siklus "dilereskeun - disusun deui - diluncurkeun", kéngingkeun kasalahan nomer atanapi uyah anu parantos parah nalika éditan mangrupikeun hal anu biasa. Sanajan kitu, aya dua titik di dieu:

  1. Ieu ngandung harti yén pesen aslina leungit. Urang kudu nyieun sababaraha antrian, urang bakal kasampak di éta engké.
  2. Naon ieu nomer kasalahan aneh? 16, 17, 18, 19, 20, 32, 33, 34, 35, 48, 64 ... mana nomer séjén, Tommy?

Dokuméntasi nyatakeun:

Maksudna nyaéta nilai error_code dikelompokkeun (error_code >> 4): contona, kodeu 0x40 — 0x4f pakait sareng kasalahan dina dékomposisi wadahna.

tapi, firstly, a shift dina arah séjén, jeung Bréh, teu masalah, dimana Konci séjén? Dina sirah pangarang?.. Sanajan kitu, ieu téh trifles.

Kecanduan dimimitian dina pesen ngeunaan status pesen sareng salinan pesen:

  • Ménta Émbaran Status Pesen
    Upami salah sahiji pihak henteu acan nampi inpormasi ngeunaan status pesen kaluarna pikeun sababaraha waktos, éta tiasa sacara eksplisit nyuhunkeun ka pihak anu sanés:
    msgs_state_req#da69fb52 msg_ids:Vector long = MsgsStateReq;
  • Pesen Inpormasi ngeunaan Status Pesen
    msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
    Ieuh, info nyaéta string anu ngandung persis hiji bait status pesen pikeun tiap pesen tina daptar msg_ids asup:

    • 1 = teu aya anu terang ngeunaan pesenna (msg_id teuing rendah, pihak anu sanés tiasa hilap)
    • 2 = pesen teu katampa (msg_id ragrag dina rentang identifiers disimpen; kumaha oge, pihak séjén can tangtu teu narima pesen kawas éta)
    • 3 = pesen teu katampa (msg_id teuing tinggi; kumaha oge, pihak séjén can tangtu narima)
    • 4 = pesen anu ditampi (perhatikeun yén réspon ieu ogé dina waktos anu sami mangrupikeun pangakuan resi)
    • +8 = pesen geus diaku
    • +16 = pesen teu merlukeun pangakuan
    • +32 = Paménta RPC anu aya dina pesen anu diolah atanapi diolah parantos réngsé
    • +64 = réspon patali eusi kana pesen anu geus dihasilkeun
    • +128 = pihak séjén nyaho kanyataan yén pesen geus narima
      Tanggapan ieu henteu peryogi pangakuan. Ieu mangrupikeun pangakuan kana msgs_state_req anu relevan, dina sareng nyalira.
      Catet yén lamun tétéla ujug-ujug yén pihak séjén teu boga talatah nu sigana geus dikirim ka eta, pesen ngan saukur bisa ulang dikirim. Sanaos pihak anu sanés kedah nampi dua salinan pesen dina waktos anu sami, duplikatna bakal dipaliré. (Lamun teuing waktos kaliwat, sarta msg_id aslina geus euweuh valid, pesen kudu dibungkus dina msg_copy).
  • Komunikasi Sukarela tina Status Pesen
    Masing-masing pihak tiasa sacara sukarela nginpokeun ka pihak anu sanés ngeunaan status pesen anu dikirimkeun ku pihak anu sanés.
    msgs_all_info#8cc0d131 msg_ids:Vector long info:string = MsgsAllInfo
  • Ngalegaan Komunikasi Sukarela tina Status Hiji 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;
  • Paménta Eksplisit pikeun Ngirimkeun deui Pesen
    msg_resend_req#7d861a08 msg_ids:Vector long = MsgResendReq;
    Pihak jauh langsung ngabales ku ngirim deui pesen anu dipénta [...]
  • Paménta Eksplisit pikeun Ngirimkeun deui Jawaban
    msg_resend_ans_req#8610baeb msg_ids:Vector long = MsgResendReq;
    Pihak jauh langsung ngabales ku ngirim deui waleran kana pesen anu dipénta […]
  • Salinan Pesen
    Dina sababaraha kaayaan, pesen heubeul kalawan msg_id nu geus euweuh valid kudu dikirim deui. Lajeng, éta dibungkus dina wadah salinan:
    msg_copy#e06046b2 orig_message:Message = MessageCopy;
    Saatos nampi, pesen diolah saolah-olah bungkusna teu aya. Nanging, upami dipikanyaho yén pesen orig_message.msg_id parantos ditampi, teras pesen énggal henteu diolah (bari dina waktos anu sami, éta sareng orig_message.msg_id diaku). Nilai orig_message.msg_id kedah langkung handap tina msg_id wadahna.

Hayu urang malah cicingeun ngeunaan naon msgs_state_info deui Ceuli tina TL tacan beres nempel kaluar (urang diperlukeun vektor bait, sarta dina dua bit handap aya hiji enum, sarta dina dua bit luhur aya bandéra). Intina mah béda. Dupi saha ngartos naha sadayana ieu dina prakna? dina klien nyata perlu?.. Kalawan kasusah, tapi hiji bisa ngabayangkeun sababaraha kauntungan lamun hiji jalma kalibet dina debugging, sarta dina modeu interaktif - nanya ka server naon jeung kumaha. Tapi di dieu requests digambarkeun ngurilingan.

Kituna, unggal pihak henteu ngan ukur kedah énkripsi sareng ngirim pesen, tapi ogé nyimpen data ngeunaan dirina, ngeunaan réspon ka aranjeunna, pikeun waktos anu teu dipikanyaho. Dokuméntasi henteu ngajelaskeun boh waktos atanapi aplikasi praktis fitur ieu. no way. Anu paling endah nyaéta aranjeunna leres-leres dianggo dina kode klien resmi! Tétéla aranjeunna ngawartoskeun hal anu teu kaasup dina dokuméntasi publik. Ngartos tina kode kunaon, geus euweuh sakumaha basajan sakumaha dina kasus TL - teu bagian (relatif) logis terasing, tapi sapotong dihijikeun ka arsitektur aplikasi, i.e. bakal meryogikeun langkung seueur waktos pikeun ngartos kode aplikasi.

Pings sareng timing. Antrian.

Ti sagalana, lamun urang apal kana guesses ngeunaan arsitektur server (distribusi requests sakuliah backends), hal rada hanjelu kieu - sanajan sagala jaminan pangiriman di TCP (boh data anu dikirimkeun, atawa anjeun bakal informed ngeunaan celah, tapi data bakal dikirimkeun saméméh masalah lumangsung), yén konfirmasi di MTProto sorangan - euweuh jaminan. server bisa kalayan gampang leungit atawa buang kaluar pesen anjeun, sarta nanaon bisa dipigawé ngeunaan eta, ngan ngagunakeun tipena béda crutches.

Sarta mimiti sagala - antrian pesen. Nya, sareng hiji hal, sadayana écés ti mimiti - pesen anu henteu dikonfirmasi kedah disimpen sareng ambek. Sareng saatos jam sabaraha? Sareng si jester terang anjeunna. Panginten pesen jasa anu kecanduan kumaha waé ngabéréskeun masalah ieu sareng crutches, sebutkeun, dina Telegram Desktop aya kira-kira 4 antrian anu saluyu sareng aranjeunna (panginten langkung seueur, sakumaha anu parantos disebatkeun, pikeun ieu anjeun kedah langkung serius kana kode sareng arsitékturna; dina waktos anu sami. waktos, urang Urang terang yen eta teu bisa dicokot salaku sampel; sababaraha jenis ti skéma MTProto teu dipaké di dinya).

Naha ieu kajadian? Panginten, programer server henteu tiasa mastikeun kabébasan dina kluster, atanapi bahkan buffering dina balancer hareup, sareng ngalihkeun masalah ieu ka klien. Kusabab asa-asa, Vasily nyobian ngalaksanakeun pilihan alternatif, ngan ukur dua antrian, nganggo algoritma tina TCP - ngukur RTT ka server sareng nyaluyukeun ukuran "jandela" (dina pesen) gumantung kana jumlah pamundut anu teu dikonfirmasi. Nyaéta, heuristik kasar sapertos pikeun ngira-ngira beban server nyaéta sabaraha pamundut urang tiasa nyapek dina waktos anu sami sareng henteu leungit.

Nya, éta, anjeun ngartos, leres? Upami anjeun kedah nerapkeun TCP deui dina luhureun protokol anu ngajalankeun TCP, ieu nunjukkeun protokol anu dirancang pisan.

Heuh, naha anjeun peryogi langkung ti hiji antrian, sareng naon hartosna pikeun jalma anu damel sareng API tingkat luhur? Tingali, anjeun ngadamel pamundut, serialize eta, tapi mindeng anjeun teu bisa ngirim langsung. Naha? Kusabab jawaban bakal msg_id, nu samentaraаAbdi mangrupikeun labél, tugas anu paling saé ditunda dugi ka telat-gancang - upami server nolak éta kusabab teu cocog waktos antara urang sareng anjeunna (tangtosna, urang tiasa ngadamel tongkat anu ngageser waktos urang ti ayeuna. ka server ku nambahkeun délta diitung tina réspon server urang - klien resmi ngalakukeun ieu, tapi atah jeung taliti alatan buffering). Ku alatan éta, nalika anjeun nyieun pamundut ku panggero fungsi lokal ti perpustakaan, pesen ngaliwatan tahapan handap:

  1. Tempatna dina hiji antrian sareng ngantosan énkripsi.
  2. Ditunjuk msg_id jeung pesen indit ka antrian sejen - mungkin diteruskeun; kirimkeun ka stop kontak.
  3. a) Server ngabales MsgsAck - pesen dikirimkeun, urang ngahapus tina "antrian anu sanés".
    b) Atanapi sabalikna, anjeunna henteu resep kana hiji hal, anjeunna ngajawab badmsg - kirimkeun deui tina "antrean anu sanés"
    c) Euweuh anu dipikawanoh, pesen kudu ambek ti antrian sejen - tapi teu dipikawanoh persis nalika.
  4. Server tungtungna ngabales RpcResult - respon sabenerna (atawa kasalahan) - teu ngan dikirimkeun, tapi ogé diolah.

meureun, pamakéan wadahna sawaréh bisa ngajawab masalah. Ieu nalika sakumpulan pesen dipak kana hiji, sareng server ngaréspon kalayan konfirmasi ka sadayana sakaligus, dina hiji. msg_id. Tapi anjeunna ogé bakal nampik pak ieu, upami aya anu salah, sadayana.

Sarta dina titik ieu pertimbangan non-teknis datangna kana antrian. Tina pangalaman, urang parantos ningali seueur crutches, sareng salian ti éta, ayeuna urang bakal ningali langkung seueur conto nasihat sareng arsitéktur anu goréng - dina kaayaan sapertos kitu, naha éta kedah dipercanten sareng nyandak kaputusan sapertos kitu? Soalna rétoris (tangtu henteu).

Naon urang ngobrol ngeunaan? Upami dina topik "pesen narkoba ngeunaan pesen" anjeun masih tiasa spekulasi kalayan bantahan sapertos "Anjeun bodo, anjeun henteu ngartos rencana kami anu saé!" (jadi nulis dokuméntasi heula, sakumaha jalma normal kedah, kalawan rationale sarta conto bursa pakét, lajeng urang bakal ngobrol), lajeng timings / timeouts mangrupakeun patarosan murni praktis tur husus, sagalana di dieu geus dipikawanoh pikeun lila. Naon anu dokuméntasi nyarioskeun ka urang ngeunaan waktosna?

A server biasana acknowledged narima pesen ti klien (biasana hiji query RPC) ngagunakeun respon RPC. Lamun respon geus lila datang, hiji server mimitina bisa ngirim hiji pangakuan resi, sarta rada engké, respon RPC sorangan.

Klién biasana ngaku nampi pesen ti server (biasana, réspon RPC) ku nambihan pangakuan kana pamundut RPC salajengna upami teu telat dikirimkeun (upami dihasilkeun, sebutkeun, 60-120 detik saatos nampi resi. pesen ti server). Nanging, upami kanggo waktos anu lami teu aya alesan pikeun ngirim pesen ka server atanapi upami aya sajumlah ageung seratan anu teu dipikanyaho ti server (sebutkeun, langkung ti 16), klien ngirimkeun pangakuan nyalira.

... Kuring narjamahkeun: urang sorangan teu nyaho sabaraha jeung kumaha urang peryogi eta, jadi hayu urang nganggap yen eta jadi kawas kieu.

Sareng ngeunaan ping:

Pesen Ping (PING/PONG)

ping#7abe77ec ping_id:long = Pong;

Tanggapan biasana dipulangkeun ka sambungan anu sami:

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

Pesen ieu henteu meryogikeun pangakuan. A pong dikirimkeun ngan dina respon kana ping a bari ping a bisa ngagagas ku dua sisi.

Panutup Sambungan nunggak + PING

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

Gawéna sapertos ping. Sajaba ti éta, sanggeus ieu narima, server dimimitian timer nu bakal nutup sambungan ayeuna disconnect_delay detik engké iwal mun narima pesen anyar tina tipe sarua nu otomatis ngareset sadayana timers saméméhna. Upami klien ngirimkeun ping ieu sakali unggal 60 detik, contona, éta tiasa nyetél disconnect_delay sami sareng 75 detik.

Ari anjeun gélo?! Dina 60 detik, karéta bakal asup ka stasion, turun sareng ngajemput panumpang, sareng deui kaleungitan kontak dina torowongan. Dina 120 detik, nalika anjeun ngupingkeun éta, éta bakal sumping ka anu sanés, sareng sambunganna sigana bakal pegat. Nya, écés ti mana asalna suku - "Kuring nguping ngirining, tapi henteu terang dimana éta", aya algoritma Nagl sareng pilihan TCP_NODELAY, dimaksudkeun pikeun karya interaktif. Tapi, hapunten abdi, tahan kana nilai standar - 200 Millidetik Upami anjeun leres-leres hoyong ngagambarkeun anu sami sareng ngahémat sababaraha pakét anu mungkin, teras pareumkeun salami 5 detik, atanapi naon waé pesen "Pamaké ngetik ..." béakna ayeuna. Tapi euweuh deui.

Sarta pamustunganana, pings. Nyaéta, mariksa liveness tina sambungan TCP. Éta lucu, tapi sakitar 10 taun ka pengker kuring nyerat téks kritis ngeunaan utusan asrama fakultas urang - pangarang di dinya ogé ping server ti klien, sareng sanés sabalikna. Tapi murid taun 3 mangrupikeun hiji hal, sareng kantor internasional sanés sanés, sanés?..

Kahiji, program atikan saeutik. Sambungan TCP, dina henteuna bursa pakét, tiasa hirup sababaraha minggu. Ieu duanana alus jeung goréng, gumantung kana tujuan. Éta saé upami anjeun ngagaduhan sambungan SSH ka server, anjeun bangun tina komputer, reboot router, uih deui ka tempat anjeun - sési ngalangkungan server ieu henteu robek (anjeun henteu ngetik nanaon, henteu aya pakét) , éta merenah. Éta goréng upami aya rébuan klien dina server, masing-masing nyéépkeun sumber daya (halo, Postgres!), Sareng host klien parantos lami parantos reboot - tapi urang moal terang ngeunaan éta.

Sistem obrolan / IM digolongkeun kana kasus kadua pikeun hiji alesan tambahan - status online. Lamun pamaké "ragrag", Anjeun kudu ngawartosan interlocutors-Na ngeunaan ieu. Upami teu kitu, anjeun bakal mungkas nepi ka kasalahan nu panyipta Jabber dijieun (jeung dilereskeun salila 20 taun) - pamaké geus dipegatkeun sambungan, tapi terus nulis pesen ka anjeunna, percanten yén anjeunna online (anu ogé leungit lengkep dina ieu. sababaraha menit sateuacan pegatna kapanggih). Henteu, pilihan TCP_KEEPALIVE, anu seueur jalma anu henteu ngartos kumaha timer TCP dianggo sacara acak (ku netepkeun nilai liar sapertos puluhan detik), moal ngabantosan di dieu - anjeun kedah mastikeun yén henteu ngan ukur kernel OS. tina mesin pamaké hirup, tapi ogé fungsi normal, dina bisa ngabales, sarta aplikasi sorangan (saur anjeun teu bisa freeze? Telegram Desktop on Ubuntu 18.04 froze pikeun kuring leuwih ti sakali).

Éta sababna anjeun kedah ping sérver klien, sarta teu sabalikna - lamun klien nu ngalakukeun ieu, lamun sambungan nu pegat, ping moal dikirimkeun, tujuan moal kahontal.

Naon anu urang tingali dina Telegram? Ieu persis sabalikna! Muhun, éta. Sacara resmi, tangtosna, dua sisi tiasa silih ping. Dina prakték, klien ngagunakeun kruk a ping_delay_disconnect, nu nyetel timer dina server. Muhun, hapunten abdi, teu nepi ka klien mutuskeun sabaraha lila manehna hayang cicing di dinya tanpa ping. Server, dumasar kana bebanna, langkung terang. Tapi, tangtosna, upami anjeun henteu kapikiran sumberdaya, maka anjeun bakal janten Pinokio jahat anjeun sorangan, sareng kruk bakal ngalakukeun ...

Kumaha sakuduna geus dirancang?

Kuring yakin yén fakta di luhur jelas nunjukkeun yén tim Telegram / VKontakte teu pisan kompeten dina widang angkutan (jeung handap) tingkat jaringan komputer jeung kualifikasi low maranéhanana dina urusan relevan.

Naha éta janten pajeulit, sareng kumaha arsitek Telegram tiasa nyobian ngabantah? Kanyataan yén aranjeunna nyobian ngadamel sési anu salamet putus sambungan TCP, nyaéta naon anu henteu dikirimkeun ayeuna, kami bakal nganteurkeun engké. Éta sigana ogé nyobian ngadamel angkutan UDP, tapi aranjeunna ngalaman kasusah sareng ngantunkeunana (éta naha dokuméntasina kosong - teu aya anu kedah dibanggakeun). Tapi kusabab kurangna pamahaman kumaha jaringan sacara umum sareng TCP khususna dianggo, dimana anjeun tiasa ngandelkeunana, sareng dimana anjeun kedah ngalakukeunana nyalira (sareng kumaha), sareng usaha pikeun ngagabungkeun ieu sareng kriptografi "dua manuk sareng hiji batu”, ieu hasilna.

Kumaha éta diperlukeun? Dumasar kana kanyataan yén msg_id mangrupakeun timestamp diperlukeun ti sudut pandang cryptographic pikeun nyegah serangan replay, éta kasalahan ngagantelkeun hiji fungsi identifier unik kana eta. Ku alatan éta, tanpa dasarna ngarobah arsitéktur ayeuna (nalika aliran Pembaruan dihasilkeun, éta topik API tingkat luhur pikeun bagian sejen tina runtuyan tulisan ieu), hiji bakal perlu:

  1. The server nyekel sambungan TCP ka klien nu nyokot tanggung jawab - lamun geus maca tina stop kontak nu, mangga ngaku, prosés atawa balik kasalahan, euweuh rugi. Lajeng konfirmasi teh sanes vektor of id, tapi ngan saukur "nu panungtungan narima seq_no" - ngan hiji angka, sakumaha dina TCP (dua angka - seq anjeun sarta dikonfirmasi). Urang sok aya dina sési, sanés?
  2. The timestamp pikeun nyegah serangan replay janten widang misah, a la nonce. Ieu dipariksa, tapi teu mangaruhan nanaon sejenna. Cukup jeung uint32 - lamun uyah urang robah sahenteuna unggal satengah poé, urang tiasa allocate 16 bit kana bit-urutan low tina bagian integer tina waktu ayeuna, sésana - ka bagian fractional detik (sakumaha ayeuna).
  3. Dipiceun msg_id pisan - ti sudut pandang ngabedakeun requests on backends, aya, firstly, klien id, sarta Bréh, sesi id, concatenate aranjeunna. Sasuai, ngan hiji hal anu cukup salaku identifier pamundut seq_no.

Ieu ogé sanés pilihan anu paling suksés; acak lengkep tiasa janten identifier - ieu parantos dilakukeun dina API tingkat luhur nalika ngirim pesen, ku jalan kitu. Eta bakal leuwih hadé pikeun sakabéhna nyieun ulang arsitéktur ti relatif ka mutlak, tapi ieu topik pikeun bagian sejen, teu pos ieu.

API?

Ta-daam! Ku kituna, sanggeus bajoang ngaliwatan jalur pinuh ku nyeri jeung crutches, kami tungtungna bisa ngirim requests wae ka server tur nampa jawaban naon mun aranjeunna, kitu ogé nampa apdet ti server (sanes dina respon kana pamundut, tapi éta sorangan). ngirim kami, sapertos PUSH, upami aya anu langkung jelas ku cara éta).

Perhatosan, ayeuna bakal aya hiji-hijina conto dina Perl dina tulisan! (pikeun anu teu wawuh jeung sintaksis, argumen kahiji berkah nyaéta struktur data objék, anu kadua nyaéta kelasna):

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' )
};

Leres, sanés spoiler ngahaja - upami anjeun henteu acan maca, teraskeun teras lakukeun!

Oh, wai~~... kumaha ieu? Hal anu dalit pisan ... meureun ieu struktur data tina API Web has di JSON, iwal yén kelas ogé napel objék? ..

Ku kituna ieu kumaha tétéla ... Naon eta sadayana ngeunaan, comrades?.. Jadi loba usaha - sarta kami dieureunkeun pikeun beristirahat dimana programer Web kakara ngamimitian?.. Moal ngan JSON leuwih HTTPS jadi basajan ?! Naon anu urang meunang di tukeran? Naha usaha éta patut?

Hayu urang evaluasi naon anu TL+MTProto masihan ka kami sareng alternatif naon anu mungkin. Muhun, HTTP, nu museurkeun kana model pamundut-response, nyaeta fit goréng, tapi sahenteuna hal di luhur TLS?

Serialisasi kompak. Ningali struktur data ieu, sami sareng JSON, kuring émut yén aya versi binér éta. Hayu urang cirian MsgPack salaku insufficiently extensible, tapi aya, contona, CBOR - ku jalan kitu, standar dijelaskeun dina RFC 7049. Éta kasohor kanyataan yén éta ngahartikeun tag, salaku mékanisme ékspansi, jeung diantara geus standar aya:

  • 25 + 256 - ngagentos garis anu diulang ku rujukan ka nomer garis, sapertos metode komprési anu murah
  • 26 - objék Perl serialized kalawan ngaran kelas na constructor argumen
  • 27 - obyék bebas basa serialized kalawan ngaran tipe na constructor argumen

Nya, kuring nyobian sérial data anu sami dina TL sareng CBOR kalayan senar sareng packing obyék diaktipkeun. Hasilna mimiti rupa-rupa dina ni'mat CBOR wae ti megabyte a:

cborlen=1039673 tl_len=1095092

Ku kituna, kacindekan: Aya format substansi basajan nu teu tunduk kana masalah gagalna sinkronisasi atawa identifier kanyahoan, kalawan efisiensi comparable.

Ngadegkeun sambungan gancang. Ieu ngandung harti enol RTT sanggeus reconnection (lamun konci geus dihasilkeun sakali) - lumaku ti pesen MTProto pisan munggaran, tapi kalawan sababaraha reservations - pencét uyah sarua, sési teu ruksak, jsb. Naon anu ditawarkeun TLS ka kami? Quote on topik:

Nalika nganggo PFS dina TLS, tiket sési TLS (RFC 5077) pikeun neruskeun sési énkripsi tanpa negotiating ulang konci na tanpa nyimpen informasi konci dina server. Nalika muka sambungan anu munggaran sareng nyiptakeun konci, server énkripsi kaayaan sambungan sareng ngirimkeunana ka klien (dina bentuk tikét sési). Sasuai, nalika sambungan diteruskeun, klien ngirimkeun tikét sési, kaasup konci sési, balik deui ka server. Tikét sorangan énkripsi ku konci samentara (konci tikét sési), nu disimpen dina server jeung kudu disebarkeun diantara sakabeh server frontend ngolah SSL dina solusi clustered.[10]. Ku kituna, bubuka tikét sési bisa ngalanggar PFS lamun konci server samentara anu compromised, contona, nalika aranjeunna disimpen keur lila (OpenSSL, nginx, Apache nyimpen aranjeunna sacara standar pikeun sakabéh durasi program; situs populér ngagunakeun. konci pikeun sababaraha jam, nepi ka poé).

Di dieu RTT henteu nol, anjeun kedah tukeur sahenteuna ClientHello sareng ServerHello, saatos éta klien tiasa ngirim data sareng Rengse. Tapi di dieu urang kudu inget yen urang teu boga Web, kalawan kebat miboga sambungan anyar dibuka, tapi hiji utusan, sambungan nu mindeng hiji atawa leuwih atawa kirang lila-cicing, requests rélatif pondok ka kaca Wéb - sagalana geus multiplexed. internal. Nyaéta, éta cukup ditarima lamun urang teu datang di sakuliah bagian subway bener goréng.

Hilap hal sejenna? Tulis dina komentar.

Ngalajengkeun!

Dina bagian kadua séri tulisan ieu, urang bakal nganggap sanés téknis, tapi masalah organisasi - pendekatan, ideologi, antarmuka, sikep ka pangguna, jsb. Dumasar, kumaha oge, kana inpormasi téknis anu disayogikeun di dieu.

Bagian katilu bakal terus nganalisis komponén téknis / pangalaman pamekaran. Anjeun bakal diajar, khususna:

  • tuluyan tina pandemonium kalawan rupa-rupa jenis TL
  • hal kanyahoan ngeunaan saluran jeung supergroups
  • naha dialogs leuwih goreng ti roster
  • ngeunaan alamat pesen mutlak vs relatif
  • naon bedana poto jeung gambar
  • kumaha emoji ngaganggu téks miring

jeung crutches séjén! Tetep di dieu!

sumber: www.habr.com

Tambahkeun komentar