Rexne li protokol û nêzîkatiyên rêxistinî yên Telegramê. Beş 1, teknîkî: ezmûna nivîsandina xerîdarek ji sifirê - TL, MT

Di van demên dawî de, postên li ser Telegram çiqasî baş e, birayên Durov çiqas jêhatî û bi tecrube ne di avakirina pergalên torê de, û hwd., li ser Habré pir caran dest pê kirin. Di heman demê de, pir hindik kes bi rastî xwe di cîhaza teknîkî de rijandine - herî zêde, ew API-ya Bot-ê ya pir sade (û ji MTProto pir cûda) li ser bingeha JSON-ê bikar tînin, û bi gelemperî tenê qebûl dikin. li ser baweriyê hemû pesn û PR ku li dora qasidê dizivire. Nêzîkî sal û nîv berê, hevkarê min li Eshelon NGO Vasily (mixabin, hesabê wî li ser Habré ligel pêşnûmeyê hate jêbirin) dest bi nivîsandina xerîdar a xweya Telegramê ji nû ve li Perl kir, û paşê nivîskarê van rêzan tev lê bû. Çima Perl, hinek dê yekser bipirsin? Ji ber ku projeyên weha di zimanên din de jî hene, bi rastî jî mesele ne ev e, dibe ku zimanek din hebe ku lê tune be. pirtûkxaneya amade, û li gorî vê yekê divê nivîskar heta dawî biçe ji serî de. Wekî din, krîptografî mijarek pêbaweriyê ye, lê verast bikin. Digel hilberek ku ji ewlehiyê re armanc dike, hûn nekarin bi tenê xwe bispêrin pirtûkxaneyek amadekirî ya ji çêkerê û bi koranî pê bawer bikin (lêbelê, ev mijarek ji bo beşa duyemîn e). Heya nuha, pirtûkxane di asta "navîn" de pir baş dixebite (dihêle ku hûn daxwazên API-ê bikin).

Lêbelê, dê di vê rêze posteyan de pir krîptografî an matematîkî tune be. Lê dê gelek hûrguliyên teknîkî û kelûpelên mîmarî yên din jî hebin (ji bo kesên ku dê ji sifirê nenivîsin, lê dê pirtûkxaneyê bi her zimanî bikar bînin jî kêrhatî ye). Ji ber vê yekê, armanca sereke ew bû ku hewl bidin ku xerîdar ji sifrê bicîh bikin li gorî belgeyên fermî. То есть, предположим, что исходный код официальных клиентов закрыт (опять же во второй части подробнее раскроем тему того, что это и правда dibe wusa), lê, wekî rojên berê, mînakî, standardek mîna RFC heye - gelo meriv dikare li gorî diyardeyê tenê, "bêyî ku li koda çavkaniyê binêre" binivîsîne, ew fermî be (Telegram Desktop, mobîl), an Telethona nefermî?

Tabloya naverokê:

Belgekirin... ew heye, rast? Rast e?..

Parçeyên notên ji bo vê gotarê havîna borî dest bi berhevkirinê kirin. Hemî vê demê li ser malpera fermî https://core.telegram.org Belge wekî Layer 23 bû, yanî. di sala 2014-an de li cîhekî asê mabûn (ji bîr mekin, wê demê kanal jî tunebûn?). Bê guman, di teoriyê de, ev yek diviyabû ku em di wê demê de di sala 2014-an de xerîdarek bi fonksiyonê bicîh bikin. Lê di vê rewşê de jî, belgekirin, pêşî ne temam bû, ya duyemîn jî, li cîhên xwe berovajî kir. Tenê mehek berê, di îlona 2019 de, ew bû bi şens обнаружено, что на сайте большое обновление документации, на вполне свежий Layer 105, с пометкой, что теперь всю надо читать заново. Действительно, многие статьи были переработаны, но многие — так и остались без изменений. Поэтому, читая критику ниже по поводу документации, следует иметь в виду, что некоторые из этих вещей уже неактуальны, но некоторые — всё еще вполне. В конце концов, 5 лет в современном мире — это не просто много, а pir zêde. Ji wan deman ve (nemaze heke hûn ji wê hingê ve malperên jeochatê yên hatine avêtin û vejîne nehesibînin), hejmara rêbazên API-ê yên di pileyê de ji sed ji dused û pêncî zêdetir bûye!

Wekî nivîskarek ciwan ji ku dest pê bike?

Ne girîng e ku hûn ji sifirê binivîsin an bikar bînin, mînakî, pirtûkxaneyên hazir ên mîna Telethon для Python an Madeline ji bo PHP, в любом случае Вам потребуется сначала serlêdana xwe qeyd bikin - получить параметры api_id и api_hash (yên ku bi VKontakte API-ê re xebitîne tavilê fêm dikin) ku server dê serîlêdanê nas bike. Ev hebe ji ber sedemên qanûnî bikin, lê em ê bêtir biaxivin ka çima nivîskarên pirtûkxaneyê nikarin wê di beşa duyemîn de biweşînin. Hûn dikarin ji nirxên testê razî bibin, her çend ew pir kêm in - rastî ev e ku naha hûn dikarin qeyd bikin только одно app, ji ber vê yekê bi serê xwe di nav wê de lez nekin.

Naha, ji hêla teknîkî ve, divê em bi vê yekê re eleqedar bibin ku piştî qeydkirinê divê em ji Telegram-ê di derheqê nûvekirina belge, protokol û hwd de agahdarî bistînin. Ango, meriv dikare texmîn bike ku malpera bi dokan bi tenê hate terikandin û bi taybetî bi kesên ku dest bi çêkirina xerîdaran kirin re xebata xwe domand, ji ber ku hêsantir e. Lê na, tiştek wisa nehat dîtin, agahî nehat.

Û heke hûn ji sifirê binivîsin, wê hingê karanîna pîvanên wergirtî bi rastî hîn jî rêyek dûr e. Herçi https://core.telegram.org/ û berî her tiştî di Destpêkê de li ser wan diaxive, bi rastî, hûn ê pêşî hewce bikin ku bicîh bikin Protokola MTProto - lê heke we bawer kir раскладке по модели OSI li dawiya rûpelê ji bo danasîna giştî ya protokolê, wê hingê ew bi tevahî vala ye.

На самом деле, и до MTProto, и после, на нескольких уровнях сразу (как говорят зарубежные работающие в ядре ОС сетевики, layer violation) на пути встанет большая, больная и ужасная тема…

Rêzkirina binary: TL (Zimanê Tîp) û nexşeya wê, û qat, û gelek peyvên din ên tirsnak

Ev mijar, bi rastî, mifteya pirsgirêkên Telegram e. Û heke hûn hewl bidin ku tê de kûr bibin dê gelek gotinên tirsnak hebin.

Ji ber vê yekê, li vir diagram e. Ger ev peyv tê bîra we, bêje: JSON Schema, Te rast fikirî. Armanc yek e: hin ziman ku komek daneya veguhestî ya gengaz diyar bike. Li vir wekhevî bi dawî dibe. Ger ji rûpelê Protokola MTProto, или из дерева исходных текстов официального клиента, мы попытаемся открыть какую-нибудь схему, то увидим нечто вроде:

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;

Человек, видящий это впервые, интуитивно сможет распознать только часть написанного — ну, это видимо структуры (хотя где имя, слева или справа?), вот есть поля в них, после которых через двоеточие идёт тип… наверное. Вот в угловых скобках наверное шаблоны как в Си++ (на самом деле, ne rast). Wateya hemî sembolên din çi ye, nîşaneyên pirsê, nîşaneyên derbirînê, sedî, nîşaneyên heş (û eşkere ye ku ew li cihên cihê wateyê didin tiştên cûda), carinan hene û carinan na, hejmarên hexadecimal - û ya herî girîng, meriv çawa ji vê yekê tê ya rast (ku dê ji hêla serverê ve neyê red kirin) byte stream? Divê hûn belgeyê bixwînin (erê, di guhertoya JSON-ê ya nêzîk de girêdanên şemayê hene - lê ew wê zelaltir nake).

Открываем страницу Binary Data Serialization û bikevin cîhana efsûnî ya kivarkan û matematîkên veqetandî, tiştek mîna matan di sala 4-an de. Alfabe, cure, nirx, hevberker, kombînatorê fonksîyonel, forma normal, tîpa hevedudanî, tîpa polîmorf... û ev hemû tenê rûpela yekem e! Piştre li benda te ye Ziman TL, ya ku, her çend ew jixwe mînakek daxwaz û bersivek piçûk dihewîne jî, ji rewşên tîpîktir re qet bersiv nade, ev tê vê wateyê ku hûn neçar in ku ji nûvekirina matematîkên ku ji rûsî bo îngilîzî hatine wergerandin li ser heşt pêvekên din ên binavkirî derbas bikin. pages!

Xwendevanên ku bi zimanên fonksiyonel û înfazkirina tîpa otomatîkî nas dikin, bê guman, dê zimanê danasînê bi vî zimanî, hetta ji nimûneyê, pir nastir bibînin, û dikarin bibêjin ku ev bi rastî di prensîbê de ne xirab e. Li dijî vê yekê nerazîbûn ev in:

  • erê цель dengek baş e, lê mixabin, ew negihîştiye
  • образование в ВУЗах России варьирует даже среди IT-шных специальностей — соответствующий курс читали не всем
  • Di dawiyê de, wekî ku em ê bibînin, di pratîkê de ew e ne hewce ye, ji ber ku ji TL-ya ku hatî diyar kirin tenê binkeyek sînorkirî tê bikar anîn

Wekî ku hate gotin LeoNerd li ser kanalê #perl di tora FreeNode IRC de, yê ku hewl da ku dergehek ji Telegram ber bi Matrixê ve bicîh bike (wergera gotinê ji bîrê nerast e):

Wusa dixuye ku kesek ji bo cara yekem teoriyê binivîsîne, heyecan bû, û dest pê kir ku bi wê re bilîze, bi rastî ne xema ku ew di pratîkê de hewce ye an na.

Смотрите сами, если необходимость bare-типов (int, long и т.д.) как чего-то элементарного вопросов не вызывают — в конечном счете их надо реализовать вручную — для примера возьмем попытку вывести из них vektor. То есть, на самом деле, rêzî, если называть получившиеся вещи своими именами.

Lê berê

Ji bo kesên ku belgeyên fermî nexwendin, ravekek kurt a binkomek hevoksaziya TL

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;

Pênasîn her dem dest pê dike çêker, piştî ku vebijarkî (di pratîkê de - her dem) bi riya sembolê # divêt CRC32 ji rêzika ravekirina normalkirî ya vî rengî. Dûv re ravekirina qadan tê; heke ew hebin, dibe ku celeb vala be. Ev hemî bi nîşanek wekhev diqede, navê celebê ku ev çêker - yanî bi rastî, binecure - tê. Zilamê li rastê nîşana wekheviyê ye polymorphic - ango çend celebên taybetî dikarin pê re têkildar bin.

Если же определение встретилось после строки ---functions---, wê hingê hevoks dê heman bimîne, lê wateya wê cûda be: çêker dê bibe navê fonksiyona RPC, zevî dê bibin parametre (baş e, ango, ew ê tam bi heman strukturê hatî dayîn bimîne, wekî ku li jêr hatî destnîşan kirin , ev ê tenê wateya destnîşankirî be), û "cureya polîmorfîk" - celebê encama vegerî ye. Rast e, ew ê hîn jî polîmorfîk bimîne - tenê di beşê de hatî destnîşan kirin ---types---, lê ev çêker dê "nehê hesibandin". Zêdebarkirina cureyên fonksiyonên binavkirî ji hêla argumanên wan ve, yanî. Ji ber hin sedeman, gelek fonksiyonên bi heman navî lê bi îmzeyên cuda, wek di C++ de, di TL de nayên peyda kirin.

Çima "çêker" û "polymorphic" heke ew ne OOP be? Welê, bi rastî, dê hêsantir be ku kesek li ser vê yekê bi şertên OOP bifikire - celebek polîmorfîk wekî çînek abstrakt, û çêker çînên dûvdana rasterast in, û final в терминологии ряда языков. На самом деле, конечно, здесь лишь похожесть bi rêbazên çêker ên rastîn ên barkirî yên di zimanên bernamesaziyê yên OO de. Ji ber ku li vir tenê strukturên daneyê ne, tu rêbaz tune ne (her çend danasîna fonksiyon û rêbazan bêtir dikare di serî de tevliheviyê çêbike ku ew hene, lê ew mijarek cûda ye) - hûn dikarin çêkerek wekî nirxek ji bifikirin. kîjan tê avakirin dema xwendina stream byte binivîse.

Ev çawa dibe? Deserializer, ku her gav 4 bytes dixwîne, nirxê dibîne 0xcrc32 — и понимает, что дальше будет field1 bi tîp int, yanî tam 4 byte dixwîne, li ser vê qada sergirtî bi tîpê PolymorType прочитано. Видит 0x2crc32 û fêm dike ku du qadên din hene, yekem long, ku tê vê wateyê ku em 8 byte dixwînin. Û dûv re dîsa celebek tevlihev, ku bi heman rengî tê deserialîzekirin. Bo nimûne, Type3 dikare bi rêzê du avaker di çerçoveyê de were ragihandin, wê hingê divê ew yek jî bicivin 0x12abcd34, piştî ku hûn hewce ne ku hûn 4 baytên din bixwînin intan jî 0x6789cdef, piştî ku dê tiştek tune. Tiştek din - hûn hewce ne ku îstîsnayek bavêjin. Lêbelê, piştî vê yekê em vegerin ser xwendina 4 bytes int zevî field_c в constructorTwo û bi vê yekê em xwendina xwe diqedînin PolymorType.

Наконец, если попался 0xdeadcrc bo constructorThree, wê demê her tişt tevlihevtir dibe. Qada me ya yekem e bit_flags_of_what_really_present bi tîp # - Bi rastî, ev tenê navek ji bo celebê ye nat, tê wateya "hejmara xwezayî". Ango, bi rastî, înt-a bênîşan, bi awayê, yekane rewş e ku jimareyên bênîşan di çerxên rastîn de çêdibin. Ji ber vê yekê, ya din avahiyek bi nîşana pirsê ye, tê vê wateyê ku ev qad - ew ê li ser têlê hebe tenê heke bit-a têkildar di qada ku tê de tête destnîşan kirin (teqrîben mîna operatorek sêalî) were danîn. Ji ber vê yekê, em bihesibînin ku ev bit hate saz kirin, ku tê vê wateyê ku bêtir hewce ne ku em zeviyek mîna bixwînin Type, у которого в нашем примере 2 конструктора. Один пустой (состоит только из идентификатора), в другом есть поле ids bi tîp ids:Vector<long>.

Dibe ku hûn bifikirin ku hem şablon û hem jî gelemperî di pêşnûmeyan an Java-yê de ne. Lê na. Hema hema. Ev tenê doza karanîna kelûpelên goşeyê di çerxên rastîn de, û ew TENÊ ji bo Vector tê bikar anîn. Di herikîna baytê de, ew ê ji bo celebê Vector bixwe 4 CRC32 byte bin, her gav heman, paşê 4 byte - hejmara hêmanên rêzê, û dûv re jî van hêmanan bixwe.

Vê rastiyê zêde bikin ku rêzenivîs her gav di peyvên 4 byte de pêk tê, hemî celeb jê pirjimar in - celebên çêkirî jî têne vegotin. bytes и string bi serialîzasyona desta ya dirêjiyê û vê hevrêziya 4-ê - baş e, wusa dixuye ku ew normal û hetta jî bi bandor bi bandor xuya dike? Her çend TL tê îddîa kirin ku serialîzasyonek binarîkî ya bi bandor e, lê bi dojehê re, bi berfirehbûna hema hema her tiştî, tewra nirxên Boolean û rêzikên yek-karakterî heya 4 byte, dê JSON hîn jî pir stûrtir be? Binêrin, tewra qadên nehewce jî dikarin bi alayên bit ve werin paşguh kirin, her tişt pir baş e, û ji bo pêşerojê jî berfireh e, ji ber vê yekê çima paşê qadên nû yên vebijarkî li çêker zêde nakin?..

Lê na, heke hûn ne kurteya min, lê belgeya tevahî bixwînin, û li ser pêkanînê bifikirin. Pêşîn, CRC32 ya çêker li gorî rêzika normalîzekirî ya danasîna nivîsê ya nexşeyê tê hesibandin (qedexeya spî ya zêde jêbirin, hwd.) - ji ber vê yekê heke qadek nû were zêdekirin, dê rêzika danasîna celebê biguhezîne, û ji ber vê yekê CRC32 û , di encamê de, serialization. Û ger xerîdarê kevin zevîyek bi alayên nû hatine danîn werbigire û nizane paşê bi wan re çi bike dê çi bike?..

Ya duyemîn, em bîr bînin CRC32, ku li vir bi bingehîn wekî tê bikar anîn fonksiyonên hash ji bo ku bi rengek yekta diyar bike ka kîjan celeb tê (bê) rêz kirin. Li vir em bi pirsgirêka pevçûnan re rû bi rû ne - û na, îhtîmal ne ji 232 yek e, lê pir mezintir e. Kê hate bîra kê ku CRC32 ji bo tespîtkirina (û rastkirina) xeletiyên di kanala ragihandinê de hatî çêkirin, û li gorî vê yekê van taybetmendiyan li zirara yên din çêtir dike? Mînakî, ew guh nade ji nû vesazkirina bytes: heke hûn CRC32 ji du rêzan hesab bikin, di ya duyemîn de hûn 4 baytên pêşîn bi 4 baytên din re biguhezînin - ew ê heman be. Gava ku têketina me rêzikên nivîsê yên ji alfabeya latînî (û piçek xalbendî) bin, û ev nav bi taybetî ne rasthatî bin, îhtîmala vesazkirinek wusa pir zêde dibe.

Кстати, а кто проверял, что там rastî CRC32? В одном из ранних исходников (еще до Вальтмана) была хэш-функция, умножавшая каждый символ на так любимое этими людьми число 239, ха-ха!

Наконец, ладно, мы поняли, что конструкторы с типом поля Vector<int> и Vector<PolymorType> dê CRC32 cûda hebe. Li ser performansa serhêl çi ye? Û ji hêla teorîk ve, ma ev dibe beşek ji cureyê? Ka em bibêjin ku em rêzek ji deh hezar jimaran derbas dikin, baş bi Vector<int> her tişt zelal e, dirêjî û 40000 baytên din. Çi eger ev Vector<Type2>, ku tenê ji yek zeviyê pêk tê int и он один в типе — надо ли нам 10000 раз повторять 0xabcdef34 и затем 4 байта int, или же язык в состоянии ВЫВЕСТИ это за нас из конструктора fixedVec û li şûna 80000 byte, dîsa tenê 40000 veguhezînin?

Ev qet ne pirsek teorîkî ya bêkar e - bifikire ku hûn navnîşek bikarhênerên grûpê werdigirin, ku her yek ji wan xwediyê nasname, nav, paşnav e - cûdahiya mîqdara daneya ku li ser pêwendiyek mobîl hatî veguheztin dikare girîng be. Ew bi rastî bandora serialîzasyona Telegram-ê ye ku ji me re tê reklam kirin.

Wiha…

Vector, который так и не смогли вывести

Если Вы попытаетесь продраться через страницы описания комбинаторов и около, Вы увидите, что вектор (и даже матрицу) формально пытаются вывести через tuples несколько листов. Но в конечном итоге забивают, конечный шаг пропускается, и просто дается определение вектора, который еще и не привязан к типу. В чем тут дело? В языках bernamekirin, особенно функциональных, вполне типично описать структуру рекурсивно — компилятор с его lazy evaluation сам всё поймёт и сделает. В языке serialization daneyan же необходима ЭФФЕКТИВНОСТЬ: достаточно просто описать lîsteya, yanî avahiya du hêmanan - ya yekem hêmanek daneyê ye, ya duyemîn heman avahî bixwe ye an cîhek vala ji bo dûvikê ye (pakêt (cons) li Lisp). Lê ev eşkere dê hewce bike ji her yekê element ji bo danasîna celebê xwe 4 baytên din (CRC32 di doza TL de) xerc dike. Arrayek jî bi hêsanî dikare were vegotin size sabît, lê di rewşek rêzek bi dirêjahiya nenas de pêş de, em vediqetin.

Ji ber vê yekê, ji ber ku TL rê nade vektorek derbikeve, diviyabû ew li kêlekê were zêdekirin. Di dawiyê de belge dibêje:

Rêzkirin her gav heman çêker "vektor" bikar tîne (const 0x1cb5c415 = crc32 ("vektor t: Tîpa # [ t ] = Vektor t") ku ne girêdayî nirxa taybetî ya guherbara tîpa t ye.

Nirxa parametra vebijarkî t di serialîzasyonê de têkildar nabe ji ber ku ew ji celebê encamê (her gav berî deserialîzasyonê tê zanîn) tête wergirtin.

Nêzîk binêre: vector {t:Type} # [ t ] = Vector t - lê li deverek Ev pênas bi xwe nabêje ku divê hejmara yekem bi dirêjahiya vektorê re be! Û ew ji tu derê nayê. Ev diyariyek e ku pêdivî ye ku meriv di hişê xwe de bihêle û bi destên we were bicîh kirin. Li deverek din, belge jî bi dilsozî behs dike ku celeb ne rast e:

Pseudotîpa polymorphic Vector t "cûreyek" e ku nirxa wê rêzek nirxan ji her cûre t ye, çi qutkirî an tazî.

... lê bala xwe nade ser. Gava ku hûn, ji dirêjkirina matematîkê westiyayî (dibe ku ji we re ji qursek zanîngehê jî were zanîn), biryar da ku dev jê berde û bi rastî binihêre ka meriv çawa di pratîkê de bi wê re bixebite, bandora ku di serê we de maye ev e ku ev ciddî ye. Matematîk di bingehê de, ew eşkere ji hêla Cool People (du matematîkzan - serketî ACM) ve hatî vedîtin, û ne tenê kesek. Armanc - xwenîşandan - pêk hat.

Кстати, о числе. Напомним, # ew hevwate ye nat, hejmara xwezayî:

Gotinên tîpan hene (type-expr) û bêjeyên hejmarî (nat-expr). Lêbelê, ew bi heman rengî têne destnîşan kirin.

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

lê di rêzimanê de jî bi heman awayî tên teswîrkirin, yanî. Divê ev cûdahî dîsa were bibîranîn û bi destan were bicîh kirin.

Ну и да, шаблонные типы (vector<int>, vector<User>) nasnameyek hevpar heye (#1cb5c415), ango. eger hûn dizanin ku bang wek ragihandin

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

wê hingê hûn êdî ne li benda vektorek, lê vektorek bikarhêneran in. Ya rasttir, divê ждать — в реальном коде каждый элемент, если не bare-тип, будет иметь конструктор, и по-хорошему в имплементации надо бы проверять — а нам точно в каждом элементе этого вектора прислали того типа? Ger ew celebek PHP-ê bûya, ku tê de arrayek dikare di hêmanên cihêreng de celebên cûda hebin?

Di vê nuqteyê de hûn dest pê dikin ku bifikirin - gelo TLyek wusa pêdivî ye? Dibe ku ji bo selikê gengaz be ku meriv serialîzatorek mirovî bikar bîne, heman protobufê ku wê hingê hebû? Teorî ev bû, em li pratîkê binêrin.

Di kodê de pêkanînên TL yên heyî

TL di kûrahiya VKontakte de ji berî bûyerên navdar ên bi firotina para Durov û (bi rastî), hê berî ku pêşveçûna Telegram dest pê bike. Û di çavkaniya vekirî de koda çavkaniyê ya pêkanîna yekem hûn dikarin gelek kincên qeşmer bibînin. Û ziman bi xwe li wir ji ya ku niha di Telegramê de ye bi tevahî bêtir hate bicîh kirin. Mînakî, haş di nexşeyê de qet nayên bikar anîn (tê wateya pseudotîpek çêkirî (wek vektorek) bi tevgerek devkî). An

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

lê bila em ji bo tambûnê bifikirin ku, bi vî rengî, pêşveçûna Dêwek Raman bişopînin.

#define ZHUKOV_BYTES_HACK

#ifdef ZHUKOV_BYTES_HACK

/* dirty hack for Zhukov request */

Или вот, прекрасное:

    static const char *reserved_words_polymorhic[] = {

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

      };

Ev parçe li ser şablonên wekî:

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

Ev pênasekirina celebek şablonê hashmap wekî vektora cotên int - Tîpa ye. Di C ++ de ew ê tiştek wusa xuya bike:

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

wiha, alpha - keyword! Lê tenê di C++ de hûn dikarin T-yê binivîsin, lê divê hûn alpha, beta binivîsin... Lê ji 8 parameteran bêtir, li vir xeyal diqede. Wusa dixuye ku carekê li St.

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

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

Lê ev yek li ser pêkanîna yekem a ku TL-ya "bi giştî" hat weşandin bû. Werin, em li ser pêkanînan di xerîdarên Telegram-ê de xwe bifikirin.

Слово Василию:

Vasily, [09.10.18 17:07] Больше всего жопа раскаляется от того, что они навертели кучу абстракций, а потом забили на них болт, и обложили кодогегератор костылями
Wekî encamek, yekem ji dock pilot.jpg
Piştre ji kodê dzhekichan.webp

Bê guman, ji mirovên ku bi algorîtma û matematîkê re nas dikin, em dikarin hêvî bikin ku wan Aho, Ullmann xwendibe, û bi amûrên ku di nav dehsalan de di pîşesaziyê de de facto standard bûne ji bo nivîsandina berhevkarên DSL-ya xwe nas dikin, rast?..

Ji telegram-cli Vitaly Valtman e, wekî ku ji qewimîna formata TLO-yê li derveyî sînorên wê (cli) tê fêm kirin, endamê tîmê ye - naha pirtûkxaneyek ji bo parskirina TL hatî veqetandin. cuda, bandora wê çi ye TL parser? ..

16.12 04:18 Vasily: Ez difikirim ku kesek lex+yacc nekiriye
16.12 04:18 Vasily: Ez nikarim wekî din vebêjim
16.12 04:18 Vasily: ну или им за количество строк в вк платили
16.12 04:19 Vasily: 3к+ строк др<censored> li şûna parsek

Dibe ku îstîsnayek? Ka em çawa bibînin dike Ev muwekîlê FERMIY e - Sermaseya Telegram:

    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+ строк на Питоне, пара регулярок + особые случаи типа вектора, который, конечно, объявлен в схеме как полагается по синтаксису TL, но клали они на этот синтаксис, парсить его еще… Спрашивается, зачем было городить всё это чудиMa ew qattir e ger kes bi her awayî wê li gorî belgeyan pars neke?!

Bi awayê ... Tê bîra me ku me li ser kontrolkirina CRC32 axivî? Ji ber vê yekê, di hilberînerê kodê Sermaseya Telegram de navnîşek îstîsnayan ji bo wan celebên ku tê de CRC32-ya hesabkirî heye heye. hev nagire bi ya ku di diagramê de hatî destnîşan kirin!

Vasily, [18.12 22:49] û li vir ez ê bifikirim ka TLyek weha hewce ye an na
ger min bixwesta ku bi pêkanînên alternatîf re mijûl bibim, ez ê dest bi xistina xêzikan bikim, nîvê parseran dê li ser pênaseyên pir-xêz bişkînin.
Lêbelê, tdesktop jî

Xala li ser yek-liner bîr bînin, em ê hinekî paşê vegerin ser wê.

Ладно, telegram-cli — неофициальный, Telegram Desktop — официальный, но что насчет других? А кто знает?.. В коде Android-клиента вообще не нашлось парсера схемы (что вызывает вопросы к опенсорсности, но это для второй части), зато нашлось несколько других весёлых кусков кода, но о них в подразделе ниже.

Di pratîkê de serialîzasyon çi pirsên din derdixe holê? Mînakî, wan gelek tişt kirin, bê guman, bi zeviyên bit û zeviyên şertî:

Vasily: flags.0? true
tê wê wateyê ku zevî heye û heke ala were danîn rast e

Vasily: flags.1? int
tê wê wateyê ku qad heyî ye û pêdivî ye ku were deserialîzekirin

Vasily: Жопа, не гори, что ты делаешь!
Vasily: Там где-то в доке есть упоминание, что true — это голый тип нулевой длины, но из их доки что-то собрать нереально
Vasily: Di pêkanînên çavkaniya vekirî de jî ev ne wusa ye, lê komek kulm û piştgirî hene

Çi li ser Telethon? Li pêşiya mijara MTProto dinihêrin, mînakek - di belgeyê de perçeyên weha hene, lê nîşana % ew bi tenê wekî "girêdayî cûreyek tazî ya diyarkirî" tê binav kirin, ango. di mînakên jêrîn de xeletiyek an tiştek nebelge heye:

Vasily, [22.06.18 18:38] В одном месте:

msg_container#73f1f8dc messages:vector message = MessageContainer;

Bi awayekî cuda:

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

И это две большие разницы, в реале приходит какой-то голый вектор

Min pênaseyek vektorê tazî nedîtiye û ez pê re nehatim

Analîz di teletonê de bi destan tê nivîsandin

Di diagrama wî de pênase tê şîrovekirin msg_container

Dîsa, pirs li ser % dimîne. Ew nayê vegotin.

Vadim Goncharov, [22.06.18 19:22] û di tdesktopê de?

Vasily, [22.06.18 19:23] Но их парсер TL на регуляиках это тоже скорее всего не съест

// parsed manually

TL abstrakasyoneke xweş e, kes bi tevahî pêk nayîne

Û % ne di guhertoya wan a nexşeyê de ye

Lê li vir belge bi xwe berevajî dike, ji ber vê yekê idk

Ew di rêzimanê de hate dîtin, wan bi hêsanî dikaribû ji bîr bikira ku ravekirina semantîkê

Ты ж видел доку на TL, там без поллитры не разберёшься

"Belê, em bibêjin," xwendevanek din dê bibêje, "hûn tiştekî rexne dikin, ji ber vê yekê nîşanî min bidin ku divê ew çawa were kirin."

Vasily bersivê dide: "Der barê parserê de, ez ji tiştên mîna hez dikim

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

bi rengekî çêtir jê hez dike

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

an

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

это ВЕСЬ лексер:

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

ewan. hêsantir bi hûrgulî tê gotin."

Bi gelemperî, di encamê de, parser û jeneratorê kodê ji bo binekoma TL-ya ku bi rastî hatî bikar anîn bi qasî 100 rêzikên rêzimanê û ~ 300 rêzikên jeneratorê cih digirin (hemû jimartin print's koda hatî çêkirin), di nav wan de bunneyên agahdariyê yên ji bo hundurîn di her polê de. Her celebek polîmorf vediguhere çînek bingehîn a razber a vala, û çêker jê mîrasê digirin û ji bo serialîzekirin û deserialîzasyonê rêbazên wan hene.

Нехватка типов в языке типов

Nivîsandina xurt tiştek baş e, rast? Na, ev ne holivar e (her çend ez zimanên dînamîk tercîh dikim), lê di çarçoveya TL de postulatek e. Li ser vê bingehê divê ziman ji bo me her cure kontrolê bide. Belê, baş e, belkî ne ew bixwe, lê bicihanîn, lê divê ew bi kêmanî wan vebêje. Û em çi fersend dixwazin?

Прежде всего, constraints. Вот мы видим в документации по закачке файлов:

Naveroka binary ya pelê paşê li beşan tê dabeş kirin. Divê hemî beşan xwedî heman mezinahiyê bin ( part_size ) û şertên jêrîn divê bêne bicîh kirin:

  • part_size % 1024 = 0 (li 1KB dabeş dibe)
  • 524288 % part_size = 0 (Divê 512 KB bi pîvan_parçeyê wekhev were dabeş kirin)

Beşa paşîn ne pêdivî ye ku van şertan têr bike, bi şertê ku mezinahiya wê ji part_size kêmtir be.

Divê her beş jimareyek rêzê hebe, file_part, bi nirxek ji 0 heta 2,999.

After the file has been partitioned you need to choose a method for saving it on the server. Use upload.saveBigFilePart in case the full size of the file is more than 10 MB and upload.saveFilePart for smaller files.
[…] dibe ku yek ji xeletiyên têketina daneya jêrîn were vegerandin:

  • FILE_PARTS_INVALID — Invalid number of parts. The value is not between 1..3000

Ma yek ji van di diagramê de ye? Ma ev bi rengek TL bikar tîne? Na. Lê min bibore, tewra bapîrê Turbo Pascal jî karîbû celebên diyarkirî rave bike диапазонами. Û wî tiştek din jî dizanibû, ku niha çêtir tê zanîn enum — тип, состоящий из перечисления фиксированного (небольшого) количества значений. В языках типа Си — числовых, заметьте, мы пока говорили только о типах jimare. Lê di heman demê de rêzik, rêzik jî hene... bo nimûne, dê xweş be ku meriv rave bike ku ev rêz tenê dikare jimareyek têlefonê bigire, ne?

Tiştek ji van di TL de nîne. Lê ji bo nimûne, di JSON Schema de heye. Û heke kesek din dikare li ser dabeşbûna 512 KB nîqaş bike, ku ev hîn jî pêdivî ye ku di kodê de were kontrol kirin, wê hingê piştrast bikin ku xerîdar bi tenê nikaribû послать номер вне диапазона 1..3000 (û xeletiya têkildar nedikarî çêbibe) ew ê gengaz bûya, rast?..

Bi awayê, li ser xeletî û nirxên vegerê. Tewra yên ku bi TL-yê re xebitîne jî çavên xwe dişewitînin - ev yek tavilê ji me re nedihat her yek fonksiyonek di TL de bi rastî dikare ne tenê celebê vegerê yê diyarkirî, lê di heman demê de xeletiyek jî vegerîne. Lê ev yek bi ti awayî bi bikaranîna TL bi xwe nayê derxistin. Bê guman, ew jixwe zelal e û di pratîkê de ne hewceyî tiştekê ye (her çend di rastiyê de, RPC dikare bi awayên cûda were kirin, em ê paşê vegerin ser vê yekê) - lê çi li ser Paqijiya têgînên Matematîkê yên Cureyên Abstract ji dinyaya bihuştê?.. Min tûg hilda - da bi hev re.

Û di dawiyê de, li ser xwendinê çi ye? Welê, li wir, bi gelemperî, ez dixwazim terîf wê di şemayê de rast hebe (di şemaya JSON de, dîsa ew e), lê heke hûn jixwe pê re tengezar bûne, wê hingê ji aliyê pratîkî ve - bi kêmanî di dema nûvekirinê de bi kêmasî li cûdahiyan mêze dikin? Ji xwe re bibînin mînakên rastîn:

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

an

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

Ew bi her kesî ve girêdayî ye, lê GitHub, mînakî, red dike ku guhertinên di hundurê xetên weha dirêj de ronî bike. Lîstika "10 ciyawaziyan bibîne", û ya ku mêjî tavilê dibîne ev e ku destpêk û dawiya her du mînakan yek in, hûn hewce ne ku hûn li deverek navîn bi westayî bixwînin... Bi dîtina min, ev ne tenê di teoriyê de ye. lê tenê bi dîtbarî qirêj û pîs.

Bi awayê, li ser paqijiya teoriyê. Çima em zeviyên bit hewce ne? Ma ew xuya nake bîn ji aliyê teoriya cureyê ve xerab e? Ravekirin dikare di guhertoyên berê yên diagramê de were dîtin. Di destpêkê de, erê, wusa bû, ji bo her pisîkê celebek nû hate afirandin. Ev rûdan hîn jî di vê formê de hene, wek nimûne:

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;

Lê naha bifikire, ger di avahiya we de 5 qadên vebijarkî hebin, wê hingê hûn ê ji bo hemî vebijarkên gengaz hewceyê 32 celeb bin. Teqîna Kombinatorî. Ji ber vê yekê, paqijiya krîstal a teoriya TL-ê li hember kerê hesinî yê rastiya tund a rêzenivîsê careke din şikand.

Bi ser de jî, li hinek cihan ev xort bi xwe tîpolojiya xwe binpê dikin. Mînakî, di MTProto de (beşa paşîn) bersiv dikare ji hêla Gzip-ê ve were berhev kirin, her tişt baş e - ji bilî ku qat û çerx têne binpêkirin. Careke din, ew ne RpcResult bixwe bû, lê naveroka wê bû. Baş e, çima vê yekê?.. Diviya bû ku ez bibirim nav kulmekê da ku kompresyon li her derê bixebite.

An mînakek din, me carekê xeletiyek dît - ew hat şandin InputPeerUser li gorî InputUser. An jî berevajî. Lê ew xebitî! Ango server guh nedaye celebê. Ev çawa dibe? Dibe ku bersiv bi perçeyên kodê yên telegram-cli ji me re were dayîn:

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

Иными словами, здесь сериализация делается MANUALLY, а не сгенерированным кодом! Может быть, сервер реализован аналогично?.. В принципе, такое сгодится, если сделать один раз, но как это потом поддерживать при обновлениях? Уж не за этим ли схема была придумана? И тут мы переходим к следующему вопросу.

Versioning. Layers

Çima ji guhertoyên şematîkî re qat tê gotin, tenê dikare li ser bingeha dîroka şematokên hatine weşandin were texmîn kirin. Xuya ye, di destpêkê de nivîskaran fikirîn ku tiştên bingehîn dikarin bi karanîna nexşeya neguhêrbar werin kirin, û tenê li cîhê ku hewce be, ji bo daxwazên taybetî, destnîşan dikin ku ew bi karanîna guhertoyek cûda têne kirin. Di prensîbê de, tewra ramanek baş e - û ya nû dê, wekî ku be, "tevlihev" be, li ser ya kevin were danîn. Lê em bibînin ka ew çawa hate kirin. Rast e, min nekaribû ji destpêkê ve lê binihêrim - ew qeşeng e, lê diagrama qata bingehîn bi tenê tune. Qat bi 2 dest pê kir. Belge ji me re li ser taybetmendiyek TL ya taybetî vedibêje:

Ger xerîdar Layer 2 piştgirî dike, wê hingê pêdivî ye ku çêkerê jêrîn were bikar anîn:

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

Di pratîkê de, ev tê vê wateyê ku berî her bangek API-ê, nirxek bi nirx heye 0x289dd1f6 must be added before the method number.

Deng normal. Lê paşê çi qewimî? Piştre xuya bû

invokeWithLayer3#b7475268 query:!X = X;

Ji ber vê yekê çi ye? Wekî ku hûn texmîn dikin,

invokeWithLayer4#dea0d430 query:!X = X;

Ecayib? Na, hê zû ye ku bikenin, li ser vê rastiyê bifikirin her daxwazek ji qatek din pêdivî ye ku di celebek wusa taybetî de were pêçandin - heke ew hemî ji we re cûda bin, wekî din hûn dikarin wan çawa ji hev cuda bikin? Û lê zêdekirina tenê 4 bytes li pêş rêbazek pir bikêr e. Wiha,

invokeWithLayer5#417a57ae query:!X = X;

Lê diyar e ku piştî demekê ev ê bibe celebek bacchanalia. Û çareserî hat:

Nûvekirin: Bi Layer 9 dest pê dike, rêbazên alîkar invokeWithLayerN tenê bi hev re dikare were bikar anîn initConnection

Hooray! Piştî 9 guhertoyan, em di dawiyê de gihîştin tiştê ku di protokolên Internetnternetê de di salên 80-an de hatî kirin - di destpêka pêwendiyê de carekê li ser guhertoyê li hev kirin!

Îcar paşê çi ye?..

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

Lê niha hûn hîn jî dikarin bikenin. Tenê piştî 9 qatên din, di dawiyê de avakerek gerdûnî ya bi jimareyek guhertoyê hate zêdekirin, ku divê di destpêka pêwendiyê de tenê carekê were gazî kirin, û wateya qatan wenda xuya bû, naha ew tenê guhertoyek şert e, mîna li her derê din. Kêşe hat çareserkirin.

Точно?..

Vasily, [16.07.18 14:01] Roja Înê ez fikirîm:
Teleserver bêyî daxwazek bûyeran dişîne. Pêdivî ye ku daxwaz di InvokeWithLayer de werin pêçan. Pêşkêşkar nûvekirinan pêça nake; avahiyek ji bo pêçakirina bersiv û nûvekirinê tune.

Ewan. xerîdar nikare qata ku tê de nûvekirinan dixwaze diyar bike

Vadim Goncharov, [16.07.18 14:02] Ma InvokeWithLayer di prensîbê de ne kêşek e?

Vasily, [16.07.18 14:02] Tenê rê ye

Vadim Goncharov, [16.07.18 14:02] ku di bingeh de divê were vê wateyê ku di destpêka danişînê de li ser qatê lihevhatin

Bi awayê, li dû vê yekê ye ku dakêşana xerîdar nayê peyda kirin

Апдейты, т.е. тип Updates di pilanê de, ev e ya ku server ne wekî bersivek daxwazek API-yê, lê gava ku bûyerek diqewime serbixwe ji xerîdar re dişîne. Ev mijarek tevlihev e ku dê di postek din de were nîqaş kirin, lê ji bo naha girîng e ku hûn zanibin ku server Nûvekirinên tomar dike tewra dema ku xerîdar negirêdayî ye.

Ji ber vê yekê, heke hûn pêçanê red bikin ji her yekê pakêt ji bo ku guhertoya xwe destnîşan bike, ev bi mentiqî dibe sedema pirsgirêkên gengaz ên jêrîn:

  • pêşkêşker nûvekirinan ji xerîdar re dişîne berî ku xerîdar agahdar bike ka kîjan guhertoyê piştgirî dike
  • piştî nûvekirina xerîdar divê ez çi bikim?
  • garantî dikeku nerîna serverê di derheqê jimareya qatê de dê di dema pêvajoyê de neyê guhertin?

Ma hûn difikirin ku ev tenê spekulasyonek teorîkî ye, û di pratîkê de ev nabe, ji ber ku server rast hatî nivîsandin (kêmtir, ew baş tê ceribandin)? Ha! Çawa be bila bibe!

Именно на это мы в августе и напоролись. 14 августа мелькали сообщения, что на серверах Telegram что-то обновляеют… а дальше в логах:

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.

û dûv re çend megabytes şopên stackê (baş, di heman demê de têketin hate rast kirin). Beriya her tiştî, heke tiştek di TL-ya we de neyê nas kirin, ew ji hêla îmzeyê ve binary e, li jêr rêzê GIŞT diçe, deşîfrekirin dê ne mumkin bibe. Di rewşeke weha de divê hûn çi bikin?

Welê, yekem tiştê ku tê hişê her kesî ev e ku veqetîne û dîsa biceribîne. Alîkarî nekir. Em li CRC32 di google-ê de digerin - ev derketin ku tiştên ji pilana 73-an in, her çend em li ser 82-an xebitîn.

Dibe ku pirsgirêk bi tenê di muwekîlê meya nefermî de ye? Na, em Telegram Desktop 1.2.17 dest pê dikin (guhertoya ku di gelek belavokên Linux-ê de tê peyda kirin), ew di têketina îstisnayê de dinivîse: MTP Nasnameya celebê nediyar #b5223b0f di MTPMessageMedia de tê xwendin…

Rexne li protokol û nêzîkatiyên rêxistinî yên Telegramê. Beş 1, teknîkî: ezmûna nivîsandina xerîdarek ji sifirê - TL, MT

Google destnîşan kir ku pirsgirêkek wusa jixwe ji yek ji xerîdarên nefermî re çêbûye, lê dûv re hejmarên guhertoyê û, li gorî vê, texmînên cûda cûda bûn ...

Îcar divê em çi bikin? Ez û Vasily ji hev veqetiyan: wî hewl da ku dora 91-ê nûve bike, min biryar da ku çend rojan li bendê bim û li ser 73-ê biceribînim. Her du rêbaz jî xebitîn, lê ji ber ku ew ampîrîk in, têgihîştin ku hûn çend guhertoyên jor an jêrîn hewce ne bazdin, an jî çiqasî hewce ye ku hûn li bendê bin.

Позже у меня получилось воспроизвести ситуацию: запускаем клиент, отключаем, перекомпилируем схему на другой слой, перезапускаем, снова ловим проблему, возвращаемся на предыдущий — опа, уже никакие переключения схемы и перезапуски клиента в течение нескольких минут не помогут. Вам будет приходить микс из структур данных с разных слоёв.

Daxûyanî? Wekî ku hûn dikarin ji nîşanên cûda yên nerasterast texmîn bikin, server ji gelek pêvajoyên cûrbecûr ên li ser makîneyên cihêreng pêk tê. Bi îhtimaleke mezin, servera ku berpirsiyarê "buffering"ê ye, tiştê ku serdestên wê dane wê, datîne nav rêzê, û wan ew di pilana ku di dema nifşê de li cîh bû da. Û heta ku ev dorê "xirab bibe", tiştek jê re nedihat kirin.

Belkî... lê ev kêzikek tirsnak e?!.. Na, berî ku em li ser ramanên dîn bifikirin, em li koda xerîdarên fermî binerin. Di guhertoya Android-ê de em tu parserek TL nabînin, lê em pelek giran (GitHub red dike ku pê vebike) bi (de)serialîzekirinê re peyda dikin. Li vir perçeyên kodê hene:

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;

an

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

Кхм… выглядит дико. Но, наверное, это сгенерированный код, тогда ладно?.. Зато уж точно все версии поддерживает! Правда, непонятно, почему всё намешано в одну кучу, и секретные чаты, и всякие _old7 bi awayekî mîna nifşê makîneyê naxuyin ... Lêbelê, herî zêde ez ji hêla min ve hatim avêtin

TL_message_layer104
TL_message_layer104_2
TL_message_layer104_3

Gelî hevalno, hûn jî nikarin biryar bidin ka di hundurê yek qatê de çi heye?! Baş e, baş e, em bibêjin "du" bi xeletiyekê hatin berdan, baş e, wusa dibe, lê SÊ?.. Di cih de, dîsa heman rake? Bibore, ev çi celeb pornografî ye?..

Di koda çavkaniyê ya Telegram Desktop de, bi awayê, tiştek wusa diqewime - heke wusa be, çendîn peywirên li pey pileyê jimara qata wê naguhezînin, lê tiştek rast dikin. Di şert û mercên ku çavkaniyek fermî ya daneyê ji bo nexşeyê tune ye, ji ku derê dikare were wergirtin, ji bilî koda çavkaniyê ya xerîdarê fermî? Û heke hûn wê ji wir bistînin, hûn nekarin pê bawer bin ku nexşe bi tevahî rast e heya ku hûn hemî rêbazan ceribînin.

А как такое вообще можно тестировать? Надеюсь, любители юнит-, функциональных и прочих тестов поделятся в комментариях.

Baş e, em li perçeyek din a kodê binêrin:

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;

Вот этот комментарий «manually created» наводит на мысль, что лишь часть этого файла написана вручную (представляете весь кошмар в части maintenance?), а остальное таки сгенерировано машиной. Однако, тогда возникает другой вопрос — о том, что исходники доступны ne bi temamî (a la GPL di kernel Linux de dişewite), lê ev jixwe mijarek beşa duyemîn e.

Lê bes e. Werin em biçin ser protokola ku li ser wê hemî ev serialîzasyon dimeşe.

MT Proto

Ji ber vê yekê, em vekin danasîna giştî и danasîna berfireh ya protokolê û yekem tiştê ku em li ser terpilîn termînolojî ye. Û bi pirbûna her tiştî. Bi gelemperî, ev xuya dike ku taybetmendiyek xwedaniya Telegram-ê ye - bangkirina tiştan li cîhên cûda, an tiştên cûda bi yek peyvê, an jî berevajî (mînakek, di API-ya asta bilind de, heke hûn pakêtek çîpek bibînin, ew ne wusa ye. tu çi difikirî).

Например, «сообщение» (message) и «сессия» (session) — здесь значат другое, чем в привычном интерфейсе Telegram-клиента. Ну, с сообщением всё понятно, его можно было бы трактовать в терминах ООП, или же просто называть словом «пакет» — это низкий, транспортный уровень, здесь не те сообщения, что в интерфейсе, много служебных. А вот сессия… но обо всём по порядку.

layer transport

Yekem tişt veguhestin e. Ew ê li ser 5 vebijarkan ji me re bibêjin:

  • TCP
  • Websocket
  • Websocket li ser HTTPS
  • HTTP
  • HTTPS

Vasily, [15.06.18 15:04] А ещё есть UDP транспорт, но он не документирован

Û TCP di sê guhertoyan de

Yekem wekî UDP-ê li ser TCP-ê ye, her pakêt hejmarek rêzik û crc vedigire
Çima xwendina belgeyên li ser selikê ewqas bi êş e?

Erê, ew niha heye TCP jixwe di 4 guhertoyan de ye:

  • Kurtedirêj
  • Di nav
  • Padded intermediate
  • Tije

Welê, ok, ji bo MTProxy navbeynkarek Padded, ev dûv re ji ber bûyerên naskirî hate zêdekirin. Lê çima du guhertoyên din (bi tevahî sê) gava ku hûn dikarin bi yek re bigihîjin? Her çar di bingeh de tenê di awayê danîna dirêjahî û bargiraniya sereke ya MTProto de cûda dibin, ku dê bêtir were nîqaş kirin:

  • di Abridged de ew 1 an 4 bytes e, lê ne 0xef, paşê laş e
  • di Navberê de ev 4 byte dirêjî û zeviyek e, û yekem car divê xerîdar bişîne 0xeeeeeeee nîşan bide ku ew Navber e
  • Bi tevahî, ji nihêrîna torêvanek herî tiryakê: dirêjî, jimareya rêzê, û NE YÊ ku bi giranî MTProto, laş, CRC32 ye. Erê, ev hemî li ser TCP-ê ye. Ku ji me re veguheztina pêbawer di şiklê herikîna byte-ya rêzdar de peyda dike; ne rêzik hewce ne, nemaze jimareyên kontrolê. Baş e, naha kes dê li hember min îtiraz bike ku TCP xwedan jimareyek 16-bit heye, ji ber vê yekê xirabiya daneyê çêdibe. Baş e, lê di rastiyê de me protokolek krîptografî heye ku bi haşeyan ji 16 byte dirêjtir e, van hemî xeletî - û hêj bêtir - dê di astek bilind de ji hêla SHA-yê ve bête girtin. Li ser vê yekê di CRC32 de TU xal tune.

Werin em Abrridged-ê, ku tê de dirêjahiya yek byte mimkun e, bi Intermediate re bidin ber hev, ku "Di rewşek ku berhevkirina daneya 4-byte hewce ye" rewa dike, ku ev yek pir bêaqil e. Çi, tê bawer kirin ku bernamenûsên Telegram ew qas bêkêmasî ne ku ew nikanin daneyan ji soketê di nav tamponek hevgirtî de bixwînin? Hûn hîn jî neçar in ku vê yekê bikin, ji ber ku xwendin dikare ji we re hejmarek byte vegerîne (û wekî mînak serverên proxy jî hene ...). An jî ji hêla din ve, çima Kurtkirî asteng bikin heke em ê hîn jî li ser 16 baytên pêlavek giran hebin - 3 bayt hilînin carinan ?

Tê dîtin ku Nikolai Durov bi rastî hez dike ku ji nû ve îcadkirina tekeran, tevî protokolên torê, bêyî hewcedariya pratîkî ya rastîn.

Vebijarkên din ên veguhastinê, di nav de. Web û MTProxy, heke daxwazek hebe, em ê naha bifikirin, dibe ku di postek din de. Di derbarê vê heman MTProxy-ê de, bila em niha tenê ji bîr mekin ku demek kurt piştî serbestberdana wê di sala 2018-an de, pêşkêşvan zû fêr bûn ku wê asteng bikin, ku ji bo astengkirina dorpêçêli gor размеру пакета! Û her weha rastiya ku servera MTProxy ya ku di C de hatî nivîsandin (dîsa ji hêla Waltman ve) pir bi taybetmendiyên Linux-ê ve girêdayî bû, her çend ev yek ne hewce bû (Phil Kulin dê piştrast bike), û ku serverek wusa di Go an Node.js de dê di kêmtirî sed rêzan de cih digire.

Lê em ê di dawiya beşê de, piştî nirxandina mijarên din, li ser xwendewariya teknîkî ya van kesan encaman derxin. Heya nuha, em biçin ser OSI layer 5, danişîn - ku li ser wê danişîna MTProto danîn.

Keys, peyam, danişîn, Diffie-Hellman

Wan ew li wir ne bi tevahî rast bi cih kirin... Danişîn ne heman danişîn e ku di navberê de di binê danişînên çalak de xuya dibe. Lê bi rêz.

Rexne li protokol û nêzîkatiyên rêxistinî yên Telegramê. Beş 1, teknîkî: ezmûna nivîsandina xerîdarek ji sifirê - TL, MT

Ji ber vê yekê me rêzek byte ya bi dirêjahiya naskirî ji qata veguhastinê wergirt. Ev an peyamek şîfrekirî ye an nivîsek eşkere ye - heke em hîn jî di qonaxa peymana sereke de ne û bi rastî wiya dikin. Em behsa kîjan ji komek têgînên bi navê "kilît" dikin? Ka em vê pirsgirêkê ji bo tîmê Telegram bixwe zelal bikin (Ez lêborînê dixwazim ku bi mejiyek westiyayî di demjimêr 4-ê sibehê de belgeyên xwe ji Englishngilîzî werdigerînim, hêsantir bû ku meriv hin hevokan wekî wan bihêle):

Есть две сущности под названием rûniştinî — одна в UI официальных клиентов под «current sessions», где каждой сессии соответствует целое устройство / OS.
Duyem - MTProto session, ku hejmara rêza peyamê (di wateya nizm de) tê de heye, û kîjan может длиться между разными TCP-соединениями. Одновременно могут быть установлены несколько MTProto-сессий, например для ускорения закачки файлов.

Di navbera van herduyan de danişînên находится понятие wekîlkirinî. Di rewşa dejenere de, em dikarin bibêjin ku danişîna UI есть то же, что wekîlkirinî, lê mixabin, her tişt tevlihev e. Ka em lê binêrin:

  • Bikarhêner li ser cîhaza nû yekem diafirîne auth_key û wê bi hesabê ve girêdide, mînakî bi SMS - ji ber vê yekê wekîlkirinî
  • Ew di hundurê yekem de çêbû MTProto session, ku heye session_id di hundurê xwe de.
  • Di vê gavê de, tevlihev wekîlkirinî и session_id dikare were gotin nimûne - ev peyv di belge û koda hin xerîdaran de xuya dike
  • Dûv re, xerîdar dikare veke çend MTProto sessions di bin heman auth_key — к одному и тому же DC.
  • Dûv re, rojek xerîdar dê hewce bike ku pelê jê bixwaze DC din - û ji bo vê DC-ya nû dê were çêkirin auth_key !
  • Ji bo pergalê agahdar bikin ku ew ne bikarhênerek nû ye ku qeyd dike, lê heman e wekîlkirinî (danişîna UI), xerîdar bangên API bikar tîne auth.exportAuthorization в домашнем DC auth.importAuthorization di DC ya nû de.
  • Her tişt yek e, dibe ku çend vekirî bin MTProto sessions (каждая с собственным session_id) ji bo vê DC-ya nû, li jêr xwe auth_key.
  • Di dawiyê de, dibe ku xerîdar Veşartina Pêşverû ya Perfect bixwaze. Herkes auth_keyherdem key - per DC - û xerîdar dikare bang bike auth.bindTempAuthKey ji bo bikaranîna derbasî auth_key - û dîsa, tenê yek temp_auth_key per DC, общий для всех MTProto sessions к этому DC.

hay jê hebe, ew xwê (û xwêyên paşerojê) jî yek li ser e auth_key т.е. shared между всеми MTProto sessions li heman DC.

Wateya "di navbera girêdanên TCP yên cihêreng" de çi ye? Ji ber vê yekê ev tê wateya tiştekî wek cookie destûrnameyê li ser malperek - ew gelek girêdanên TCP-ê bi serverek diyarkirî re berdewam dike (dijî) lê rojek ew xirab dibe. Tenê berevajî HTTP-ê, di MTProto de peyamên di nav danişînê de bi rêz têne hejmartin û piştrast kirin; heke ew ketin tunelê, pêwendiyek qut bû - piştî ku pêwendiyek nû saz kir, server dê di vê danişînê de bi dilovanî her tiştê ku di berê de radest nekiriye bişîne. Girêdana TCP.

Lêbelê, agahdariya li jor piştî lêkolîna gelek mehan tête kurt kirin. Di vê navberê de, gelo em muwekîlê xwe ji sifrê bicîh dikin? - em vegerin ser destpêkê.

Ji ber vê yekê em hilberînin auth_key li ser Guhertoyên Diffie-Hellman ji Telegram. Ka em hewl bidin ku belgeyê fêm bikin...

Vasily, [19.06.18 20:05] data_with_hash := SHA1 (dane) + dane + (biteyên bêserûber); wisa ku dirêjahî 255 byte ye;
encrypted_data := RSA(data_bi_hash, server_public_key); jimareyek dirêj a 255-byte (endiya mezin) bi hêza pêdivî li ser modula pêdivî tê hilanîn, û encam wekî jimarek 256-byte tê hilanîn.

DH hin dope hene

Dişibe DH ya mirovekî saxlem
В дх нет двух публичных ключей

Welê, di dawiyê de ev yek hate rêz kirin, lê bermayek ma - delîla xebatê ji hêla xerîdar ve tê kirin ku wî karîbû hejmarê faktor bike. Cureya parastinê li dijî êrîşên DoS. Û mifteya RSA tenê carekê di yek alî de tê bikar anîn, bi bingehîn ji bo şîfrekirinê new_nonce. Но пока эта вроде бы простая операция получится, с чем придется столкнуться?

Vasily, [20.06.18 00:26] Ez hê negihîştim daxwaza sepanê

Это я запрос на DH отправил

Û, di doka veguhastinê de ew dibêje ku ew dikare bi 4 bytes kodek xeletiyek bersiv bide. Navê pêger

Baş e, wî ji min re got -404, îcar çi?

Ji ber vê yekê min jê re got: "Bêşengiya xwe ya bi mifteya serverê bi şopa tiliyek wusa ve hatî şîfrekirin bigire, ez DH-ê dixwazim," û wê bi 404-ek ehmeq bersiv da.

Что бы Вы подумали на такой ответ сервера? Что делать? Спросить-то не у кого (но об этом во второй части).

Тут весь интерес по доке сделать

Tiştê min ê din tune ku ez bikim, min tenê xewna veguhertina jimareyan paş û paş kir

Du hejmarên 32 bit. Min ew jî wek her kesî pak kirin

Lê na, pêdivî ye ku ev her du pêşî wekî BE li rêzê werin zêdekirin

Vadim Goncharov, [20.06.18 15:49] û ji ber vê yekê 404?

Vasily, [20.06.18 15:49] ДА!

Vadim Goncharov, [20.06.18 15:50] Ji ber vê yekê ez fêm nakim ku ew dikare "ne dît"

Vasily, [20.06.18 15:50] nêzîkî

Min nikarî veqetandinek wusa di faktorên bingehîn de bibînim%)

Даже error reporting не осилили

Vasily, [20.06.18 20:18] Oh, MD5 jî heye. Jixwe sê heşeyên cuda hene

Şopa tiliya sereke bi vî rengî tê hesibandin:

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

SHA1 û sha2

Ji ber vê yekê em bihêlin auth_key размером 2048 бит мы по Диффи-Хеллману получили. Что дальше? Дальше мы обнаруживаем, что младшие 1024 бита этого ключа никак не используются… но подумаем пока вот о чем. На данном шаге у нас есть с сервером общий секрет. Установлен аналог TLS-сессии, весьма затратной процедурой. Но сервер еще ничего не знает о том, кто мы такие! Еще нет, собственно, wekîlkirinî. Ewan. heke hûn di nav "têketin-şîfreya" de difikirin, wekî ku we carekê di ICQ-ê de kir, an jî bi kêmanî "key-têketinê", wekî di SSH de (mînak, li ser hin gitlab / github). Me yekî nenas wergirt. Ger server ji me re bêje "ev hejmarên têlefonê ji hêla DC-ya din ve têne xizmet kirin" çi dibe? An jî "hejmara têlefona we qedexe ye"? Ya herî çêtirîn ku em dikarin bikin ev e ku mifteyê bi hêviya ku ew bikêr be û wê hingê xirap nebe.

Bi awayê, me ew bi rezervan "wergirt". Mînakî, em ji serverê bawer dikin? Ger sexte be çi? Kontrolên krîptografîk hewce ne:

Vasily, [21.06.18 17:53] Ew xerîdarên mobîl pêşkêş dikin ku ji bo seretayî jimarek 2kbit kontrol bikin%)

Lê qet ne diyar e, nafeijoa

Vasily, [21.06.18 18:02] Dokument nabêje ku heke ne hêsan be çi bikin

Ne gotin. Ka em bibînin ka xerîdarê fermî yê Android-ê di vê rewşê de çi dike? YEK ew çi ye (и да, там весь файл интересный) — как говорится, я просто оставлю это здесь:

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

Na, bê guman ew hîn jî heye hin проверки простоты числа есть, но лично я достаточными познаниями в математике уже не обладаю.

Ладно, мы получили основной ключ. Чтобы авторизоваться, т.е. послать запросы, надо производить дальнейшее шифрование, уже с помощью AES.

Mifteya peyamê wekî 128 bitên navîn ên SHA256 ya laşê peyamê (di nav de danişîn, nasnameya peyamê, hwd.) tê pênase kirin, di nav de baytên padding jî hene, ku ji hêla 32 baytên ku ji mifteya destûrnameyê hatine girtin ve têne destnîşan kirin.

Vasily, [22.06.18 14:08] Average, bit, bit

Wergirtin auth_key. Gişt. Ji wan wêdetir... ji belgeyê ne diyar e. Hûn dikarin koda çavkaniya vekirî bixwînin.

Bala xwe bidinê ku MTProto 2.0 ji 12 heta 1024 baytên peldankê hewce dike, hîn jî bi şertê ku dirêjahiya peyama encam li 16 bayt were dabeş kirin.

Так сколько паддинга сыпать?

Û erê, di rewşeke xelet de 404 jî heye

Ger kesek bi baldarî diagram û nivîsa belgeyê lêkolîn kir, wan dît ku li wir MAC tune. Û ew AES di moda hin IGE de ku li cîhek din nayê bikar anîn tê bikar anîn. Ew, bê guman, di Pirs û Pirsên xwe de li ser vê yekê dinivîsin... Li vir, mîna, mifteya peyamê bi xwe jî haşa SHA ya daneyên deşîfrekirî ye, ku ji bo kontrolkirina yekrêziyê tê bikar anîn - û di bûyera nelihevkirinê de, ji ber hin sedeman belge pêşniyar dike ku bi bêdengî guh nedin wan (lê li ser ewlehiyê çi, heke ew me bişkînin?).

Ez ne krîptograf im, belkî di vê rewşê de ji hêla teorîk ve tiştek xelet tune. Lê ez dikarim bi zelalî pirsgirêkek pratîkî bi nav bikim, wekî mînakek Telegram Desktop bikar tîne. Ew cache-ya herêmî (hemû van D877F783D5D3EF8C) bi heman rengî wekî peyamên li MTProto şîfre dike (tenê di vê rewşê de guhertoya 1.0), ango. pêşî mifteya peyamê, dûv re jî daneya xwe (û li deverek mezinahiya sereke auth_key 256 bytes, bêyî ku msg_key bikarnayê). Ji ber vê yekê, pirsgirêk li ser pelên mezin xuya dibe. Ango, hûn hewce ne ku du kopiyên daneyan - şîfrekirî û deşîfrekirî biparêzin. Û eger wek nimûne megabytes, an vîdyoyê diherikînin hene?.. Pîvanên klasîk ên bi MAC-ê re piştî şîfretextê dihêle hûn wê biherikînin, tavilê wê bişînin. Lê bi MTProto re hûn ê neçar bibin di destpêkê de Tevahiya peyamê şîfre an şîfre bike, tenê wê hingê wê veguhezîne torê an dîskê. Ji ber vê yekê, di guhertoyên herî dawî yên Sermaseya Telegram de di cache-ê de ye user_data Formatek din jî tê bikar anîn - bi AES di moda CTR de.

Vasily, [21.06.18 01:27] Oh, min fêhm kir ku IGE çi ye: IGE yekem hewildana "moda şîfrekirinê ya rastdar" bû, bi eslê xwe ji bo Kerberos. Ew hewldanek têkçûyî bû (ew parastina yekrêziyê peyda nake), û pêdivî bû ku were rakirin. Ew destpêka lêgerînek 20-salî bû ji bo moda şîfrekirinê ya pejirandî ya ku dixebite, ku vê dawiyê di modên mîna OCB û GCM de bi dawî bû.

Û niha argumanên ji aliyê selikê ve:

Tîma li pişt Telegram, ku ji hêla Nikolai Durov ve tê rêvebirin, ji şeş şampiyonên ACM-ê pêk tê, nîvê wan di matematîkê de Ph.D. Nêzîkî du sal ji wan re girt ku guhertoya heyî ya MTProto derxînin.

Чот смешно. Два года на нижний уровень

An jî hûn dikarin tenê tls bigirin

Ладно, допустиим, шифрование и прочие нюансы мы сделали. Можно, наконец, посылать сериализованные в TL запросы и десериализовывать ответы? Так а что и как слать надо? Вот, допустим, метод initConnection, наверное это оно?

Vasily, [25.06.18 18:46] Têkiliyê dest pê dike û agahdariya li ser cîhaz û serîlêdana bikarhêner hilîne.

Ew app_id, device_model, system_version, app_version û lang_code qebûl dike.

Û hinek pirs

Belgekirin wekî her gav. Ji bo lêkolîna çavkaniya vekirî hîs bikin

Ger her tişt bi invokeWithLayer bi qasî zelal bû, wê hingê li vir çi xelet e? Derket holê, em bibêjin ku me heye - xerîdar jixwe tiştek hebû ku ji serverê bipirse - daxwazek heye ku me dixwest em bişînin:

Vasily, [25.06.18 19:13] Li gorî kodê dadbar kirin, banga yekem di nav vê crapê de tête pêçandin, û crap bixwe di nav invokewithlayer de tête pêçandin.

Çima nekare initConnection bibe bangek veqetandî, lê pêdivî ye ku pêçek be? Erê, wekî ku derket holê, divê ew her carê di destpêka her danişînê de were kirin, û ne yek carî, wekî bi kilîta sereke. Lebê! Ew ji hêla bikarhênerek bê destûr ve nayê gazî kirin! Niha em gihîştine qonaxa ku tê tetbîqkirin Ev yek rûpela belgekirinê - û ew ji me re vedibêje ku ...

Tenê beşek piçûk a rêbazên API-ê ji bikarhênerên bêdestûr re hene:

  • auth.sendCode
  • auth.resendCode
  • account.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.getLanguages
  • langpack.getLanguage

Ya yekem ji wan, auth.sendCode, û ew daxwaza yekem a hêja heye ku tê de em api_id û api_hash dişînin, û pişt re em SMSek bi kodek distînin. Û heke em di DC-ya çewt de bin (hejmarên têlefonê yên li vî welatî ji hêla yekî din ve têne xizmet kirin, mînakî), wê hingê em ê bi hejmara DC-ya xwestinê re xeletiyek bistînin. Ji bo ku hûn bizanin ka kîjan navnîşana IP-ê ji hêla jimareya DC-ê ve divê hûn pê ve girêbidin, ji me re bibin alîkar help.getConfig. Di demekê de tenê 5 navnîşan hebûn, lê piştî bûyerên navdar ên 2018-an, hejmar pir zêde bûye.

Naha em ji bîr mekin ku em gihîştin vê qonaxê li ser serverê nenas. Ma ne pir biha ye ku meriv tenê navnîşek IP-yê bistîne? Çima vê yekê, û operasyonên din, di beşa neşîfrekirî ya MTProto de nakin? Ez nerazîbûnê dibihîzim: "Em çawa dikarin piştrast bikin ku ew ne RKN e ku dê bi navnîşanên derewîn bersiv bide?" Ji bo vê yekê em bi bîr tînin ku, bi gelemperî, xerîdarên fermî вшиты RSA-ключи, yanî tu dikarî tenê nîşan ev agahî. Bi rastî, ev jixwe ji bo agahdariya li ser dorpêçkirina astengkirina ku xerîdar bi kanalên din distînin tê kirin (bi mentiqî, ev di MTProto bixwe de nayê kirin; hûn jî hewce ne ku zanibin ku hûn bi ku ve girêdin).

OK. Di vê qonaxa destûra xerîdar de, em hîna ne destûr in û serlêdana xwe tomar nekiriye. Em tenê dixwazin heya niha bibînin ka server çi bersivê dide rêbazên ku ji bikarhênerek nedestûr re peyda dibin. Û li vir…

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;

Di planê de, yekem tê duyemîn

Di şemaya tdesktop de nirxa sêyemîn e

Erê, ji hingê ve, bê guman, belge hatine nûve kirin. Her çend dibe ku ew di demek nêzîk de dîsa bêguneh bibe. Divê pêşdebirek nûjen çawa bizanibe? Dibe ku hûn serlêdana xwe tomar bikin, ew ê we agahdar bikin? Vasily ev kir, lê mixabin, wan tiştek jê re neşand (dîsa, em ê di beşa duyemîn de li ser vê biaxivin).

...We bala xwe dayê ku me berê bi rengekî berê xwe da API-yê, yanî. berbi asta din, û di mijara MTProto de tiştek wenda kir? Ne surprîz:

Vasily, [28.06.18 02:04] Mm, ew li hin algorîtmayên li ser e2e dikolin

Mtproto algorîtmayên şîfrekirinê û kilît ji bo her du domanan, û her weha piçek avahiyek pêçandî diyar dike.

Lê ew bi domdarî astên cihêreng ên stakê tevlihev dikin, ji ber vê yekê her gav ne diyar e ku mtproto li ku qediya û asta din dest pê kir

Как смешивают? Ну вот тот же временный ключ для PFS, например (кстати, Telegram Desktop его не умеет). Он выполняется запросом API auth.bindTempAuthKey, yanî ji asta jor. Lê di heman demê de ew di asta jêrîn de bi şîfrekirinê re têkildar dibe - piştî wê, wek nimûne, hûn hewce ne ku wê dîsa bikin initConnection hwd., ev ne tenê daxwaza normal. Tişta taybetî jî ev e ku hûn dikarin li her DC tenê YEK mifteya demkî hebe, her çend zevî auth_key_id di her peyamê de destûrê dide te ku bi kêmanî her peyamê mifteyê biguhezîne, û ku server xwedî maf e ku di her kêliyê de mifteya demkî "jibîr bike" - belge nabêjin ku di vê rewşê de çi bikin ... baş e, çima nekare 'Te çend kilît hene, mîna komek xwêyên pêşerojê, û ?..

Di derbarê mijara MTProto de çend tiştên din hene ku hêjayî balê ne.

Peyamên peyamê, msg_id, msg_seqno, piştrastkirin, pingên di riya çewt de û taybetmendiyên din

Çima hûn hewce ne ku li ser wan bizanibin? Ji ber ku ew berbi astek bilindtir "diherikin", û dema ku bi API-yê re dixebitin divê hûn ji wan haydar bin. Ka em bihesibînin ku em bi msg_key re eleqedar nabin; asta jêrîn ji me re her tişt deşîfre kiriye. Lê di hundurê daneyên deşîfrekirî de qadên me yên jêrîn hene (di heman demê de dirêjahiya daneyê jî, ji ber vê yekê em dizanin ku peldank li ku ye, lê ew ne girîng e):

  • xwê - int64
  • session_id - int64
  • message_id - int64
  • seq_no - int32

Ka em bînin bîra we ku ji bo tevahiya DC tenê yek xwê heye. Çima li ser wê dizanin? Ne tenê ji ber ku daxwazek heye get_future_salts, ku ji we re vedibêje ka kîjan navber dê derbasdar bin, di heman demê de ji ber ku ger xwêya we "xirab" be, wê hingê peyam (daxwaz) dê bi hêsanî winda bibe. Server dê, bê guman, xwêya nû bi weşandinê rapor bike new_session_created — но со старым придется как-то делать перепосылку, например. И этот вопрос влияет на архитектуру приложения.

Ji ber gelek sedeman destûr tê dayîn ku server bi tevahî danişînan bavêje û bi vî rengî bersivê bide. Bi rastî, danişîna MTProto ji hêla xerîdar ve çi ye? Ev du hejmar in session_id и seq_no сообщения внутри этой сессии. Ну, и нижележащее TCP-соединение, конечно. Допустим, наш клиент еще много чего не умеет, отсоединился, переподсоединился. Если это произошло быстро — в новом TCP-соединении продолжилась старая сессия, увеличиваем seq_no дальше. Если долго — сервер мог её удалить, потому что на его стороне это еще и очередь, как мы выяснили.

Divê çi be seq_no? Oh, ew pirsek dijwar e. Biceribînin ku bi rastî fêm bikin ku tê çi wateyê:

Peyama-related Content

A message requiring an explicit acknowledgment. These include all the user and many service messages, virtually all with the exception of containers and acknowledgments.

Hejmara Rêzeya Peyamê (msg_seqno)

A 32-bit number equal to twice the number of “content-related” messages (those requiring acknowledgment, and in particular those that are not containers) created by the sender prior to this message and subsequently incremented by one if the current message is a content-related message. A container is always generated after its entire contents; therefore, its sequence number is greater than or equal to the sequence numbers of the messages contained in it.

Ev çi celeb sîrkek e ku bi 1-ê zêde dibe, û dûv re jî ya din bi 2-an?.. Ez guman dikim ku di destpêkê de wateya wan "ji bo ACK-ê ya herî hindik girîng, ya mayî jimarek e", lê encam bi tevahî ne wekî hev e - bi taybetî, derdikeve, dikare were şandin çend tesdîqên ku heman hene seq_no! Çawa? Welê, mînakî, server tiştek ji me re dişîne, dişîne, û em bixwe jî bêdeng dimînin, tenê bi peyamên karûbarê ku wergirtina peyamên wê piştrast dikin bersiv didin. Di vê rewşê de, piştrastkirinên me yên derketinê dê heman hejmara derketinê hebe. Ger hûn bi TCP-ê nas in û difikirin ku ev bi rengek hov xuya dike, lê ew ne pir hov xuya dike, ji ber ku di TCP de seq_no nayê guhertin, lê piştrastkirin diçe seq_no li aliyê din ez ê lez bikim ku we aciz bikim. Pejirandin di MTProto de têne peyda kirin Ne li ser seq_no, wekî di TCP de, lê ji hêla msg_id !

Eve çîye msg_id, ji van qadan ya herî girîng? Nasnameya peyamek yekta, wekî ku ji navê xwe diyar dike. Ew wekî jimareyek 64-bit tê pênase kirin, bitên herî jêrîn ên ku dîsa sêrbaziya "server-ne-server" heye, û ya mayî jî îşaretek Unix-ê ye, tevî beşa perçeyî, 32 bit ber bi çepê ve guhestiye. Ewan. demjimêr bixwe (û peyamên bi demên ku pir cûda ne dê ji hêla serverê ve bêne red kirin). Ji vê yekê derdikeve ku bi gelemperî ev nasnameyek ji bo xerîdar gerdûnî ye. Ji ber vê yekê - em bîr bînin session_id - em garantî ne: Di bin ti şert û mercan de nikare peyamek ku ji bo yek danişînê hatî armanc kirin di danişînek cûda de were şandin. Yanî derdikeve holê ku jixwe heye ast - danişîn, hejmara danişînê, nasnameya peyamê. Çima wisa zêde tevlihevî, ev sir pir mezin e.

Û vî awayî, msg_id pêwîst ji bo ...

RPC: daxwaz, bersiv, xeletî. Confirmations.

Wekî ku we ferq kiriye, di diagramê de celeb an fonksiyonek taybetî ya "daxwaza RPC çêbikin" tune, her çend bersiv jî hebin. Beriya her tiştî, me peyamên girêdayî naverokê hene! Ku heye, her peyam dikare daxwazek be! An jî nebe. Di encamê da, ji her yekê e msg_id. Lê bersiv hene:

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

Li vir tê destnîşan kirin ku ev bersiv ji kîjan peyamê re ye. Ji ber vê yekê, di asta jorîn a API-ê de, hûn ê ji bîr nekin ku hejmara daxwaza we çi bû - ez difikirim ku ne hewce ye ku meriv rave bike ku kar asynkron e, û dibe ku di heman demê de çend daxwaz di pêşkeftinê de hebin, bersivên ku dikarin di her rêzê de werin vegerandin? Di prensîbê de, ji vê yekê û peyamên xeletiyê yên mîna ne karkeran, mîmariya li pişt vê dikare were şopandin: servera ku pêwendiyek TCP-ê bi we re diparêze balansek pêş-end e, ew daxwazan ber bi paşîn ve dişîne û wan bi navgîniya paşde kom dike. message_id. Wusa dixuye ku li vir her tişt zelal, mentiqî û baş e.

Erê?.. Û eger hûn li ser bifikirin? Jixwe, bersiva RPC bixwe jî qadek heye msg_id! Ma hewce ye ku em li serverê biqîrin "hûn bersiva min nadin!"? Û erê, di derbarê pejirandinan de çi hebû? Der barê rûpelê сообщения про сообщения ji me re dibêje çi ye

msgs_ack#62d6b459 msg_ids:Vector long = MsgsAck;

û divê ji her alî ve were kirin. Lê ne her gav! Ger we RpcResult standiye, ew bixwe wekî pejirandinek kar dike. Ango, server dikare bi MsgsAck bersivê bide daxwaza we - mîna, "min ew wergirt." RpcResult dikare yekser bersiv bide. Ew dikare her du be.

И да, Вы таки должны ответить на ответ! Подтверждением. Иначе сервер будет считать его недоставленным и вывалит Вам его опять. Даже после переподсоединения. Но тут, конечно, вопрос таймаутов возникнет. Рассмотрим их чуть позже.

Di vê navberê de, bila em li xeletiyên darvekirina pirsê yên muhtemel binêrin.

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

О, воскликнет кто-то, здесь более человечный формат — есть строка! Не торопитесь. Вот lîsteya çewtiyên, lê bê guman ne temam. Ji wê em fêr dibin ku kod e tiştekî wek HTTP-ошибки (ну разумеется, семантика ответов не соблюдается, местами они распределены по кодам как попало), а строка имеет вид типа БОЛЬШИЕ_БУКВЫ_И_ЦИФРЫ. Mînak, PHONE_NUMBER_OCCUPIED an FILE_PART_Х_MISSING. Welê, ew e, hûn ê hîn jî hewceyê vê rêzê bin пропарсить. Mînakî FLOOD_WAIT_3600 tê wê wateyê ku divê hûn saetekê li bendê bin, û PHONE_MIGRATE_5, ku jimareyek têlefonê bi vê pêşgir divê li DC 5-ê were tomar kirin. Zimanek me heye, rast? Em ne hewceyî argumanek ji rêzê ne, yên birêkûpêk dê bikin, baş e.

Dîsa, ev ne li ser rûpela peyamên karûbarê ye, lê, wekî berê bi vê projeyê re normal e, agahdarî dikare were dîtin на другой странице документации. An gumanê avêtin. Pêşîn, binihêrin, nivîsandina / binpêkirina qatê - RpcError может быть вложен в RpcResult. Çima ne li derve? Me çi hesab nekir?.. Li gorî vê yekê garantiya wê li ku ye RpcError dibe ku NE di nav de were bicîh kirin RpcResult, lê rasterast an hêlîn di celebek din de be?.. Û heke nikaribe, çima ne di asta jor de ye, yanî. wenda ye req_msg_id ? ..

Lê em li ser peyamên karûbarê berdewam bikin. Dibe ku xerîdar bifikire ku server demek dirêj difikire û vê daxwaza ecêb bike:

rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;

Sê bersivên gengaz ên vê pirsê hene, ku dîsa bi mekanîzmaya pejirandinê re têkildar in; hewl didin ku fêm bikin ka divê ew çi bin (û navnîşa gelemperî ya celebên ku pejirandinê ne hewce ne) ji xwendevan re wekî karê malê tê hiştin (têbînî: agahdariya di koda çavkaniyê Sermaseya Telegram ne temam e).

Girêdana narkotîkê: statûyên peyamê

Bi giştî li gelek cihan di TL, MTProto û Telegramê de bi giştî hesta serhişkiyê, lê ji edeb, taktîk û hwd. hunerê nerm Me bi hurmetî li hember vê yekê bêdeng hişt, û bêbextiyên di diyalogan de sansûr kir. Lêbelê, ev cîhОpiraniya rûpelê li ser e сообщения про сообщения Tewra ji bo min jî, yê ku ev demek dirêj bi protokolên torê re dixebitî û duçerxeyên cûrbecûr yên çolê dîtiye, şok e.

Ew bêkêmasî, bi pejirandinan dest pê dike. Piştre ew ji me re dibêjin

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;

Welê, her kesê ku bi MTProto re dest bi xebatê dike, dê neçar bimîne ku bi wan re mijûl bibe; di çerxa "serastkirin - ji nû ve berhevkirin - destpêkirin" de, girtina xeletiyên hejmarê an xwêya ku di dema guherandinê de xirab bûye tiştek hevpar e. Lêbelê, li vir du xal hene:

  1. Из этого следует, что оригинальное сообщение потеряно. Нужно городить какие-то очереди, рассмотрим это позже.
  2. Что за странные номера ошибок? 16, 17, 18, 19, 20, 32, 33, 34, 35, 48, 64… где остальные номера, Томми?

Di belgeyê de wiha tê gotin:

Mebest ev e ku nirxên_error_code têne kom kirin (error_code >> 4): Mînakî, kodên 0x40 - 0x4f bi xeletiyên di hilweşandina konteynerê de têkildar in.

но, во-первых, сдвиг в другую сторону, во-вторых, всё равно, где остальные коды? В голове автора?.. Впрочем, это мелочи.

Zehmetî di peyamên di derbarê rewşa peyam û kopiyên peyamê de dest pê dike:

  • Daxwaza Agahdariya Rewşa Peyamê
    Ger her aliyek ji bo demekê agahdarî li ser rewşa peyamên xwe yên derketinê negirtibe, ew dikare bi eşkere ji aliyek din bixwaze:
    msgs_state_req#da69fb52 msg_ids:Vector long = MsgsStateReq;
  • Peyama Agahdarî li ser Rewşa Peyaman
    msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
    Vir, info rêzek e ku ji bo her peyamek ji navnîşa msg_ids-a tê de tam yek byte statûya peyamê vedihewîne:

    • 1 = di derbarê peyamê de tiştek nayê zanîn (msg_id pir kêm e, dibe ku alîyê din ew ji bîr kiribe)
    • 2 = peyam nehat wergirtin (msg_id dikeve nav rêza nasnameyên hilanîn; Lêbelê, partiya din bê guman peyamek wusa negirtiye)
    • 3 = peyam nehat wergirtin (msg_id pir zêde ye; lêbelê, partiya din bê guman hîn negirtiye)
    • 4 = message received (note that this response is also at the same time a receipt acknowledgment)
    • +8 = peyam jixwe hatiye pejirandin
    • +16 = peyama ku pejirandî ne hewce ye
    • +32 = RPC query contained in message being processed or processing already complete
    • +64 = bersiva naverokê ya ji bo peyama ku berê hatî çêkirin
    • +128 = aliyek din bi rastî dizane ku peyam jixwe hatiye wergirtin
      Ev bersiv ne hewceyê pejirandinê ye. Ew bi serê xwe pejirandina msgs_state_req têkildar e.
      Bala xwe bidinê ku heke ji nişka ve derkeve holê ku partiya din peyamek ku xuya dike ji wî re hatî şandin tune, ew peyam bi hêsanî dikare ji nû ve were şandin. Tevî ku divê aliyek du kopiyên peyamê di heman demê de werbigire jî, dê dubare were paşguh kirin. (Heke pir wext derbas bûbe, û msg_id-ya orîjînal êdî ne derbasdar e, divê peyam di msg_copy de were pêçandin).
  • Ragihandina dilxwazî ​​ya Rewşa Mesajan
    Her aliyek dikare bi dilxwazî ​​​​aliyê din li ser rewşa peyamên ku ji hêla alîyê din ve hatî şandin agahdar bike.
    msgs_all_info#8cc0d131 msg_ids:Vector long info:string = MsgsAllInfo
  • Ragihandina Dilxwaz a Berfireh ya Rewşa Yek Peyamê
    ...
    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;
  • Daxwaza Eşkere ya Ji nû ve şandina Mesajan
    msg_resend_req#7d861a08 msg_ids:Vector long = MsgResendReq;
    Partiya dûr tavilê bi şandina peyamên daxwazkirî ji nû ve bersiv dide […]
  • Daxwaza Eşkere ya Ji bo Re-Send Bersiv
    msg_resend_ans_req#8610baeb msg_ids:Vector long = MsgResendReq;
    The remote party immediately responds by re-sending bersivên ji peyamên daxwazkirî re […]
  • Kopiyên Peyamê
    Di hin rewşan de, peyamek kevn bi msg_id ku êdî ne derbasdar e divê ji nû ve were şandin. Dûv re, ew di konteynirek kopî de tê pêçandin:
    msg_copy#e06046b2 orig_message:Message = MessageCopy;
    Piştî ku hate wergirtin, peyam wekî ku palpişt ne li wir be tê pêvajo kirin. Lêbelê, heke bi teqez were zanîn ku peyama orig_message.msg_id hatiye wergirtin, wê hingê peyama nû nayê pêvajo kirin (di heman demê de, ew û orig_message.msg_id têne pejirandin). Nirxa orig_message.msg_id divê ji msg_id-a konteynerê kêmtir be.

Em jî li ser çi bêdeng bimînin msgs_state_info опять торчат уши недоделанного TL (нужен был вектор байт, и в младших двух битах enum, а в старших флаги). Суть в другом. Кто-нибудь понимает, зачем всё это на практике di mişterek rastîn de hewce ye?.. Bi dijwarî, lê mirov dikare hin feydeyekê bifikire ger kesek bi debuggkirinê, û di moda înteraktîf de mijûl be - ji serverê bipirse ka çi û çawa. Lê li vir daxwaz têne vegotin gera dor.

Ji ber vê yekê divê her aliyek ne tenê şîfre bike û peyaman bişîne, lê di heman demê de daneyên di derbarê xwe de, li ser bersivên ji wan re, ji bo demek nediyar hilîne. Belgekirin ne dem û ne jî pêkanîna pratîkî ya van taybetmendiyan diyar nake. tu awayî. Ya herî ecêb ev e ku ew bi rastî di koda xerîdarên fermî de têne bikar anîn! Xuya ye ji wan re tiştekî ku di belgeyên giştî de cih negirtiye re gotiye. Ji kodê fêm bikin çima, êdî ne ew qas hêsan e ku di rewşa TL-ê de - ew ne parçeyek mentiqî (bi nisbetî) veqetandî ye, lê perçeyek bi mîmariya serîlêdanê ve girêdayî ye, yanî. dê ji bo famkirina koda serîlêdanê bi girîngî bêtir dem hewce bike.

Ping û demjimêr. Queues.

Ji her tiştî, heke em texmînên di derbarê mîmariya serverê de (belavkirina daxwaznameyên li ser piştan) bi bîr bînin, tiştek xemgîn peyda dibe - tevî hemî garantiyên radestkirinê yên di TCP-ê de (an dane têne radest kirin, an hûn ê di derheqê valahiyê de agahdar bibin, lê berî ku pirsgirêk çêbibe dê dane bêne radest kirin), ku piştrastkirina di MTProto bixwe de - garantî tune. Pêşkêşkar dikare bi hêsanî peyama we winda bike an bavêje, û tiştek li ser wê nayê kirin, tenê cûreyên cûrbecûr kêşan bikar bînin.

Û berî her tiştî - rêzikên peyaman. Welê, bi yek tiştî her tişt ji destpêkê ve diyar bû - pêdivî ye ku peyamek nepejirandin were hilanîn û rezîl kirin. Û piştî çend demjimêr? Û henekker wî nas dike. Dibe ku ew peyamên karûbarê tiryakê bi rengekî vê pirsgirêkê bi kêzikan çareser bikin, bêje, di Sermaseya Telegram de bi qasî 4 rêzên li gorî wan hene (dibe ku bêtir, wekî ku berê hatî behs kirin, ji bo vê yekê hûn hewce ne ku hûn bi ciddî li kod û mîmariya wê bigerin; di heman demê de dem, em dizanin ku ew nikare wekî nimûne were girtin; hejmarek celeb ji pilana MTProto tê de nayên bikar anîn).

Почему так происходит? Вероятно, программисты сервера не смогли обеспечить надежность внутри кластера, или хотя бы даже буферизацию на фронте-балансировщике, и переложили эту проблему на клиента. От безысходности Василий попытался реализовать альтернативный вариант, с всего двумя очередями, используя алгоритмы из TCP — замеряя RTT до сервера и корректируя размер «окна» (в сообщениях) в зависимости от числа неподтвержденных запросов. То есть, грубая такая эвристика для оценки загруженности сервера — сколько одновременно наших запросов он может жевать и не терять.

Baş e, ew e, hûn fêm dikin, rast? Ger pêdivî ye ku hûn TCP-ê dîsa li ser protokolek ku li ser TCP-ê dimeşe bicîh bikin, ev protokolek pir xirab hatî sêwirandin destnîşan dike.

Oh erê, çima hûn ji yekê zêdetir rêz hewce ne, û ev tê çi wateyê ji bo kesê ku bi API-a-asta bilind re dixebite? Binêrin, hûn daxwazek dikin, serialîze dikin, lê pir caran hûn nikarin tavilê bişînin. Çima? Ji ber ku dê bersiv be msg_id, ku demkî yeаEz etîketek im, ku peywira wî herî baş heya ku dibe dereng were paşve xistin - heke server wê ji ber nehevsengiya wextê di navbera me û wî de red bike (bê guman, em dikarin kêşek çêbikin ku dema me ji niha ve vediguhezîne ji serverê re bi lêzêdekirina deltayek ku ji bersivên serverê tê hesibandin - xerîdarên fermî vê yekê dikin, lê ji ber tamponkirinê ew xav û nerast e). Ji ber vê yekê, gava ku hûn bi bangek fonksiyonek herêmî ji pirtûkxaneyê daxwazek dikin, peyam di qonaxên jêrîn de derbas dibe:

  1. Ew di yek rêzê de dimîne û li benda şîfrekirinê ye.
  2. Tayîn kirin msg_id û peyam çû rêzek din - şandina gengaz; bişînin soketê.
  3. a) Pêşkêşkar bersiv da MsgsAck - peyam hate radest kirin, em wê ji "rêka din" jêbirin.
    b) An jî berevajî, wî tiştek jê hez nekir, wî bersiv da badmsg - ji "rêkek din" ji nû ve bişîne
    c) Tiştek nayê zanîn, pêdivî ye ku peyam ji rêzek din were şandin - lê bi rastî nayê zanîn kengê.
  4. Server di dawiyê de bersiv da RpcResult - Bersiva rastîn (an xeletî) - ne tenê hatî radest kirin, lê di heman demê de pêvajo jî.

Dibe ku, bikaranîna konteynir dikare bi qismî pirsgirêkê çareser bike. Ev gava ku komek peyam di yek de têne pak kirin, û server bi yekcarî, bi yekcarî, bi pejirandinek ji hemîyan re bersiv da msg_id. Но и отвергнет он эту пачку, если что-то пошло не так, тоже всю целиком.

Û di vê nuqteyê de ramanên ne-teknîkî têne lîstikê. Ji tecrubeyên xwe, me gelek qûntar dîtine, û ji bilî vê, em ê niha bêtir mînakên şîret û mîmariya xirab bibînin - di şert û mercên weha de, gelo hêjayî bawerî û girtina biryarên weha ye? Pirs retorîk e (bê guman ne).

Em behsa çi dikin? Ger li ser mijara "peyamên narkotîkê yên li ser peyaman" hûn hîn jî dikarin bi îtirazên wekî "hûn ehmeq in, we plana meya berbiçav fam nekir!" (ji ber vê yekê, wekî ku mirovên normal divê pêşî belgeyê binivîsin, bi mentiq û mînakên pevguhertina pakêtan, paşê em ê biaxivin), paşê dem/demjimêr pirsek bi tevahî pratîk û taybetî ye, her tişt li vir ji zû ve tê zanîn. Belgekirin ji me re li ser demaran çi vedibêje?

A server usually acknowledges the receipt of a message from a client (normally, an RPC query) using an RPC response. If a response is a long time coming, a server may first send a receipt acknowledgment, and somewhat later, the RPC response itself.

Xerîdarek bi gelemperî wergirtina peyamek ji serverek (bi gelemperî, bersivek RPC) qebûl dike, heke ew pir dereng neyê veguheztin (heke ew were çêkirin, bêje, 60-120 saniye li dû wergirtinê peyamek ji serverê). Lêbelê, heke ji bo demek dirêj ti sedem tune ku ji serverê re peyaman bişîne an heke hejmareke mezin ji peyamên nepejirkirî ji serverê re hebe (bibêjin, ji 16 zêdetir), xerîdar pejirandinek serbixwe vediguhezîne.

... Ez wergerînim: em bi xwe nizanin ka çiqas û çawa hewcedariya me pê heye, ji ber vê yekê em texmîn bikin ku bila wusa be.

Û li ser ping:

Peyamên Ping (PING/PONG)

ping#7abe77ec ping_id:long = Pong;

Bersiv bi gelemperî li heman girêdanê vedigere:

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

Ev peyam ne hewceyî pejirandinê ne. Pongek tenê di bersiva pingê de tê veguheztin dema ku ping dikare ji hêla her du aliyan ve were destpêkirin.

Girtina Têkiliya Deferkirî + PING

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

Wek ping dixebite. Wekî din, piştî ku ev hate wergirtin, server demjimêrek dest pê dike ku dê pêwendiya heyî ya disconnect_delay çend çirkeyan paşde bigire heya ku ew peyamek nû ya heman celebê wernegire ku bixweber hemî demjimêrên berê ji nû ve vedike. Mînakî, heke xerîdar van pingan her 60 çirkeyan carekê bişîne, dibe ku ew disconnect_delay wekî 75 çirkeyan destnîşan bike.

Ma tu dîn î?! Di 60 saniyeyan de, trên dê bikeve stasyonê, dakeve û rêwiyan bigire, û dîsa têkiliya di tunelê de winda bike. Di 120 saniyeyan de, dema ku hûn wê dibihîzin, ew ê bigihîje yekî din, û bi îhtîmalek mezin dê têkilî biqede. Welê, diyar e ku ling ji ku derê têne - "Min zengilek bihîst, lê nizanim ew li ku ye", algorîtmaya Nagl û vebijarka TCP_NODELAY heye, ku ji bo xebata înteraktîf hatî armanc kirin. Lê, min bibore, li ser nirxa xweya xwerû - 200 bisekinin Milliсекунд. Если вам так уж хочется изобразить нечто похожее и сэкономить на возможной паре пакетов — ну отложите, накрайняк, на 5 секунд, или чему там сейчас равен таймаут сообщения «User is typing…». Но не больше.

Û di dawiyê de, ping. Ev e, kontrolkirina zindîbûna girêdana TCP. Kêfxweş e, lê nêzîkê 10 sal berê min nivîsek rexneyî li ser peyamnêrê jûreya fakulteya me nivîsand - nivîskarên li wir jî server ji xerîdar ping kirin, û ne berevajî. Lê xwendekarên pola 3-an tiştek in, û nivîsgehek navneteweyî tiştek din e, rast?..

Сначала небольшой ликбез. TCP-соединение, при отсутствии обмена пакетами, может жить неделями. Это и хорошо, и плохо, в зависимости от цели. Хорошо, если у Вас было открыто SSH-соединение на сервер, Вы встали из-за компа, перезагрузили роутер по питанию, вернулись на место — сессия через этот сервер не порвалась (ничего не набирали, пакетов не было), удобно. Плохо, если на сервере тысячи клиентов, каждый занимает ресурсы (привет, Постгрес!), и хост клиента, возможно, давно уже перезагрузился — но мы об этом не узнаем.

Pergalên chat / IM-ê ji ber sedemek din - statûyên serhêl dikevin doza duyemîn. Ger bikarhêner "hilweşiya", hûn hewce ne ku li ser vê yekê muxatabên xwe agahdar bikin. Wekî din, hûn ê bi xeletiyek ku afirînerên Jabber çêkiriye (û 20 salan rast kir) biqede - bikarhêner jê qut bûye, lê ew berdewam dikin ku ji wî re peyaman dinivîsin, ji ber ku bawer dikin ku ew serhêl e (yên ku di van de jî bi tevahî winda bûne çend hûrdem berî ku veqetandin hate kifş kirin). Na, vebijarka TCP_KEEPALIVE, ya ku gelek kesên ku fêm nakin ka demjimêrên TCP-ê çawa bi rengek bêkêmasî dixebitînin (bi danîna nirxên hov ên mîna deh çirkeyan), dê li vir ne alîkar be - hûn hewce ne ku pê ewle bin ku ne tenê kernel OS makîna bikarhêner sax e, lê di heman demê de bi normalî dixebite, nikare bersivê bide, û serîlêdan bixwe jî (hûn difikirin ku ew nikare bicemidîne? Sermaseya Telegram li ser Ubuntu 18.04 ji min re ji carekê zêdetir cemidand).

Именно поэтому пинговать должен server xerîdar, û ne berevajî - heke xerîdar vê yekê bike, heke girêdan têkbiçe, ping nayê radest kirin, armanc dê neyê bidestxistin.

Em li ser Telegram çi dibînin? Tam berovajî ye! Belê, ew e. Bi awayekî fermî, bê guman, her du alî dikarin hevdu ping bikin. Di pratîkê de, xerîdar kelekek bikar tînin ping_delay_disconnect, ku demjimêrê li ser serverê saz dike. Welê, min bibore, ne li ser xerîdar e ku biryar bide ka ew dixwaze kengî bêyî ping li wir bijî. Server, li ser bingeha barkirina xwe, çêtir dizane. Lê, bê guman, heke hûn ji çavkaniyan aciz nebin, wê hingê hûn ê bibin Pinocchio-ya xweya xirab, û kulmek dê bike…

А как надо было проектировать?

Ez bawer dikim ku rastiyên li jor eşkere destnîşan dikin ku tîmê Telegram/VKontakte di warê veguheztina (û kêmtir) asta torên komputerê û kalîteyên wan ên kêm di mijarên têkildar de ne pir jêhatî ye.

Çima ew qas tevlihev derket, û mîmarên Telegram çawa dikarin hewl bidin ku îtîraz bikin? Rastiya ku wan hewl da ku danişînek ku pêwendiya TCP-ê sax bimîne têk diçe, ango ya ku naha nehatiye radest kirin, em ê paşê radest bikin. Belkî wan jî hewl da ku veguheztinek UDP-yê çêbike, lê wan rastî dijwariyan hat û dev jê berda (ji ber vê yekê belge vala ye - tiştek ku pesnê xwe bide tune). Lê ji ber têgihiştinek ku torgilok bi gelemperî û TCP bi taybetî çawa dixebitin, hûn dikarin li ku derê xwe bispêrin wê û li ku hûn hewce ne ku wiya bi xwe bikin (û çawa), û hewildanek ku vê yekê bi krîptografiyê re "du çûk bi yek kevirî re" bi hev re bikin, ” Encam cesaretek wisa bû.

Çawa pêwîst bû? Li ser bingeha ku msg_id ji bo pêşîlêgirtina êrîşên dubarekirinê ji hêla krîptografîk ve zemanek pêdivî ye, xeletiyek e ku meriv fonksiyonek nasnameyek yekta pê ve girêbide. Ji ber vê yekê, bêyî guheztina bingehîn a mîmariya heyî (gava ku nûvekirina Nûvekirinê tê hilberandin, ew ji bo beşek din a vê rêze posteyan mijarek API-a-asta bilind e), pêdivî ye ku meriv:

  1. Pêşkêşkara ku pêwendiya TCP-ê bi xerîdar re digire berpirsiyariyê digire - heke ew ji soketê xwendibe, ji kerema xwe xeletiyek qebûl bikin, pêvajo bikin an vegerînin, bê windahî. Dûv re pejirandin ne vektorek nasnameyan dibe, lê bi tenê "seq_no-ya paşîn a wergirtî" - tenê hejmarek, wekî di TCP-ê de (du hejmar - rêzika we û ya pejirandî). Em her gav di nav rûniştinê de ne, ne wusa?
  2. Demjimêra ku pêşî li êrîşên dubarekirinê bigire dibe qadek cihê, a la nonce. Ew tê kontrol kirin, lê bandorê li tiştek din nake. Bes û uint32 - heke xwêya me bi kêmanî her nîv rojê carekê were guheztin, em dikarin 16 bit ji bitsên rêza nizm ên beşek jimare ya dema niha veqetînin, yên mayî - ji bo perçeyek saniyeyê (wek niha).
  3. Rakirin msg_id bi tevahî - ji hêla veqetandina daxwazên li ser piştan ve, yekem, nasnameya xerîdar heye, û ya duyemîn jî, nasnameya danişînê, wan bi hev ve girêdide. Li gorî vê yekê, tenê yek tişt wekî nasnameya daxwaznameyê bes e seq_no.

Ev di heman demê de ne vebijarka herî serketî ye; bêkêmasîyek bêkêmasî dikare wekî nasnameyek bixebite - ev jixwe di API-a-asta bilind de dema ku peyamek dişîne, bi awayê, tête kirin. Dê çêtir be ku mîmarî bi tevahî ji nisbî berbi mutleq ji nû ve were çêkirin, lê ev mijarek ji bo beşek din e, ne ev post.

API?

Ta-daam! Ji ber vê yekê, ku em di rêyek tije êş û kêşan de têkoşîn kirin, di dawiyê de me karî her daxwazek ji serverê re bişînin û her bersivên wan werbigirin, û her weha nûvekirinên ji serverê werbigirin (ne bersiva daxwazek, lê ew bixwe ji me re dişîne, mîna PUSH, ger kesek bi vî rengî zelaltir be).

Внимание, сейчас будет единственный в статье пример на Perl! (для тех, кто не знаком с синтаксисом, первый аргумент bless — структура данных объекта, второй — его класс):

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

Erê, ne bi mebesta spoiler - heke we hîn nexwendiye, pêşde biçin û wiya bikin!

Oh, wai~~... ev çi xuya dike? Tiştek pir naskirî ... dibe ku ev strukturek daneyê ya Web API-ya tîpîk a JSON be, ji bilî ku çîn jî bi tiştan ve girêdayî ne?..

Ji ber vê yekê ev çawa diqewime... Ev çi ye hevalno?.. Ewqas hewldan - û em sekinîn ku li cihê bernamenûsên Webê bêhna xwe bidin. tenê dest pê dike?..Ma tenê JSON li ser HTTPS ne hêsan e?! Me di berdêl de çi bi dest xist? Ma hewldan hêja bû?

Ka em binirxînin TL+MTProto çi daye me û çi alternatîv mimkûn in. Welê, HTTP, ku balê dikişîne ser modela daxwaz-bersiv, guncanek xirab e, lê bi kêmanî tiştek li ser TLS?

serialization Compact. Bi dîtina vê avahiya daneyê, mîna JSON, tê bîra min ku guhertoyên wê yên binary hene. Ka em MsgPack wekî têra xwe berfirehkirî nîşan bidin, lê mînakek CBOR heye - bi awayê, standardek ku di RFC 7049. Ji ber vê yekê ku ew diyar dike, balkêş e tags, wekî mekanîzmaya berfirehbûnê, û di nav de jixwe standard kirin berdeste:

  • 25 + 256 - guheztina rêzikên dubare bi referansa jimara rêzê, rêbazek wusa erzan a berhevkirinê
  • 26 - Tişta Perl ya serialkirî ya bi navê polê û argumanên çêker
  • 27 - Tiştek serbixwe-zimanî ya serialkirî bi navê tîp û argumanên çêker

Welê, min hewl da ku heman daneyan di TL û CBOR-ê de bi xêz û pakkirina tiştan çalak bikim serialîze bikim. Encam dest pê kir ku di berjewendiya CBOR de li cîhek megabyte cûda bibe:

cborlen=1039673 tl_len=1095092

Û vî awayî, encamê: Formatên bi giranî sadetir hene ku ne mijara têkçûna hevdemkirinê an nasnameyek nenas in, bi karbidestiya berawirdî.

Sazkirina pêwendiya bilez. Ev tê vê wateyê ku RTT sifir piştî vegirêdanê (gava ku miftek berê carekê hatî çêkirin) - ji yekem peyama MTProto ve tête sepandin, lê digel hin veqetandî - heman xwê lêxe, danişîn ne zirav e, hwd. TLS li şûna me çi pêşkêşî me dike? Gotin li ser mijarê:

Dema ku PFS di TLS de bikar bînin, bilêtên danişîna TLS (RFC 5077) ji nû ve danişîna şîfrekirî bêyî danûstandina bişkojan û bêyî hilanîna agahdariya sereke li ser serverê. Dema ku pêwendiya yekem vedike û bişkojan diafirîne, server rewşa pêwendiyê şîfre dike û ji xerîdar re dişîne (di forma bilêtek danişînê de). Li gorî vê yekê, gava ku girêdan ji nû ve dest pê dike, xerîdar bilêtek danişînê, tevî mifteya danişînê, vedigere serverê. Bilêt bixwe bi mifteyek demkî (mifteya bilêta danişînê) tê şîfrekirin, ku li ser serverê tê hilanîn û divê di nav hemî pêşkêşkerên pêşîn ên ku SSL-ê di çareseriyên komkirî de hilberandin de were belav kirin.[10]. Ji ber vê yekê, danasîna bilêtek danişînê dibe ku PFS binpê bike heke bişkokên serverê yên demkî werin tawîz kirin, mînakî, dema ku ew ji bo demek dirêj têne hilanîn (OpenSSL, nginx, Apache wan bi xwerû ji bo tevahiya dirêjahiya bernameyê hilîne; Malperên populer bikar tînin kilît ji bo çend saetan, heta bi rojan).

Li vir RTT ne sifir e, hûn hewce ne ku bi kêmanî ClientHello û ServerHello biguhezînin, piştî ku xerîdar dikare digel Finished daneyan bişîne. Lê li vir divê em ji bîr mekin ku ne Weba me, digel komek girêdanên wê yên nû vekiriye, lê peyamberek me heye, ku pêwendiya wî bi gelemperî yek û kêm-zêde demdirêj e, daxwaziyên nisbeten kurt ji rûpelên malperê re - her tişt piralî ye. navxweyî. Ango, heke em rastî beşa metroyê ya bi rastî xirab nehatibin, pir tê pejirandin.

Что-то еще забыл? Di şîroveyan de binivîsin.

Ez bêtir ji te hez dikim!

Во второй части этой серии постов мы рассмотрим более не технические, а организационные моменты — подходы, идеология, интерфейс, отношение к пользователям и т.д. Опираясь, впрочем, на ту техническую информацию, что была изложена здесь.

Beşa sêyemîn dê analîzkirina pêkhateya teknîkî / ezmûna pêşkeftinê bidomîne. Hûn ê bi taybetî fêr bibin:

  • bi cûrbecûr cureyên TLyê pandemonyumê berdewam dike
  • tiştên nenas di derbarê kanal û superkoman de
  • çima diyalog ji rêzê xerabtir in
  • di derbarê navnîşana peyama mutleq û têkildar de
  • ferqa wêne û wêneyê çi ye
  • çawa emoji bi nivîsa îtalîk re mudaxele dike

û kincên din! Li bendê bin!

Source: www.habr.com

Add a comment