Ukosoaji wa itifaki na njia za shirika za Telegraph. Sehemu ya 1, kiufundi: uzoefu wa kuandika mteja kutoka mwanzo - TL, MT

Hivi majuzi, machapisho kuhusu jinsi Telegram ni nzuri, jinsi ndugu wa Durov walivyo na kipaji na uzoefu katika kujenga mifumo ya mtandao, nk yameanza kuonekana mara nyingi zaidi kwa Habre. Wakati huo huo, watu wachache sana wamejiingiza kwenye kifaa cha kiufundi - zaidi, hutumia API ya Bot rahisi (na tofauti kabisa na MTProto) kulingana na JSON, na kawaida hukubali tu. juu ya imani sifa zote na PR zinazomzunguka mjumbe. Karibu mwaka mmoja na nusu uliopita, mwenzangu katika NGO ya Eshelon Vasily (kwa bahati mbaya, akaunti yake juu ya HabrΓ© ilifutwa pamoja na rasimu) alianza kuandika mteja wake wa Telegram tangu mwanzo huko Perl, na baadaye mwandishi wa mistari hii akajiunga. Kwa nini Perl, wengine watauliza mara moja? Kwa sababu miradi kama hii tayari ipo katika lugha nyingine.Kwa kweli, hii sio maana, kunaweza kuwa na lugha nyingine yoyote ambapo hakuna lugha yoyote. maktaba iliyotengenezwa tayari, na ipasavyo mwandishi lazima aende njia yote kutoka mwanzo. Kwa kuongezea, kriptografia ni suala la kuaminiwa, lakini thibitisha. Ukiwa na bidhaa inayolenga usalama, huwezi kutegemea tu maktaba iliyotengenezwa tayari kutoka kwa mtengenezaji na kuiamini kwa upofu (hata hivyo, hii ni mada ya sehemu ya pili). Kwa sasa, maktaba inafanya kazi vizuri katika kiwango cha "wastani" (inakuruhusu kufanya maombi yoyote ya API).

Hata hivyo, hakutakuwa na kriptografia au hesabu nyingi katika mfululizo huu wa machapisho. Lakini kutakuwa na maelezo mengine mengi ya kiufundi na viboko vya usanifu (pia ni muhimu kwa wale ambao hawataandika kutoka mwanzo, lakini watatumia maktaba kwa lugha yoyote). Kwa hivyo, lengo kuu lilikuwa kujaribu kutekeleza mteja kutoka mwanzo kulingana na nyaraka rasmi. Hiyo ni, hebu tufikirie kwamba msimbo wa chanzo wa wateja rasmi umefungwa (tena, katika sehemu ya pili tutashughulikia kwa undani zaidi mada ya ukweli kwamba hii ni kweli. hutokea kwa hivyo), lakini, kama katika siku za zamani, kwa mfano, kuna kiwango kama RFC - inawezekana kuandika mteja kulingana na vipimo peke yake, "bila kuangalia" msimbo wa chanzo, iwe rasmi (Desktop ya Telegraph, simu), au Telethon isiyo rasmi?

Jedwali la Yaliyomo:

Nyaraka... ipo, sivyo? Ni ukweli?..

Vipande vya maelezo ya nakala hii vilianza kukusanywa msimu wa joto uliopita. Wakati huu wote kwenye tovuti rasmi https://core.telegram.org Nyaraka zilikuwa kama za Tabaka la 23, i.e. kukwama mahali fulani mnamo 2014 (kumbuka, hakukuwa na chaneli wakati huo?). Kwa kweli, kwa nadharia, hii inapaswa kuturuhusu kutekeleza mteja na utendaji wakati huo mnamo 2014. Lakini hata katika hali hii, nyaraka zilikuwa, kwanza, hazijakamilika, na pili, katika maeneo zilijipinga yenyewe. Zaidi ya mwezi mmoja uliopita, mnamo Septemba 2019, ilikuwa ajali Iligunduliwa kuwa kulikuwa na sasisho kubwa la nyaraka kwenye tovuti, kwa Safu ya hivi karibuni ya 105, na kumbuka kwamba sasa kila kitu kinahitaji kusoma tena. Kwa kweli, makala nyingi zilirekebishwa, lakini nyingi hazijabadilika. Kwa hiyo, wakati wa kusoma ukosoaji hapa chini kuhusu nyaraka, unapaswa kukumbuka kwamba baadhi ya mambo haya hayafai tena, lakini baadhi bado ni kabisa. Baada ya yote, miaka 5 katika ulimwengu wa kisasa sio muda mrefu tu, lakini sana mengi. Tangu nyakati hizo (hasa ikiwa hauzingatii tovuti za geochat zilizotupwa na zilizofufuliwa tangu wakati huo), idadi ya mbinu za API katika mpango huo imeongezeka kutoka mia moja hadi zaidi ya mia mbili na hamsini!

Wapi kuanza kama mwandishi mchanga?

Haijalishi ikiwa unaandika kutoka mwanzo au unatumia, kwa mfano, maktaba zilizotengenezwa tayari kama Telethon kwa Python au Madeline kwa PHP, kwa hali yoyote, utahitaji kwanza sajili ombi lako - pata vigezo api_id ΠΈ api_hash (wale ambao wamefanya kazi na API ya VKontakte wanaelewa mara moja) ambayo seva itatambua programu. Hii lazima ifanye kwa sababu za kisheria, lakini tutazungumza zaidi kuhusu kwa nini waandishi wa maktaba hawawezi kuichapisha katika sehemu ya pili. Unaweza kuridhika na maadili ya mtihani, ingawa ni mdogo sana - ukweli ni kwamba sasa unaweza kujiandikisha kimoja tu app, kwa hivyo usikimbilie kuingia ndani.

Sasa, kutoka kwa mtazamo wa kiufundi, tunapaswa kupendezwa na ukweli kwamba baada ya usajili tunapaswa kupokea arifa kutoka kwa Telegram kuhusu sasisho kwa nyaraka, itifaki, nk. Hiyo ni, mtu anaweza kudhani kuwa tovuti iliyo na docks iliachwa tu na kuendelea kufanya kazi hasa na wale ambao walianza kufanya wateja, kwa sababu. ni rahisi zaidi. Lakini hapana, hakuna kitu kama hicho kilizingatiwa, hakuna habari iliyokuja.

Na ikiwa unaandika kutoka mwanzo, basi kutumia vigezo vilivyopatikana kwa kweli bado ni mbali. Ingawa https://core.telegram.org/ na kuyazungumza katika Anza kwanza kabisa, kwa kweli, itabidi kwanza utekeleze Itifaki ya MTProto - lakini ikiwa unaamini mpangilio kulingana na mfano wa OSI mwisho wa ukurasa kwa maelezo ya jumla ya itifaki, basi ni bure kabisa.

Kwa kweli, kabla na baada ya MTProto, kwa viwango kadhaa mara moja (kama wanamtandao wa kigeni wanaofanya kazi kwenye kernel ya OS wanasema, ukiukaji wa safu), mada kubwa, chungu na ya kutisha itawazuia ...

Usanifu wa binary: TL (Lugha ya Aina) na mpango wake, na tabaka, na maneno mengine mengi ya kutisha.

Mada hii, kwa kweli, ni ufunguo wa matatizo ya Telegram. Na kutakuwa na maneno mengi ya kutisha ikiwa utajaribu kuzama ndani yake.

Kwa hiyo, hapa ni mchoro. Neno hili likija akilini mwako, sema, JSON Schema, Ulifikiri kwa usahihi. Lengo ni lile lile: baadhi ya lugha kuelezea seti inayowezekana ya data iliyopitishwa. Hapa ndipo kufanana kunakoishia. Ikiwa kutoka kwa ukurasa Itifaki ya MTProto, au kutoka kwa mti wa chanzo wa mteja rasmi, tutajaribu kufungua schema fulani, tutaona kitu kama:

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;

Mtu akiona hii kwa mara ya kwanza ataweza kutambua sehemu tu ya kile kilichoandikwa - vizuri, hizi ni muundo (ingawa jina liko wapi, kushoto au kulia?), Kuna uwanja ndani yao, baada ya hapo aina hufuata baada ya koloni... pengine. Hapa kwenye mabano ya pembe labda kuna templeti kama kwenye C++ (kwa kweli, sivyo) Na alama zingine zote zinamaanisha nini, alama za kuuliza, alama za mshangao, asilimia, alama za hashi (na ni wazi zinamaanisha vitu tofauti katika sehemu tofauti), wakati mwingine zipo na wakati mwingine sio, nambari za hexadecimal - na muhimu zaidi, jinsi ya kupata kutoka kwa hii. sahihi (ambayo haitakataliwa na seva) mkondo wa kawaida? Itabidi kusoma nyaraka (ndio, kuna viungo vya schema katika toleo la JSON karibu - lakini hiyo haifanyi iwe wazi zaidi).

Fungua ukurasa Utayarishaji wa Data binary na kupiga mbizi katika ulimwengu wa kichawi wa uyoga na hisabati tofauti, kitu sawa na matan katika mwaka wa 4. Alfabeti, aina, thamani, kiunganishi, kiunganishi kinachofanya kazi, umbo la kawaida, aina ya mchanganyiko, aina ya polimorphic... na hayo yote ni ukurasa wa kwanza tu! Inayofuata inakungoja Lugha ya TL, ambayo, ingawa tayari ina mfano wa ombi na jibu dogo, haitoi jibu hata kidogo kwa kesi za kawaida zaidi, ambayo inamaanisha kuwa utalazimika kupitia urejeshaji wa hesabu uliotafsiriwa kutoka kwa Kirusi hadi Kiingereza kwenye zingine nane zilizopachikwa. kurasa!

Wasomaji wanaofahamu lugha za kazi na uelekezaji wa aina otomatiki, kwa kweli, wataona lugha ya maelezo katika lugha hii, hata kutoka kwa mfano, kama kawaida zaidi, na wanaweza kusema kwamba hii sio mbaya kwa kanuni. Mapingamizi kwa hili ni:

  • ndio, lengo inaonekana nzuri, lakini ole, yeye haijafikiwa
  • Elimu katika vyuo vikuu vya Kirusi inatofautiana hata kati ya utaalam wa IT - sio kila mtu amechukua kozi inayolingana
  • Hatimaye, kama tutakavyoona, katika mazoezi ni haihitajiki, kwani ni sehemu ndogo tu ya hata TL iliyoelezwa ndiyo inayotumika

Kama ilivyosemwa LeoNerd kwenye kituo #perl katika mtandao wa FreeNode IRC, ambao walijaribu kutekeleza lango kutoka kwa Telegraph hadi Matrix (tafsiri ya nukuu sio sahihi kutoka kwa kumbukumbu):

Inahisi kama mtu alitambulishwa kuandika nadharia kwa mara ya kwanza, akasisimka, na kuanza kujaribu kuichezea, bila kujali ikiwa inahitajika katika mazoezi.

Jionee mwenyewe, ikiwa hitaji la aina tupu (int, ndefu, n.k.) kama jambo la msingi halizushi maswali - hatimaye lazima litekelezwe kwa mikono - kwa mfano, wacha tujaribu kupata kutoka kwao. vekta. Hiyo ni kweli, safu, ikiwa unaita vitu vinavyotokana na majina yao sahihi.

Lakini kabla

Maelezo mafupi ya kikundi kidogo cha sintaksia ya TL kwa wale ambao hawasomi hati rasmi

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;

Ufafanuzi huanza kila wakati mjenzi, baada ya hapo kwa hiari (katika mazoezi - daima) kupitia ishara # lazima CRC32 kutoka kwa safu ya maelezo ya kawaida ya aina hii. Inayofuata inakuja maelezo ya sehemu; ikiwa zipo, aina inaweza kuwa tupu. Hii yote inaisha na ishara sawa, jina la aina ambayo mjenzi huyu - ambayo ni, kwa kweli, aina ndogo - ni ya. Mwanamume aliye upande wa kulia wa ishara sawa ni polymorphic - yaani, aina kadhaa maalum zinaweza kuendana nayo.

Ikiwa ufafanuzi hutokea baada ya mstari ---functions---, basi syntax itabaki sawa, lakini maana itakuwa tofauti: mjenzi atakuwa jina la kazi ya RPC, shamba zitakuwa vigezo (vizuri, yaani, itabaki sawa na muundo uliopewa, kama ilivyoelezwa hapa chini. , hii itakuwa tu maana iliyopewa), na "aina ya polymorphic" - aina ya matokeo yaliyorejeshwa. Kweli, bado itabaki polymorphic - imefafanuliwa tu katika sehemu hiyo ---types---, lakini mjenzi huyu "hatazingatiwa". Kupakia zaidi aina za kazi zinazoitwa kwa hoja zao, i.e. Kwa sababu fulani, utendakazi kadhaa zilizo na jina moja lakini saini tofauti, kama katika C++, hazijatolewa katika TL.

Kwa nini "mjenzi" na "polymorphic" ikiwa sio OOP? Kweli, kwa kweli, itakuwa rahisi kwa mtu kufikiria juu ya hili kwa maneno ya OOP - aina ya polymorphic kama darasa la kufikirika, na wajenzi ni darasa la kizazi chake cha moja kwa moja, na final katika istilahi za idadi ya lugha. Kwa kweli, hapa tu mfanano na njia halisi za wajenzi zilizojaa kupita kiasi katika lugha za programu za OO. Kwa kuwa hapa kuna muundo wa data tu, hakuna njia (ingawa maelezo ya kazi na njia zaidi yana uwezo wa kuunda machafuko kichwani kwamba yapo, lakini hiyo ni jambo tofauti) - unaweza kufikiria mjenzi kama dhamana kutoka ambayo inajengwa chapa unaposoma mkondo wa baiti.

Je, hii hutokeaje? Deserializer, ambayo daima inasoma byte 4, huona thamani 0xcrc32 - na anaelewa nini kitatokea baadaye field1 na aina int, i.e. inasoma ka 4 haswa, kwenye uwanja huu ulio na aina PolymorType soma. Inaona 0x2crc32 na kuelewa kwamba kuna nyanja mbili zaidi, kwanza long, ambayo inamaanisha tunasoma ka 8. Na kisha tena aina tata, ambayo ni deserialized kwa njia sawa. Kwa mfano, Type3 inaweza kutangazwa katika mzunguko mara tu wajenzi wawili, mtawaliwa, basi lazima wakutane 0x12abcd34, baada ya hapo unahitaji kusoma ka 4 zaidi intAu 0x6789cdef, baada ya hapo hakutakuwa na chochote. Kitu kingine chochote - unahitaji kutupa ubaguzi. Walakini, baada ya hii tunarudi kusoma ka 4 int shamba field_c Π² constructorTwo na kwa hayo tunamaliza kusoma yetu PolymorType.

Hatimaye, ikiwa utakamatwa 0xdeadcrc kwa constructorThree, basi kila kitu kinakuwa ngumu zaidi. Uwanja wetu wa kwanza ni bit_flags_of_what_really_present na aina # - kwa kweli, hii ni pak kwa aina nat, maana yake "nambari asilia". Hiyo ni, kwa kweli, int isiyosajiliwa ni, kwa njia, kesi pekee wakati nambari zisizosajiliwa zinatokea katika nyaya halisi. Kwa hivyo, inayofuata ni ujenzi ulio na alama ya swali, ikimaanisha kuwa uwanja huu - utakuwepo kwenye waya tu ikiwa sehemu inayolingana imewekwa kwenye uwanja unaorejelewa (takriban kama mwendeshaji wa ternary). Kwa hiyo, hebu tuchukue kwamba kidogo hii iliwekwa, ambayo ina maana kwamba zaidi tunahitaji kusoma shamba kama Type, ambayo kwa mfano wetu ina wajenzi 2. Moja ni tupu (ina kitambulisho tu), nyingine ina uwanja ids na aina ids:Vector<long>.

Unaweza kufikiria kuwa violezo na jenetiki zote ziko kwenye faida au Java. Lakini hapana. Karibu. Hii pekee kesi ya kutumia mabano ya pembe katika mizunguko halisi, na inatumika TU kwa Vekta. Katika mkondo wa kawaida, hizi zitakuwa 4 CRC32 byte kwa aina ya Vector yenyewe, daima sawa, kisha 4 byte - idadi ya vipengele vya safu, na kisha vipengele hivi wenyewe.

Ongeza kwa hili ukweli kwamba ujumuishaji kila wakati hufanyika kwa maneno ya ka 4, aina zote ni nyingi - aina zilizojengwa pia zinaelezewa. bytes ΠΈ string na usanifu wa mwongozo wa urefu na usawa huu kwa 4 - vizuri, inaonekana kuwa ya kawaida na hata yenye ufanisi? Ijapokuwa TL inadaiwa kuwa ni usanifu wa mfumo wa binary, kuzimu pamoja nao, pamoja na upanuzi wa karibu kitu chochote, hata maadili ya Boolean na kamba za herufi moja hadi baiti 4, je JSON bado itakuwa nene zaidi? Angalia, hata sehemu zisizo za lazima zinaweza kurukwa kwa bendera kidogo, kila kitu ni kizuri kabisa, na kinaweza kupanuka kwa siku zijazo, kwa nini usiongeze sehemu mpya za hiari kwa mjenzi baadaye?..

Lakini hapana, ikiwa husoma maelezo yangu mafupi, lakini nyaraka kamili, na fikiria juu ya utekelezaji. Kwanza, CRC32 ya mjenzi huhesabiwa kulingana na mstari wa kawaida wa maelezo ya maandishi ya mpango (ondoa nafasi nyeupe ya ziada, nk) - kwa hivyo ikiwa uwanja mpya umeongezwa, mstari wa maelezo ya aina utabadilika, na kwa hivyo CRC32 yake na , kwa hivyo, usanifu. Na mteja wa zamani angefanya nini ikiwa angepokea sehemu iliyo na bendera mpya, na hajui la kufanya nazo baadaye? ..

Pili, tukumbuke CRC32, ambayo inatumika hapa kimsingi kama kazi za hashi ili kubaini kipekee ni aina gani inayofanywa (de) serialized. Hapa tunakabiliwa na shida ya migongano - na hapana, uwezekano sio mmoja kati ya 232, lakini ni mkubwa zaidi. Nani alikumbuka kuwa CRC32 imeundwa kugundua (na kusahihisha) makosa katika njia ya mawasiliano, na ipasavyo kuboresha sifa hizi kwa madhara ya wengine? Kwa mfano, haijali kuhusu kupanga upya baiti: ukihesabu CRC32 kutoka kwa mistari miwili, kwa pili unabadilisha baiti 4 za kwanza na ka 4 zinazofuata - itakuwa sawa. Wakati ingizo letu ni mifuatano ya maandishi kutoka kwa alfabeti ya Kilatini (na alama za uakifishaji kidogo), na majina haya si ya nasibu haswa, uwezekano wa upangaji upya kama huo huongezeka sana.

Kwa njia, ni nani aliyeangalia ni nini hapo? kweli CRC32? Mojawapo ya misimbo ya awali ya chanzo (hata kabla ya Waltman) ilikuwa na kipengele cha kukokotoa cha heshi ambacho kilizidisha kila herufi kwa nambari 239, inayopendwa sana na watu hawa, ha ha!

Mwishowe, sawa, tuligundua kuwa wajenzi walio na aina ya uwanja Vector<int> ΠΈ Vector<PolymorType> itakuwa na CRC32 tofauti. Vipi kuhusu utendaji wa mtandaoni? Na kutoka kwa mtazamo wa kinadharia, hii inakuwa sehemu ya aina? Wacha tuseme tunapitisha safu ya nambari elfu kumi, vizuri na Vector<int> kila kitu kiko wazi, urefu na ka nyingine 40000. Nini kama hii Vector<Type2>, ambayo ina uga mmoja tu int na iko peke yake katika aina - tunahitaji kurudia 10000xabcdef0 mara 34 na kisha ka 4 int, au lugha ina uwezo wa KUTUTEGEMEA kutoka kwa mjenzi fixedVec na badala ya ka 80000, uhamishe tena 40000 tu?

Hili sio swali la kinadharia hata kidogo - fikiria unapokea orodha ya watumiaji wa kikundi, ambao kila mmoja ana kitambulisho, jina la kwanza, jina la mwisho - tofauti katika kiasi cha data iliyohamishwa kwenye muunganisho wa simu inaweza kuwa muhimu. Ni hasa ufanisi wa usanifu wa Telegram ambao unatangazwa kwetu.

Hivyo…

Vector, ambayo haijawahi kutolewa

Ukijaribu kupitia kurasa za maelezo ya viunganishi na kadhalika, utaona kwamba vekta (na hata matrix) inajaribu rasmi kutolewa kupitia nakala za karatasi kadhaa. Lakini mwisho wao husahau, hatua ya mwisho inaruka, na ufafanuzi wa vector hutolewa tu, ambayo bado haijafungwa kwa aina. Kuna nini? Katika lugha programu, hasa kazi, ni kawaida kabisa kuelezea muundo kwa kurudia - mkusanyaji na tathmini yake ya uvivu ataelewa na kufanya kila kitu yenyewe. Kwa lugha utayarishaji wa data kinachohitajika ni UFANISI: inatosha kuelezea kwa urahisi orodha, i.e. muundo wa vipengele viwili - ya kwanza ni kipengele cha data, ya pili ni muundo huo yenyewe au nafasi tupu kwa mkia (pakiti (cons) katika Lisp). Lakini hii ni wazi itahitaji kila mmoja kipengele hutumia baiti 4 za ziada (CRC32 katika kesi katika TL) kuelezea aina yake. Safu pia inaweza kuelezewa kwa urahisi saizi iliyowekwa, lakini katika kesi ya safu ya urefu usiojulikana mapema, tunavunja.

Kwa hivyo, kwa kuwa TL hairuhusu kutoa vekta, ilibidi iongezwe kwa upande. Hatimaye nyaraka zinasema:

Kusawazisha kila mara hutumia kijenzi sawa na β€œvekta” (const 0x1cb5c415 = crc32(β€œvekta t:Aina # [ t ] = Vekta t”) ambayo haitegemei thamani mahususi ya tofauti ya aina t.

Thamani ya kigezo cha hiari cha t haihusiki katika usakinishaji kwa kuwa imetolewa kutoka kwa aina ya matokeo (inajulikana kila wakati kabla ya kufutwa).

Angalia kwa karibu: vector {t:Type} # [ t ] = Vector t - lakini mahali popote Ufafanuzi huu yenyewe hausemi kwamba nambari ya kwanza lazima iwe sawa na urefu wa vector! Na haitoki popote. Hii ni iliyotolewa ambayo inahitaji kuwekwa katika akili na kutekelezwa kwa mikono yako. Mahali pengine, hati hata zinataja kwa uaminifu kuwa aina hiyo sio kweli:

Vector t polymorphic pseudotype ni "aina" ambayo thamani yake ni mlolongo wa maadili ya aina yoyote t, iwe ya sanduku au wazi.

... lakini haizingatii. Wakati wewe, umechoka kupitia kunyoosha kwa hisabati (labda hata unajulikana kwako kutoka kozi ya chuo kikuu), unaamua kukata tamaa na kwa kweli uangalie jinsi ya kufanya kazi nayo kwa vitendo, hisia iliyobaki kichwani mwako ni kwamba hii ni Serious. Hisabati katika msingi, ni wazi zuliwa na Cool People (wataalamu wawili wa hisabati - ACM mshindi), na si tu mtu yeyote. Lengo - kujionyesha - limefikiwa.

Kwa njia, kuhusu idadi. Hebu tuwakumbushe hilo # ni kisawe nat, nambari asilia:

Kuna misemo ya aina (aina-expr) na maneno ya nambari (nat-expr) Walakini, zinafafanuliwa kwa njia ile ile.

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

lakini katika sarufi wanaelezewa kwa njia ile ile, i.e. Tofauti hii lazima ikumbukwe tena na kuwekwa katika utekelezaji kwa mkono.

Kweli, ndio, aina za templeti (vector<int>, vector<User>) kuwa na kitambulisho cha kawaida (#1cb5c415), yaani. ikiwa unajua kuwa simu inatangazwa kama

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

basi hausubiri tena vekta tu, lakini vekta ya watumiaji. Usahihi zaidi, lazima subiri - kwa nambari halisi, kila kitu, ikiwa sio aina tupu, kitakuwa na mjenzi, na kwa njia nzuri katika utekelezaji itakuwa muhimu kuangalia - lakini tulitumwa haswa katika kila sehemu ya vekta hii. aina hiyo? Je, ikiwa ni aina fulani ya PHP, ambayo safu inaweza kuwa na aina tofauti katika vipengele tofauti?

Kwa wakati huu unaanza kufikiria - TL kama hiyo ni muhimu? Labda kwa mkokoteni itawezekana kutumia serializer ya kibinadamu, protobuf sawa ambayo tayari ilikuwepo wakati huo? Hiyo ndiyo ilikuwa nadharia, tuangalie mazoezi.

Utekelezaji uliopo wa TL katika msimbo

TL alizaliwa katika kina cha VKontakte hata kabla ya hafla maarufu na uuzaji wa sehemu ya Durov na (hakika), hata kabla ya maendeleo ya Telegram kuanza. Na katika chanzo wazi msimbo wa chanzo wa utekelezaji wa kwanza unaweza kupata mengi ya magongo funny. Na lugha yenyewe ilitekelezwa huko kikamilifu zaidi kuliko ilivyo sasa katika Telegramu. Kwa mfano, heshi haitumiki kabisa katika mpango (ikimaanisha pseudotype iliyojengewa ndani (kama vekta) yenye tabia potovu). Au

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

lakini hebu tuzingatie, kwa ajili ya utimilifu, kufuatilia, kwa kusema, mageuzi ya Jitu la Mawazo.

#define ZHUKOV_BYTES_HACK

#ifdef ZHUKOV_BYTES_HACK

/* dirty hack for Zhukov request */

Au hii nzuri:

    static const char *reserved_words_polymorhic[] = {

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

      };

Sehemu hii inahusu violezo kama vile:

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

Huu ndio ufafanuzi wa aina ya kiolezo cha hashmap kama vekta ya int - Aina ya jozi. Katika C ++ ingeonekana kitu kama hiki:

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

hivyo, alpha - neno kuu! Lakini tu katika C ++ unaweza kuandika T, lakini unapaswa kuandika alpha, beta ... Lakini si zaidi ya vigezo 8, ndio ambapo fantasy inaisha. Inaonekana kwamba hapo zamani huko St. Petersburg mazungumzo kama haya yalifanyika:

-- Надо ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Π² TL ΡˆΠ°Π±Π»ΠΎΠ½Ρ‹
-- Π‘Π»... Ну ΠΏΡƒΡΡ‚ΡŒ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ Π·ΠΎΠ²ΡƒΡ‚ Π°Π»ΡŒΡ„Π°, Π±Π΅Ρ‚Π°,... КакиС Ρ‚Π°ΠΌ Π΅Ρ‰Ρ‘ Π±ΡƒΠΊΠ²Ρ‹ Π΅ΡΡ‚ΡŒ... О, тэта!
-- Π“Ρ€Π°ΠΌΠΌΠ°Ρ‚ΠΈΠΊΠ°? Ну ΠΏΠΎΡ‚ΠΎΠΌ напишСм

-- Π‘ΠΌΠΎΡ‚Ρ€ΠΈΡ‚Π΅, ΠΊΠ°ΠΊΠΎΠΉ я синтаксис ΠΏΡ€ΠΈΠ΄ΡƒΠΌΠ°Π» для шаблонов ΠΈ Π²Π΅ΠΊΡ‚ΠΎΡ€Π°!
-- Π’Ρ‹ долбанулся, ΠΊΠ°ΠΊ ΠΌΡ‹ это ΠΏΠ°Ρ€ΡΠΈΡ‚ΡŒ Π±ΡƒΠ΄Π΅ΠΌ?
-- Π”Π° Π½Π΅ ссытС, ΠΎΠ½ Ρ‚Π°ΠΌ ΠΎΠ΄ΠΈΠ½ Π² схСмС, Π·Π°Ρ…Π°Ρ€ΠΊΠΎΠ΄ΠΈΡ‚ΡŒ -- ΠΈ ΠΎΠΊ

Lakini hii ilikuwa juu ya utekelezaji wa kwanza uliochapishwa wa TL "kwa ujumla". Wacha tuendelee kuzingatia utekelezaji katika wateja wa Telegraph wenyewe.

Neno kwa Vasily:

Vasily, [09.10.18 17:07] Zaidi ya yote, punda ni moto kwa sababu waliunda rundo la vifupisho, na kisha wakapiga bolt juu yao, na kufunika jenereta ya msimbo kwa mikongojo.
Kwa hivyo, kwanza kutoka dock pilot.jpg
Kisha kutoka kwa kanuni dzhekichan.webp

Bila shaka, kutoka kwa watu wanaofahamu algoriti na hisabati, tunaweza kutarajia kwamba wamesoma Aho, Ullmann, na wanafahamu zana ambazo zimekuwa za kawaida katika tasnia kwa miongo kadhaa ya kuandika vikusanyaji vyao vya DSL, sivyo?..

Na telegram-cli ni Vitaly Valtman, kama inavyoweza kueleweka kutokana na kutokea kwa umbizo la TLO nje ya mipaka yake (cli), mwanachama wa timu - sasa maktaba ya uchanganuzi wa TL imetengwa. tofauti, ni nini hisia yake Kichanganuzi cha TL? ..

16.12 04:18 Vasily: Nafikiri kuna mtu hakujua lex+yacc
16.12 04:18 Vasily: Siwezi kueleza vinginevyo
16.12 04:18 Vasily: vizuri, au walilipwa kwa idadi ya mistari katika VK
16.12 04:19 Vasily: mistari 3k+ n.k.<censored> badala ya mchanganuzi

Labda ubaguzi? Hebu tuone jinsi gani huenda Huyu ndiye mteja RASMI - Eneo-kazi la Telegramu:

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

Mistari 1100+ huko Python, misemo kadhaa ya kawaida + kesi maalum kama vekta, ambayo, kwa kweli, inatangazwa kwenye mpango kama inavyopaswa kuwa kulingana na syntax ya TL, lakini walitegemea syntax hii ili kuichanganua ... Swali linatokea, kwa nini yote yalikuwa muujiza?ΠΈImepangwa zaidi ikiwa hakuna mtu atakayeichanganua kulingana na hati hata hivyo?!

By the way... Unakumbuka tulizungumza kuhusu ukaguzi wa CRC32? Kwa hivyo, katika jenereta ya nambari ya Desktop ya Telegraph kuna orodha ya kando kwa aina hizo ambazo CRC32 iliyohesabiwa. hailingani na ile iliyoonyeshwa kwenye mchoro!

Vasily, [18.12/22 49:XNUMX] na hapa ningefikiria ikiwa TL kama hiyo inahitajika.
ikiwa ningetaka kuvuruga na utekelezaji mbadala, ningeanza kuingiza mapumziko ya mstari, nusu ya vichanganuzi vitavunjika kwa ufafanuzi wa safu nyingi.
tdesktop, hata hivyo, pia

Kumbuka uhakika kuhusu mjengo mmoja, tutarudi kwake baadaye kidogo.

Sawa, telegram-cli sio rasmi, Telegraph Desktop ni rasmi, lakini vipi kuhusu zingine? Ni nani anayejua? kifungu kidogo hapa chini.

Ni maswali gani mengine ambayo ufuataji huibua katika vitendo? Kwa mfano, walifanya mambo mengi, kwa kweli, na uwanja kidogo na uwanja wa masharti:

Vasily: flags.0? true
inamaanisha kuwa uga upo na ni sawa ikiwa bendera imewekwa

Vasily: flags.1? int
inamaanisha kuwa uwanja upo na unahitaji kutengwa

Vasily: Punda, usijali kuhusu kile unachofanya!
Vasily: Kuna kutajwa mahali fulani kwenye hati kwamba kweli ni aina ya urefu wa sifuri, lakini haiwezekani kukusanya chochote kutoka kwa waraka wao.
Vasily: Katika utekelezaji wa chanzo wazi hii sivyo, lakini kuna rundo la mikongojo na msaada.

Vipi kuhusu Telethon? Kuangalia mbele ya mada ya MTProto, mfano - katika nyaraka kuna vipande vile, lakini ishara % inaelezewa tu kuwa "inayolingana na aina isiyo wazi", i.e. katika mifano hapa chini kuna kosa au kitu kisicho na kumbukumbu:

Vasily, [22.06.18 18:38] Katika sehemu moja:

msg_container#73f1f8dc messages:vector message = MessageContainer;

Katika tofauti:

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

Na hizi ni tofauti mbili kubwa, katika maisha halisi aina fulani ya vector ya uchi inakuja

Sijaona ufafanuzi wa vekta wazi na sijapata moja

Uchambuzi umeandikwa kwa mkono katika telethon

Katika mchoro wake ufafanuzi umetolewa maoni msg_container

Tena, swali linabaki juu ya %. Haijaelezewa.

Vadim Goncharov, [22.06.18 19:22] na katika tdesktop?

Vasily, [22.06.18 19:23] Lakini kichanganuzi chao cha TL kwenye injini za kawaida hakitakula hii pia.

// parsed manually

TL ni muhtasari mzuri, hakuna anayeitekeleza kabisa

Na % haiko katika toleo lao la mpango huo

Lakini hapa hati zinajipinga yenyewe, kwa hivyo idk

Ilipatikana katika sarufi, wangeweza tu kusahau kuelezea semantiki

Uliona hati kwenye TL, huwezi kuigundua bila nusu lita

"Kweli, wacha tuseme," msomaji mwingine atasema, "unakosoa kitu, kwa hivyo nionyeshe jinsi kinapaswa kufanywa."

Vasily anajibu: "Kuhusu mshiriki, napenda vitu kama hivyo

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

kwa namna fulani ni bora kuliko

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

au

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

hii ni lexer YOTE:

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

hizo. rahisi zaidi ni kuiweka kwa upole."

Kwa ujumla, kama matokeo, kichanganuzi na jenereta ya msimbo kwa kitengo kidogo cha TL kinachotumika hulingana na takriban mistari 100 ya sarufi na ~ 300 mistari ya jenereta (kuhesabu yote. printNambari iliyotengenezwa), pamoja na vifungu vya habari vya aina kwa uchunguzi katika kila darasa. Kila aina ya polimofi hubadilika kuwa darasa tupu la msingi la kufikirika, na wajenzi hurithi kutoka kwayo na wana mbinu za usanifu na uondoaji.

Ukosefu wa aina katika lugha ya aina

Kuandika kwa nguvu ni jambo zuri, sivyo? Hapana, hii sio holivar (ingawa napendelea lugha zinazobadilika), lakini maandishi ndani ya mfumo wa TL. Kwa msingi wake, lugha inapaswa kutoa kila aina ya ukaguzi kwa ajili yetu. Kweli, sawa, labda sio yeye mwenyewe, lakini utekelezaji, lakini anapaswa kuwaelezea angalau. Na tunataka fursa za aina gani?

Kwanza kabisa, vikwazo. Hapa tunaona katika nyaraka za kupakia faili:

Yaliyomo kwenye jozi ya faili kisha hugawanywa katika sehemu. Sehemu zote lazima ziwe na saizi sawa ( saizi_ya_sehemu ) na masharti yafuatayo lazima yatimizwe:

  • part_size % 1024 = 0 (inagawanywa na 1KB)
  • 524288 % part_size = 0 (512 KB lazima igawanywe kwa usawa kwa part_size)

Sehemu ya mwisho si lazima kukidhi masharti haya, mradi ukubwa wake ni chini ya part_size.

Kila sehemu inapaswa kuwa na nambari ya mlolongo, faili_sehemu, yenye thamani kuanzia 0 hadi 2,999.

Baada ya faili kugawanywa, unahitaji kuchagua njia ya kuihifadhi kwenye seva. Tumia upload.saveBigFilePart ikiwa saizi kamili ya faili ni zaidi ya 10 MB na upload.saveFilePart kwa faili ndogo.
[…] mojawapo ya hitilafu zifuatazo za uingizaji wa data zinaweza kurejeshwa:

  • FILE_PARTS_INVALID - Idadi batili ya sehemu. Thamani haiko kati 1..3000

Je, yoyote kati ya haya kwenye mchoro? Hii inaweza kuelezewa kwa njia fulani kwa kutumia TL? Hapana. Lakini samahani, hata Turbo Pascal wa babu aliweza kuelezea aina zilizoainishwa safu. Na alijua jambo moja zaidi, ambalo sasa linajulikana zaidi kama enum - aina inayojumuisha hesabu ya nambari maalum (ndogo) ya maadili. Katika lugha kama C - nambari, kumbuka kuwa hadi sasa tumezungumza tu juu ya aina nambari. Lakini pia kuna safu, masharti ... kwa mfano, itakuwa nzuri kuelezea kwamba kamba hii inaweza tu kuwa na nambari ya simu, sawa?

Hakuna kati ya hii iliyo kwenye TL. Lakini kuna, kwa mfano, katika JSON Schema. Na ikiwa mtu mwingine anaweza kubishana juu ya mgawanyiko wa 512 KB, kwamba hii bado inahitaji kuangaliwa kwa nambari, basi hakikisha kuwa mteja kwa urahisi. kutoweza tuma nambari nje ya anuwai 1..3000 (na kosa linalolingana lisingeweza kutokea) ingewezekana, sawa?..

Kwa njia, kuhusu makosa na maadili ya kurudi. Hata wale ambao wamefanya kazi na TL walitia ukungu macho - hatukugundua mara moja kila moja chaguo la kukokotoa katika TL linaweza kurudisha sio tu aina iliyoelezwa ya kurudi, lakini pia kosa. Lakini hii haiwezi kuamuliwa kwa njia yoyote kwa kutumia TL yenyewe. Kwa kweli, tayari iko wazi na hakuna haja ya kitu chochote katika mazoezi (ingawa kwa kweli, RPC inaweza kufanywa kwa njia tofauti, tutarudi kwa hii baadaye) - lakini vipi kuhusu Usafi wa dhana za Hisabati za Aina za Kikemikali. kutoka kwa ulimwengu wa mbinguni? .. Nilichukua tug - kwa hivyo ifanane nayo.

Na hatimaye, vipi kuhusu usomaji? Kweli, hapo, kwa ujumla, ningependa maelezo iko sawa kwenye schema (kwenye schema ya JSON, tena, iko), lakini ikiwa tayari una shida nayo, basi vipi kuhusu upande wa vitendo - angalau ukiangalia tofauti wakati wa sasisho? Jionee mwenyewe kwenye mifano halisi:

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

au

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

Inategemea kila mtu, lakini GitHub, kwa mfano, inakataa kuonyesha mabadiliko ndani ya mistari hiyo mirefu. Mchezo "kupata tofauti 10", na kile ubongo unaona mara moja ni kwamba mwanzo na mwisho katika mifano yote miwili ni sawa, unahitaji kusoma kwa bidii mahali fulani katikati ... Kwa maoni yangu, hii sio tu katika nadharia, lakini kwa macho tu mchafu na mzembe.

Kwa njia, kuhusu usafi wa nadharia. Kwa nini tunahitaji mashamba kidogo? Je, haionekani kuwa wao harufu mbaya kutoka kwa mtazamo wa nadharia ya aina? Maelezo yanaweza kuonekana katika matoleo ya awali ya mchoro. Mara ya kwanza, ndiyo, ndivyo ilivyokuwa, kwa kila kupiga chafya aina mpya iliundwa. Kanuni hizi bado zipo katika fomu hii, kwa mfano:

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;

Lakini sasa fikiria, ikiwa una mashamba 5 ya hiari katika muundo wako, basi utahitaji aina 32 kwa chaguo zote zinazowezekana. Mlipuko wa pamoja. Kwa hivyo, usafi wa kioo wa nadharia ya TL kwa mara nyingine tena ulisambaratika dhidi ya punda-chuma wa ukweli mkali wa usanifu.

Kwa kuongezea, katika sehemu zingine watu hawa wenyewe wanakiuka typolojia yao wenyewe. Kwa mfano, katika MTProto (sura inayofuata) majibu yanaweza kubanwa na Gzip, kila kitu ni sawa - isipokuwa kwamba tabaka na mzunguko zimekiukwa. Kwa mara nyingine tena, haikuwa RpcResult yenyewe ambayo ilivunwa, lakini yaliyomo. Kweli, kwanini nifanye hivi? .. Ilinibidi kukata kwenye mkongojo ili ukandamizaji ufanye kazi popote.

Au mfano mwingine, tuligundua hitilafu mara moja - ilitumwa InputPeerUser badala ya InputUser. Au kinyume chake. Lakini ilifanya kazi! Hiyo ni, seva haikujali aina. Hii inawezaje kuwa? Jibu linaweza kutolewa kwetu na vipande vya nambari kutoka telegram-cli:

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

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

Kwa maneno mengine, hapa ndipo usanifu unafanywa KWA MKONO, si kanuni zinazozalishwa! Labda seva inatekelezwa kwa njia sawa? .. Kimsingi, hii itafanya kazi ikiwa imefanywa mara moja, lakini inawezaje kuungwa mkono baadaye wakati wa sasisho? Je, hii ndiyo sababu mpango huo uligunduliwa? Na hapa tunaendelea na swali linalofuata.

Inatayarisha. Tabaka

Kwa nini matoleo ya mpangilio yanaitwa tabaka yanaweza kukisiwa tu kulingana na historia ya michoro iliyochapishwa. Inavyoonekana, mwanzoni waandishi walidhani kwamba mambo ya msingi yanaweza kufanywa kwa kutumia mpango huo ambao haujabadilika, na pale tu inapobidi, kwa maombi maalum, yanaonyesha kuwa yalikuwa yanafanywa kwa kutumia toleo tofauti. Kimsingi, hata wazo nzuri - na mpya itakuwa, kama ilivyokuwa, "iliyochanganywa", iliyowekwa juu ya ya zamani. Lakini hebu tuone jinsi ilivyofanywa. Ukweli, sikuweza kuiangalia tangu mwanzo - ni ya kuchekesha, lakini mchoro wa safu ya msingi haipo. Tabaka zilianza na 2. Hati hutuambia kuhusu kipengele maalum cha TL:

Ikiwa mteja anaunga mkono Tabaka 2, basi mjenzi afuatayo lazima atumike:

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

Kwa mazoezi, hii inamaanisha kuwa kabla ya kila simu ya API, int na thamani 0x289dd1f6 lazima iongezwe kabla ya nambari ya mbinu.

Sauti ya kawaida. Lakini nini kilitokea baadaye? Kisha ikaonekana

invokeWithLayer3#b7475268 query:!X = X;

Kwa hivyo ni nini kinachofuata? Kama unavyoweza kudhani,

invokeWithLayer4#dea0d430 query:!X = X;

Mapenzi? Hapana, ni mapema sana kucheka, fikiria juu ya ukweli kwamba kila mmoja ombi kutoka kwa safu nyingine inahitaji kuvikwa kwa aina maalum - ikiwa zote ni tofauti kwako, unawezaje kuzitofautisha? Na kuongeza baiti 4 tu mbele ni njia nzuri sana. Kwa hiyo,

invokeWithLayer5#417a57ae query:!X = X;

Lakini ni dhahiri kwamba baada ya muda hii itakuwa aina fulani ya bacchanalia. Na suluhisho likaja:

Sasisha: Kuanzia na Tabaka la 9, njia za msaidizi invokeWithLayerN inaweza kutumika tu pamoja initConnection

Hooray! Baada ya matoleo 9, hatimaye tulikuja kwa kile kilichofanywa katika itifaki za mtandao nyuma katika miaka ya 80 - kukubaliana juu ya toleo mara moja mwanzoni mwa muunganisho!

Kwa hivyo ni nini kinachofuata? ..

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

Lakini sasa bado unaweza kucheka. Tu baada ya tabaka zingine 9, mjenzi wa ulimwengu wote aliye na nambari ya toleo aliongezwa hatimaye, ambayo inahitaji kuitwa mara moja tu mwanzoni mwa unganisho, na maana ya tabaka ilionekana kutoweka, sasa ni toleo la masharti, kama. kila mahali pengine. Tatizo limetatuliwa.

Kweli?..

Vasily, [16.07.18 14:01] Hata Ijumaa nilifikiria:
Teleserver hutuma matukio bila ombi. Maombi lazima yamefungwa katika InvokeWithLayer. Seva haifungi masasisho; hakuna muundo wa kufunga majibu na masasisho.

Wale. mteja hawezi kutaja safu ambayo anataka sasisho

Vadim Goncharov, [16.07.18 14:02] je InvokeWithLayer si njia ya msingi?

Vasily, [16.07.18 14:02] Hii ndiyo njia pekee

Vadim Goncharov, [16.07.18 14:02] ambayo kimsingi inapaswa kumaanisha kukubaliana juu ya safu mwanzoni mwa kikao.

Kwa njia, inafuata kwamba kupungua kwa mteja hakutolewa

Sasisho, i.e. aina Updates katika mpango, hii ndio seva hutuma kwa mteja sio kwa kujibu ombi la API, lakini kwa kujitegemea wakati tukio linatokea. Hii ni mada ngumu ambayo itajadiliwa katika chapisho lingine, lakini kwa sasa ni muhimu kujua kwamba seva huhifadhi Sasisho hata wakati mteja yuko nje ya mtandao.

Kwa hivyo, ikiwa unakataa kufunika kila mmoja kifurushi kuashiria toleo lake, hii inasababisha shida zifuatazo zinazowezekana:

  • seva hutuma sasisho kwa mteja hata kabla ya mteja kujulisha ni toleo gani linaauni
  • nifanye nini baada ya kuboresha mteja?
  • nani dhamanakwamba maoni ya seva kuhusu nambari ya safu hayatabadilika wakati wa mchakato?

Unafikiri hii ni uvumi tu wa kinadharia, na kwa mazoezi hii haiwezi kutokea, kwa sababu seva imeandikwa kwa usahihi (angalau, imejaribiwa vizuri)? Ha! Haijalishi ni jinsi gani!

Hivi ndivyo tulivyokimbilia mnamo Agosti. Mnamo Agosti 14, kulikuwa na ujumbe kwamba kitu kilikuwa kikisasishwa kwenye seva za Telegramu... na kisha kwenye kumbukumbu:

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.

na kisha megabytes kadhaa za ufuatiliaji wa stack (vizuri, wakati huo huo ukataji wa miti uliwekwa). Baada ya yote, ikiwa kitu hakitambuliwi katika TL yako, ni ya binary kwa saini, chini ya mstari YOTE huenda, usimbuaji hautawezekana. Unapaswa kufanya nini katika hali kama hiyo?

Kweli, jambo la kwanza linalokuja akilini mwa mtu yeyote ni kukata muunganisho na kujaribu tena. Haikusaidia. Tuna google CRC32 - hizi ziligeuka kuwa vitu kutoka kwa mpango wa 73, ingawa tulifanya kazi kwenye 82. Tunaangalia kwa makini magogo - kuna vitambulisho kutoka kwa mipango miwili tofauti!

Labda shida iko kwa mteja wetu ambaye sio rasmi? Hapana.

Ukosoaji wa itifaki na njia za shirika za Telegraph. Sehemu ya 1, kiufundi: uzoefu wa kuandika mteja kutoka mwanzo - TL, MT

Google ilionyesha kuwa shida kama hiyo ilikuwa tayari imetokea kwa mmoja wa wateja wasio rasmi, lakini basi nambari za toleo na, ipasavyo, mawazo yalikuwa tofauti ...

Kwa hiyo tufanye nini? Vasily na mimi tuligawanyika: alijaribu kusasisha mzunguko hadi 91, niliamua kusubiri siku chache na kujaribu 73. Njia zote mbili zilifanya kazi, lakini kwa kuwa ni za nguvu, hakuna ufahamu wa matoleo ngapi juu au chini unahitaji. kuruka, au unahitaji kusubiri kwa muda gani.

Baadaye niliweza kuzaliana hali hiyo: tunazindua mteja, kuzima, kukusanya mzunguko kwenye safu nyingine, kuanzisha upya, kupata tatizo tena, kurudi kwa ile ya awali - oops, hakuna kiasi cha kubadili mzunguko na mteja anaanza tena. dakika chache zitasaidia. Utapokea mchanganyiko wa miundo ya data kutoka kwa tabaka tofauti.

Maelezo? Kama unavyoweza kukisia kutoka kwa dalili mbali mbali zisizo za moja kwa moja, seva ina michakato mingi ya aina tofauti kwenye mashine tofauti. Uwezekano mkubwa zaidi, seva ambayo inawajibika kwa "buffering" iliweka kwenye foleni yale ambayo wakubwa wake walimpa, na waliitoa katika mpango uliokuwapo wakati wa kizazi. Na mpaka foleni hii "imeoza", hakuna kitu kinachoweza kufanywa kuhusu hilo.

Labda ... lakini hii ni crutch ya kutisha?!.. Hapana, kabla ya kufikiria mawazo ya mambo, hebu tuangalie kanuni za wateja rasmi. Katika toleo la Android hatupati kichanganuzi chochote cha TL, lakini tunapata faili kubwa (GitHub inakataa kuigusa) na (de) usanifu. Hapa kuna vijisehemu vya kanuni:

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;

au

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

Hmm ... inaonekana porini. Lakini, pengine, hii inazalishwa msimbo, basi sawa? .. Lakini hakika inasaidia matoleo yote! Kweli, haijulikani kwa nini kila kitu kinachanganywa pamoja, mazungumzo ya siri, na kila aina ya _old7 kwa namna fulani haionekani kama kizazi cha mashine ... Hata hivyo, zaidi ya yote nilipeperushwa na

TL_message_layer104
TL_message_layer104_2
TL_message_layer104_3

Jamani, hamwezi hata kuamua kilichomo ndani ya safu moja?! Naam, sawa, hebu sema "mbili" zilitolewa na kosa, vizuri, hutokea, lakini TATU? .. Mara moja, tafuta sawa tena? Hii ni ponografia ya aina gani, samahani?..

Katika msimbo wa chanzo wa Desktop ya Telegraph, kwa njia, jambo kama hilo hufanyika - ikiwa ni hivyo, ahadi kadhaa mfululizo kwenye mpango hazibadilishi nambari yake ya safu, lakini rekebisha kitu. Katika hali ambapo hakuna chanzo rasmi cha data kwa mpango huo, inaweza kupatikana wapi, isipokuwa kwa nambari ya chanzo ya mteja rasmi? Na ikiwa utaichukua kutoka hapo, huwezi kuwa na uhakika kuwa mpango huo ni sahihi kabisa hadi ujaribu njia zote.

Je, hii inawezaje hata kujaribiwa? Natumaini mashabiki wa kitengo, kazi na vipimo vingine watashiriki katika maoni.

Sawa, wacha tuangalie kipande kingine cha msimbo:

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;

Maoni haya "yaliyoundwa kwa mikono" yanapendekeza kwamba ni sehemu tu ya faili hii iliandikwa kwa mikono (unaweza kufikiria ndoto nzima ya matengenezo?), na iliyobaki ilitolewa na mashine. Hata hivyo, basi swali jingine linatokea - kwamba vyanzo vinapatikana sio kabisa (matone ya la GPL kwenye kinu cha Linux), lakini hii tayari ni mada ya sehemu ya pili.

Lakini inatosha. Wacha tuendelee kwenye itifaki ambayo juu yake usanifu huu wote unaendeshwa.

Proto ya MT

Kwa hiyo, tufungue maelezo ya Jumla ΠΈ maelezo ya kina ya itifaki na kitu cha kwanza tunachojikwaa ni istilahi. Na kwa wingi wa kila kitu. Kwa ujumla, hii inaonekana kuwa hulka ya umiliki wa Telegramu - kuita vitu kwa njia tofauti katika sehemu tofauti, au vitu tofauti kwa neno moja, au kinyume chake (kwa mfano, katika API ya kiwango cha juu, ikiwa unaona pakiti ya vibandiko, sivyo. ulichofikiria).

Kwa mfano, "ujumbe" na "kikao" humaanisha kitu tofauti hapa kuliko kiolesura cha kawaida cha mteja wa Telegramu. Kweli, kila kitu kiko wazi na ujumbe, inaweza kufasiriwa kwa maneno ya OOP, au kuitwa tu neno "pakiti" - hii ni kiwango cha chini, cha usafirishaji, hakuna ujumbe sawa na kwenye kiolesura, kuna ujumbe mwingi wa huduma. . Lakini kikao... lakini kwanza mambo kwanza.

safu ya usafiri

Jambo la kwanza ni usafiri. Watatuambia kuhusu chaguzi 5:

  • TCP
  • Mtandao
  • Soketi ya wavuti juu ya HTTPS
  • HTTP
  • HTTPS

Vasily, [15.06.18 15:04] Pia kuna usafiri wa UDP, lakini haujarekodiwa

Na TCP katika lahaja tatu

Ya kwanza ni sawa na UDP juu ya TCP, kila pakiti inajumuisha nambari ya mlolongo na crc
Kwa nini kusoma hati kwenye mkokoteni ni chungu sana?

Naam, hapo sasa TCP tayari katika vibadala 4:

  • abridged
  • Kati
  • Padded kati
  • Kamili

Sawa, Sawa, Iliyowekwa kati ya MProxy, hii iliongezwa baadaye kwa sababu ya hafla zinazojulikana. Lakini kwa nini matoleo mawili zaidi (tatu kwa jumla) wakati unaweza kupata na moja? Zote nne kimsingi hutofautiana tu katika jinsi ya kuweka urefu na upakiaji wa MTProto kuu, ambayo itajadiliwa zaidi:

  • katika Ufupisho ni ka 1 au 4, lakini si 0xef, basi mwili
  • katika Kati hii ni baiti 4 za urefu na uga, na mara ya kwanza mteja lazima atume 0xeeeeeeee kuashiria kuwa ni ya Kati
  • katika Kamili inayolevya zaidi, kutoka kwa mtazamo wa mwanamtandao: urefu, nambari ya mfuatano, na SIYO Ile ambayo ni MTProto, mwili, CRC32. Ndiyo, yote haya ni juu ya TCP. Ambayo hutupatia usafiri wa kutegemewa katika mfumo wa mtiririko wa baiti unaofuatana; hakuna mfuatano unaohitajika, hasa hundi. Sawa, sasa mtu atanipinga kuwa TCP ina ukaguzi wa 16-bit, kwa hivyo uharibifu wa data hutokea. Sawa, lakini kwa kweli tuna itifaki ya kriptografia yenye heshi ndefu zaidi ya baiti 16, hitilafu hizi zote - na hata zaidi - zitanaswa na kutolingana kwa SHA kwa kiwango cha juu zaidi. Hakuna uhakika katika CRC32 juu ya hii.

Wacha tulinganishe Iliyofupishwa, ambayo byte moja ya urefu inawezekana, na ya Kati, ambayo inahalalisha "Ikiwa upatanishi wa data wa 4-byte inahitajika," ambayo ni upuuzi kabisa. Je, inaaminika kuwa watengeneza programu wa Telegramu hawana uwezo kiasi kwamba hawawezi kusoma data kutoka kwa tundu hadi kwenye bafa iliyosawazishwa? Bado unapaswa kufanya hivyo, kwa sababu kusoma kunaweza kukurudishia idadi yoyote ya ka (na pia kuna seva za wakala, kwa mfano ...). Au kwa upande mwingine, kwa nini uzuie Kifupi ikiwa bado tutakuwa na pedi kubwa juu ya baiti 16 - kuokoa ka 3 wakati mwingine ?

Mtu hupata maoni kwamba Nikolai Durov anapenda sana kuunda tena magurudumu, pamoja na itifaki za mtandao, bila hitaji la kweli la vitendo.

Chaguzi zingine za usafiri, pamoja na. Mtandao na MTProxy, hatutazingatia sasa, labda katika chapisho lingine, ikiwa kuna ombi. Kuhusu MTProxy hii hii, hebu tukumbuke sasa kwamba muda mfupi baada ya kutolewa mnamo 2018, watoa huduma walijifunza haraka kuizuia, iliyokusudiwa kuzuia bypassna saizi ya kifurushi! Na pia ukweli kwamba seva ya MProxy iliyoandikwa (tena na Waltman) katika C iliunganishwa kupita kiasi na maelezo mahususi ya Linux, ingawa hii haikuhitajika hata kidogo (Phil Kulin atathibitisha), na kwamba seva kama hiyo katika Go au Node.js ingeweza. inafaa kwa chini ya mistari mia moja.

Lakini tutafanya hitimisho kuhusu ujuzi wa kiufundi wa watu hawa mwishoni mwa sehemu, baada ya kuzingatia masuala mengine. Kwa sasa, wacha tuendelee kwenye safu ya 5 ya OSI, kikao - ambacho waliweka kikao cha MTProto.

Funguo, ujumbe, vipindi, Diffie-Hellman

Waliiweka hapo si kwa usahihi kabisa... Kipindi si kipindi kile kile kinachoonekana kwenye kiolesura chini ya Vipindi Amilifu. Lakini kwa utaratibu.

Ukosoaji wa itifaki na njia za shirika za Telegraph. Sehemu ya 1, kiufundi: uzoefu wa kuandika mteja kutoka mwanzo - TL, MT

Kwa hiyo tulipokea kamba ya byte ya urefu unaojulikana kutoka kwa safu ya usafiri. Huu ni ujumbe uliosimbwa kwa njia fiche au maandishi wazi - ikiwa bado tuko katika hatua kuu ya makubaliano na tunafanya hivyo. Ni ipi kati ya kundi la dhana inayoitwa "ufunguo" tunayozungumzia? Hebu tufafanue suala hili kwa timu ya Telegram yenyewe (naomba radhi kwa kutafsiri hati zangu kutoka kwa Kiingereza na ubongo uliochoka saa 4 asubuhi, ilikuwa rahisi kuacha baadhi ya maneno kama yalivyo):

Kuna vyombo viwili vinavyoitwa kikao - moja katika UI ya wateja rasmi chini ya "vipindi vya sasa", ambapo kila kipindi kinalingana na kifaa kizima / Mfumo wa Uendeshaji.
Pili - Kikao cha MTProto, ambayo ina nambari ya mfuatano wa ujumbe (kwa maana ya kiwango cha chini) ndani yake, na ambayo inaweza kudumu kati ya miunganisho tofauti ya TCP. Vipindi kadhaa vya MTProto vinaweza kusakinishwa kwa wakati mmoja, kwa mfano, ili kuharakisha upakuaji wa faili.

Kati ya hizi mbili vikao vya kuna dhana idhini. Katika hali mbaya, tunaweza kusema hivyo Kipindi cha UI ni sawa na idhini, lakini ole, kila kitu ni ngumu. Hebu tuangalie:

  • Mtumiaji kwenye kifaa kipya hutoa kwanza auth_key na kuifunga kwa akaunti, kwa mfano kupitia SMS - ndiyo sababu idhini
  • Ilifanyika ndani ya kwanza Kikao cha MTProto, ambayo ina session_id ndani yako.
  • Katika hatua hii, mchanganyiko idhini ΠΈ session_id inaweza kuitwa mfano - neno hili linaonekana katika nyaraka na kanuni za baadhi ya wateja
  • Kisha, mteja anaweza kufungua baadhi Vikao vya MTProto chini ya huo auth_key - kwa DC huyo huyo.
  • Kisha, siku moja mteja atahitaji kuomba faili kutoka DC mwingine - na kwa DC hii mpya itatolewa auth_key !
  • Ili kufahamisha mfumo kuwa sio mtumiaji mpya anayejiandikisha, lakini ni sawa idhini (Kipindi cha UI), mteja hutumia simu za API auth.exportAuthorization nyumbani DC auth.importAuthorization katika DC mpya.
  • Kila kitu ni sawa, kadhaa zinaweza kufunguliwa Vikao vya MTProto (kila moja na yake session_id) kwa DC huyu mpya, chini ya yake auth_key.
  • Hatimaye, mteja anaweza kutaka Usiri Mbele kamili. Kila auth_key ilikuwa kudumu ufunguo - kwa DC - na mteja anaweza kupiga simu auth.bindTempAuthKey kwa matumizi muda auth_key - na tena, moja tu temp_auth_key kwa DC, kawaida kwa wote Vikao vya MTProto kwa huyu DC.

taarifa, hiyo chumvi (na chumvi za baadaye) pia ni moja auth_key hizo. imeshirikiwa kati ya kila mtu Vikao vya MTProto kwa DC huyo huyo.

Je, "kati ya miunganisho tofauti ya TCP" inamaanisha nini? Hivyo hii ina maana kitu kama kidakuzi cha uidhinishaji kwenye tovuti - kinaendelea (kunusurika) miunganisho mingi ya TCP kwa seva fulani, lakini siku moja huenda vibaya. Tofauti na HTTP pekee, katika ujumbe wa MTProto ndani ya kikao huhesabiwa kwa mpangilio na kuthibitishwa; ikiwa waliingia kwenye handaki, unganisho ulivunjika - baada ya kuanzisha muunganisho mpya, seva itatuma kila kitu katika kipindi hiki ambacho haikutoa hapo awali. Uunganisho wa TCP.

Walakini, habari iliyo hapo juu inafupishwa baada ya miezi mingi ya uchunguzi. Wakati huo huo, je, tunatekeleza mteja wetu kutoka mwanzo? - hebu kurudi mwanzo.

Basi hebu kuzalisha auth_key juu ya Matoleo ya Diffie-Hellman kutoka Telegram. Hebu jaribu kuelewa nyaraka ...

Vasily, [19.06.18 20:05] data_with_hash := SHA1(data) + data + (baiti zozote za nasibu); kiasi kwamba urefu ni sawa na baiti 255;
data_iliyosimbwa := RSA(data_with_hash, server_public_key); nambari ndefu ya baiti 255 (endian kubwa) inainuliwa hadi kwa nguvu inayohitajika juu ya moduli inayohitajika, na matokeo yake huhifadhiwa kama nambari ya baiti 256.

Wana dope DH

Haionekani kama DH ya mtu mwenye afya njema
Hakuna funguo mbili za umma katika dx

Kweli, mwishowe hii ilitatuliwa, lakini mabaki yalibaki - uthibitisho wa kazi unafanywa na mteja kwamba aliweza kuhesabu nambari. Aina ya ulinzi dhidi ya mashambulizi ya DoS. Na ufunguo wa RSA hutumiwa mara moja tu katika mwelekeo mmoja, kimsingi kwa usimbaji fiche new_nonce. Lakini ingawa operesheni hii inayoonekana kuwa rahisi itafaulu, utalazimika kukabiliana na nini?

Vasily, [20.06.18/00/26 XNUMX:XNUMX] Bado sijapata ombi la programu

Nilituma ombi hili kwa DH

Na, kwenye kizimbani cha usafirishaji inasema kwamba inaweza kujibu na ka 4 za msimbo wa makosa. Ni hayo tu

Naam, aliniambia -404, ili iweje?

Kwa hivyo nilimwambia: "Chukua ujinga wako uliosimbwa kwa ufunguo wa seva na alama ya vidole kama hii, nataka DH," na ilijibu kwa 404 ya kijinga.

Je, una maoni gani kuhusu jibu hili la seva? Nini cha kufanya? Hakuna wa kuuliza (lakini zaidi juu ya hilo katika sehemu ya pili).

Hapa maslahi yote yanafanywa kwenye kizimbani

Sina la kufanya, niliota tu kubadilisha nambari na kurudi

Nambari mbili za 32-bit. Nilizipakia kama kila mtu mwingine

Lakini hapana, hizi mbili zinahitaji kuongezwa kwenye mstari kwanza kama BE

Vadim Goncharov, [20.06.18 15:49] na kwa sababu ya hii 404?

Vasily, [20.06.18 15:49] NDIYO!

Vadim Goncharov, [20.06.18 15:50] kwa hivyo sielewi kile anachoweza "hakupata"

Vasily, [20.06.18 15:50] kuhusu

Sikuweza kupata mtengano kama huo katika sababu kuu%)

Hatukudhibiti hata kuripoti makosa

Vasily, [20.06.18 20:18] Lo, pia kuna MD5. Tayari heshi tatu tofauti

Alama ya vidole kuu imehesabiwa kama ifuatavyo:

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

SHA1 na sha2

Basi hebu tuweke auth_key tulipokea biti 2048 kwa ukubwa kwa kutumia Diffie-Hellman. Nini kinafuata? Halafu tunagundua kwamba bits 1024 za chini za ufunguo huu hazitumiwi kwa njia yoyote ... lakini hebu tufikirie juu ya hili kwa sasa. Katika hatua hii, tuna siri iliyoshirikiwa na seva. Analog ya kikao cha TLS imeanzishwa, ambayo ni utaratibu wa gharama kubwa sana. Lakini seva bado haijui chochote kuhusu sisi ni nani! Bado, kwa kweli. idhini. Wale. ikiwa ulifikiria kulingana na "nenosiri la kuingia", kama ulivyofanya kwenye ICQ, au angalau "ufunguo wa kuingia", kama katika SSH (kwa mfano, kwenye gitlab/github fulani). Tumepokea mtu asiyejulikana. Je, ikiwa seva itatuambia "nambari hizi za simu zinahudumiwa na DC mwingine"? Au hata "nambari yako ya simu imepigwa marufuku"? Bora tunaweza kufanya ni kuweka ufunguo kwa matumaini kwamba itakuwa muhimu na haitaharibika kufikia wakati huo.

Kwa njia, "tuliipokea" kwa kutoridhishwa. Kwa mfano, tunaamini seva? Je, ikiwa ni bandia? Ukaguzi wa kriptografia utahitajika:

Vasily, [21.06.18 17:53] Wanatoa wateja wa simu kuangalia nambari ya 2kbit ili kupata ubora%)

Lakini haijulikani hata kidogo, nafeijoa

Vasily, [21.06.18 18:02] Hati hiyo haisemi la kufanya ikiwa itakuwa si rahisi.

Si alisema. Hebu tuone nini mteja rasmi wa Android anafanya katika kesi hii? A ndio nini (na ndio, faili nzima inavutia) - kama wanasema, nitaacha hii hapa:

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

Hapana, bila shaka bado iko wengine Kuna majaribio ya ubora wa nambari, lakini kibinafsi sina tena maarifa ya kutosha ya hisabati.

Sawa, tumepata ufunguo mkuu. Ili kuingia, i.e. kutuma maombi, unahitaji kufanya encryption zaidi, kwa kutumia AES.

Ufunguo wa ujumbe unafafanuliwa kama biti 128 za kati za SHA256 za mwili wa ujumbe (pamoja na kipindi, kitambulisho cha ujumbe, n.k.), ikijumuisha baiti za kuweka, hutanguliwa na baiti 32 zilizochukuliwa kutoka kwa ufunguo wa uidhinishaji.

Vasily, [22.06.18 14:08] Wastani, bitch, biti

Imepokelewa auth_key. Wote. Zaidi yao ... haijulikani wazi kutoka kwa hati. Jisikie huru kusoma msimbo wa chanzo huria.

Kumbuka kuwa MTProto 2.0 inahitaji kutoka baiti 12 hadi 1024 za pedi, bado chini ya masharti kwamba urefu wa ujumbe unaotokana unaweza kugawanywa kwa baiti 16.

Kwa hivyo unapaswa kuongeza pedi ngapi?

Na ndio, pia kuna 404 ikiwa kuna kosa

Ikiwa mtu yeyote alisoma kwa uangalifu mchoro na maandishi ya nyaraka, aligundua kuwa hakuna MAC hapo. Na kwamba AES inatumika katika hali fulani ya IGE ambayo haitumiki popote pengine. Wao, bila shaka, huandika kuhusu hili katika Maswali Yanayoulizwa Mara kwa Mara... Hapa, kama, ufunguo wa ujumbe wenyewe pia ni SHA hash ya data iliyosimbwa, inayotumiwa kuangalia uadilifu - na ikiwa kuna kutolingana, hati kwa sababu fulani. inapendekeza kuwapuuza kimya kimya (lakini vipi kuhusu usalama, vipi ikiwa watatuvunja?).

Mimi si mwandishi wa maandishi, labda hakuna kitu kibaya na hali hii katika kesi hii kutoka kwa mtazamo wa kinadharia. Lakini naweza kutaja wazi shida ya vitendo, kwa kutumia Telegraph Desktop kama mfano. Inasimba akiba ya ndani (yote D877F783D5D3EF8C) kwa njia sawa na ujumbe katika MTProto (tu katika kesi hii toleo la 1.0), i.e. kwanza ufunguo wa ujumbe, kisha data yenyewe (na mahali pengine kando kuu kuu auth_key 256 ka, bila hiyo msg_key isiyo na maana). Kwa hiyo, tatizo linaonekana kwenye faili kubwa. Yaani, unahitaji kuweka nakala mbili za data - encrypted na decrypted. Na ikiwa kuna megabaiti, au video ya kutiririsha, kwa mfano?.. Miradi ya kawaida iliyo na MAC baada ya maandishi ya siri hukuruhusu kuisoma mkondo, kuisambaza mara moja. Lakini kwa MTProto itabidi mwanzoni encrypt au kusimbua ujumbe mzima, kisha tu kuhamisha kwa mtandao au disk. Kwa hiyo, katika matoleo ya hivi karibuni ya Telegram Desktop katika cache in user_data Muundo mwingine pia hutumiwa - na AES katika hali ya CTR.

Vasily, [21.06.18 01:27] Lo, nimegundua IGE ni nini: IGE lilikuwa jaribio la kwanza la "hali ya uthibitishaji wa usimbaji fiche," asili ya Kerberos. Lilikuwa ni jaribio lililofeli (halitoi ulinzi wa uadilifu), na ilibidi liondolewe. Huo ulikuwa mwanzo wa jitihada ya miaka 20 ya uthibitishaji wa hali ya usimbaji fiche ambayo inafanya kazi, ambayo hivi majuzi iliishia katika hali kama vile OCB na GCM.

Na sasa hoja kutoka upande wa gari:

Timu iliyo nyuma ya Telegram, inayoongozwa na Nikolai Durov, ina mabingwa sita wa ACM, nusu yao wakiwa Ph.D katika hesabu. Iliwachukua takriban miaka miwili kusambaza toleo la sasa la MTProto.

Hiyo inachekesha. Miaka miwili katika ngazi ya chini

Au unaweza tu kuchukua tls

Sawa, tuseme tumefanya usimbaji fiche na nuances nyingine. Inawezekana hatimaye kutuma maombi yaliyosasishwa katika TL na kuondoa majibu? Kwa hivyo unapaswa kutuma nini na jinsi gani? Hapa, hebu sema, mbinu initConnection, labda hii ni?

Vasily, [25.06.18 18:46] Huanzisha muunganisho na kuhifadhi maelezo kwenye kifaa na programu ya mtumiaji.

Inakubali app_id, device_model, system_version, app_version na lang_code.

Na swali fulani

Nyaraka kama kawaida. Jisikie huru kusoma chanzo wazi

Ikiwa kila kitu kilikuwa wazi kwa invokeWithLayer, basi kuna nini kibaya hapa? Inageuka, wacha tuseme tunayo - mteja tayari alikuwa na kitu cha kuuliza seva - kuna ombi ambalo tulitaka kutuma:

Vasily, [25.06.18 19:13] Kwa kuzingatia msimbo, simu ya kwanza imefungwa kwenye upuuzi huu, na ujinga wenyewe umefungwa kwa invokewithlayer.

Kwa nini initConnection haikuweza kuwa simu tofauti, lakini lazima iwe kanga? Ndio, kama ilivyotokea, lazima ifanyike kila wakati mwanzoni mwa kila kikao, na sio mara moja, kama ilivyo kwa ufunguo kuu. Lakini! Haiwezi kuitwa na mtumiaji ambaye hajaidhinishwa! Sasa tumefikia hatua ambayo inatumika Huyu ukurasa wa nyaraka - na inatuambia kuwa ...

Sehemu ndogo tu ya mbinu za API zinapatikana kwa watumiaji wasioidhinishwa:

  • auth.sendCode
  • auth.rendCode
  • account.getPassword
  • auth.checkPassword
  • auth.checkSimu
  • auth.signUp
  • auth.signIn
  • auth.importIdhinishaji
  • help.getConfig
  • help.getNearestDc
  • help.getAppUpdate
  • help.getCdnConfig
  • langpack.getLangPack
  • langpack.getStrings
  • langpack.getDifference
  • langpack.getLanguages
  • langpack.getLanguage

Wa kwanza kabisa wao, auth.sendCode, na kuna lile ombi la kwanza linalopendwa ambalo tunatuma api_id na api_hash, na kisha tunapokea SMS iliyo na msimbo. Na ikiwa tuko katika DC isiyo sahihi (nambari za simu katika nchi hii zinatumiwa na mwingine, kwa mfano), basi tutapokea kosa na nambari ya DC inayotaka. Ili kujua ni anwani gani ya IP kwa nambari ya DC unahitaji kuunganisha, tusaidie help.getConfig. Wakati mmoja kulikuwa na maingizo 5 tu, lakini baada ya matukio maarufu ya 2018, idadi imeongezeka kwa kiasi kikubwa.

Sasa hebu tukumbuke kwamba tulifika kwenye hatua hii kwenye seva bila kujulikana. Je, si ni ghali sana kupata tu anwani ya IP? Kwa nini usifanye hivi, na shughuli zingine, katika sehemu ambayo haijasimbwa kwa MTProto? Ninasikia pingamizi: "tunawezaje kuhakikisha kuwa sio RKN ambaye atajibu kwa anwani za uwongo?" Kwa hili tunakumbuka kwamba, kwa ujumla, wateja rasmi Vifunguo vya RSA vimepachikwa, i.e. unaweza tu saini habari hii. Kwa kweli, hii tayari inafanywa kwa habari juu ya kupitisha kizuizi ambacho wateja hupokea kupitia chaneli zingine (kimantiki, hii haiwezi kufanywa katika MTProto yenyewe; unahitaji pia kujua wapi pa kuunganishwa).

SAWA. Katika hatua hii ya uidhinishaji wa mteja, bado hatujaidhinishwa na hatujasajili ombi letu. Tunataka tu kuona kwa sasa kile seva hujibu kwa mbinu zinazopatikana kwa mtumiaji ambaye hajaidhinishwa. Na hapa…

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;

Katika mpango, kwanza huja pili

Kwenye schema ya tdesktop thamani ya tatu ni

Ndiyo, tangu wakati huo, bila shaka, nyaraka zimesasishwa. Ingawa hivi karibuni inaweza kuwa haina maana tena. Msanidi programu anayeanza anapaswa kujuaje? Labda ukisajili ombi lako, watakujulisha? Vasily alifanya hivyo, lakini ole, hawakumtuma chochote (tena, tutazungumza juu ya hili katika sehemu ya pili).

...Uligundua kuwa tayari kwa namna fulani tumehamia API, yaani. kwa kiwango kinachofuata, na umekosa kitu katika mada ya MTProto? Hakuna mshangao:

Vasily, [28.06.18 02:04] Mm, wanachunguza baadhi ya algoriti kwenye e2e

Mtproto inafafanua algoriti na funguo za usimbaji fiche kwa vikoa vyote viwili, na vile vile muundo mdogo wa kanga.

Lakini huchanganya viwango tofauti vya safu kila wakati, kwa hivyo haijulikani kila wakati mtproto iliishia wapi na kiwango kinachofuata kilianza.

Je, wanachanganyaje? Naam, hapa ni ufunguo huo wa muda kwa PFS, kwa mfano (kwa njia, Telegram Desktop haiwezi kufanya hivyo). Inatekelezwa na ombi la API auth.bindTempAuthKey, i.e. kutoka ngazi ya juu. Lakini wakati huo huo inaingilia usimbuaji kwa kiwango cha chini - baada yake, kwa mfano, unahitaji kuifanya tena. initConnection nk, hii sivyo tu ombi la kawaida. Kilicho maalum pia ni kwamba unaweza kuwa na ufunguo MOJA tu wa muda kwa kila DC, ingawa uga auth_key_id katika kila ujumbe hukuruhusu kubadilisha ufunguo angalau kila ujumbe, na kwamba seva ina haki ya "kusahau" ufunguo wa muda wakati wowote - nyaraka hazisemi nini cha kufanya katika kesi hii ... vizuri, kwa nini haikuweza. Je! huna funguo kadhaa, kama na seti ya chumvi za baadaye, na ?..

Kuna mambo mengine machache ya kuzingatia kuhusu mandhari ya MTProto.

Ujumbe wa ujumbe, msg_id, msg_seqno, uthibitisho, pings katika mwelekeo usio sahihi na dhana nyinginezo.

Kwa nini unahitaji kujua juu yao? Kwa sababu "huvuja" hadi kiwango cha juu, na unahitaji kuwafahamu wakati wa kufanya kazi na API. Wacha tuchukulie kuwa hatuvutiwi na msg_key; kiwango cha chini kimeondoa kila kitu kwa ajili yetu. Lakini ndani ya data iliyosimbwa tuna sehemu zifuatazo (pia urefu wa data, kwa hivyo tunajua wapi pedi iko, lakini hiyo sio muhimu):

  • chumvi - int64
  • kikao_id - int64
  • message_id - int64
  • seq_no - int32

Tuwakumbushe kuwa kuna chumvi moja tu kwa DC mzima. Kwa nini kujua juu yake? Sio tu kwa sababu kuna ombi get_future_salts, ambayo inakuambia ni vipindi gani vitakuwa halali, lakini pia kwa sababu ikiwa chumvi yako "imeoza", basi ujumbe (ombi) utapotea tu. Seva, bila shaka, itaripoti chumvi mpya kwa kutoa new_session_created - lakini na ile ya zamani itabidi uitume tena kwa njia fulani, kwa mfano. Na suala hili linaathiri usanifu wa maombi.

Seva inaruhusiwa kuacha vipindi kabisa na kujibu kwa njia hii kwa sababu nyingi. Kweli, kikao cha MTProto kutoka kwa upande wa mteja ni nini? Hizi ni nambari mbili session_id ΠΈ seq_no ujumbe ndani ya kipindi hiki. Kweli, na unganisho la msingi la TCP, kwa kweli. Hebu tuseme mteja wetu bado hajui jinsi ya kufanya mambo mengi, alitenganisha na kuunganisha tena. Ikiwa hii ilitokea haraka - kikao cha zamani kiliendelea katika uunganisho mpya wa TCP, ongezeko seq_no zaidi. Ikiwa inachukua muda mrefu, seva inaweza kuifuta, kwa sababu kwa upande wake pia ni foleni, kama tulivyogundua.

Inapaswa kuwa nini seq_no? Lo, hilo ni swali gumu. Jaribu kuelewa kwa uaminifu nini kilimaanisha:

Ujumbe unaohusiana na Maudhui

Ujumbe unaohitaji uthibitisho wa wazi. Hizi ni pamoja na mtumiaji na jumbe nyingi za huduma, karibu zote isipokuwa vyombo na shukrani.

Nambari ya Mfuatano wa Ujumbe (msg_seqno)

Nambari ya biti 32 sawa na mara mbili ya idadi ya jumbe "zinazohusiana na yaliyomo" (zile zinazohitaji kukiri, na haswa zile ambazo sio vyombo) iliyoundwa na mtumaji kabla ya ujumbe huu na baadaye kuongezwa kwa moja ikiwa ujumbe wa sasa ni ujumbe unaohusiana na maudhui. Chombo daima hutolewa baada ya yaliyomo yake yote; kwa hivyo, nambari yake ya mfuatano ni kubwa kuliko au sawa na nambari za mfuatano wa jumbe zilizomo ndani yake.

Hii ni sarakasi ya aina gani iliyo na nyongeza kwa 1, na kisha nyingine kwa 2? hasa, inatoka nje, inaweza kutumwa baadhi uthibitisho kuwa sawa seq_no! Vipi? Kweli, kwa mfano, seva hututumia kitu, hutuma, na sisi wenyewe tunakaa kimya, tukijibu ujumbe wa huduma unaothibitisha kupokea ujumbe wake. Katika kesi hii, uthibitishaji wetu unaotoka utakuwa na nambari sawa inayotoka. Ikiwa unafahamu TCP na ulifikiri kwamba hii inaonekana kwa namna fulani ya mwitu, lakini inaonekana si ya mwitu sana, kwa sababu katika TCP seq_no haibadiliki, lakini uthibitisho huenda kwa seq_no kwa upande mwingine, nitaharakisha kukukasirisha. Uthibitisho hutolewa katika MTPto NOT juu ya seq_no, kama katika TCP, lakini kwa msg_id !

Hii ni nini msg_id, muhimu zaidi ya nyanja hizi? Kitambulisho cha kipekee cha ujumbe, kama jina linapendekeza. Inafafanuliwa kama nambari ya biti-64, biti za chini kabisa ambazo tena zina uchawi wa "seva-si-seva", na iliyobaki ni muhuri wa muda wa Unix, pamoja na sehemu ya sehemu, iliyohamishiwa biti 32 kushoto. Wale. timestamp per se (na jumbe zenye nyakati zinazotofautiana sana zitakataliwa na seva). Kutoka kwa hili inageuka kuwa kwa ujumla hii ni kitambulisho ambacho ni cha kimataifa kwa mteja. Kwa kuzingatia - wacha tukumbuke session_id - tumehakikishiwa: Kwa hali yoyote hakuna ujumbe unaokusudiwa kwa kipindi kimoja kutumwa katika kipindi tofauti. Hiyo ni, zinageuka kuwa tayari kuna tatu kiwango - kikao, nambari ya kikao, kitambulisho cha ujumbe. Kwa nini overcomplication vile, siri hii ni kubwa sana.

Hivyo, msg_id inahitajika kwa...

RPC: maombi, majibu, makosa. Uthibitisho.

Kama unaweza kuwa umegundua, hakuna aina maalum ya "fanya ombi la RPC" au kazi mahali popote kwenye mchoro, ingawa kuna majibu. Baada ya yote, tuna ujumbe unaohusiana na maudhui! Hiyo ni, yoyote ujumbe unaweza kuwa ombi! Au si kuwa. Baada ya yote, kila mmoja kuna msg_id. Lakini kuna majibu:

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

Hapa ndipo inapoonyeshwa ni jibu gani la ujumbe huu. Kwa hivyo, katika kiwango cha juu cha API, itabidi ukumbuke nambari ya ombi lako ilikuwa nini - nadhani hakuna haja ya kuelezea kuwa kazi hiyo ni ya asynchronous, na kunaweza kuwa na maombi kadhaa yanayoendelea kwa wakati mmoja, majibu ambayo yanaweza kurejeshwa kwa mpangilio wowote? Kimsingi, kutoka kwa hii na ujumbe wa makosa kama hakuna wafanyikazi, usanifu nyuma ya hii unaweza kufuatiliwa: seva inayodumisha muunganisho wa TCP na wewe ni kisawazisha cha mbele, hutuma maombi kwa sehemu za nyuma na kuzikusanya kupitia. message_id. Inaonekana kwamba kila kitu hapa ni wazi, mantiki na nzuri.

Ndiyo? .. Na ikiwa unafikiri juu yake? Baada ya yote, majibu ya RPC yenyewe pia yana uwanja msg_id! Je! tunahitaji kupiga kelele kwa seva "hujibu jibu langu!"? Na ndio, kulikuwa na nini juu ya uthibitisho? Kuhusu ukurasa ujumbe kuhusu ujumbe inatuambia ni nini

msgs_ack#62d6b459 msg_ids:Vector long = MsgsAck;

na ni lazima ifanywe kwa kila upande. Lakini si mara zote! Ikiwa umepokea RpcResult, yenyewe hutumika kama uthibitisho. Hiyo ni, seva inaweza kujibu ombi lako na MsgsAck - kama, "Nimeipokea." RpcResult inaweza kujibu mara moja. Inaweza kuwa zote mbili.

Na ndio, bado unapaswa kujibu jibu! Uthibitisho. Vinginevyo, seva itaiona kuwa haiwezi kuwasilishwa na itakurejeshea tena. Hata baada ya kuunganishwa tena. Lakini hapa, kwa kweli, suala la kumalizika kwa muda linatokea. Hebu tutazame baadaye kidogo.

Wakati huo huo, hebu tuangalie makosa yanayowezekana ya utekelezaji wa hoja.

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

Lo, mtu atashangaa, hapa kuna muundo wa kibinadamu zaidi - kuna mstari! Kuchukua muda wako. Hapa orodha ya makosa, lakini bila shaka haijakamilika. Kutoka kwake tunajifunza kuwa kanuni ni kitu kama Makosa ya HTTP (vizuri, kwa kweli, semantiki za majibu haziheshimiwi, katika sehemu zingine zinasambazwa nasibu kati ya nambari), na mstari unaonekana kama. CAPITAL_LETTERS_AND_NUMBERS. Kwa mfano, PHONE_NUMBER_OCCUPIED au FILE_PART_Π₯_MISSING. Naam, yaani, bado utahitaji mstari huu changanua. Kwa mfano FLOOD_WAIT_3600 itamaanisha kwamba unapaswa kusubiri saa moja, na PHONE_MIGRATE_5, kwamba nambari ya simu iliyo na kiambishi awali hiki lazima isajiliwe katika 5th DC. Tuna lugha ya aina, sawa? Hatuhitaji hoja kutoka kwa kamba, wale wa kawaida watafanya, sawa.

Tena, hii haiko kwenye ukurasa wa ujumbe wa huduma, lakini, kama ilivyo kawaida na mradi huu, habari inaweza kupatikana kwenye ukurasa mwingine wa nyaraka. Au kutia shaka. Kwanza, angalia, ukiukaji wa kuandika/safu - RpcError inaweza kuwekwa ndani RpcResult. Kwa nini si nje? Hatukuzingatia nini?.. Ipasavyo, dhamana hiyo iko wapi RpcError HAIWEZI kupachikwa RpcResult, lakini kuwa moja kwa moja au kiota katika aina nyingine? .. Na ikiwa haiwezi, kwa nini sio kwenye ngazi ya juu, i.e. inakosekana req_msg_id ? ..

Lakini hebu tuendelee kuhusu ujumbe wa huduma. Mteja anaweza kufikiria kuwa seva inafikiria kwa muda mrefu na kufanya ombi hili zuri:

rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;

Kuna majibu matatu yanayowezekana kwa swali hili, tena yakiingiliana na utaratibu wa uthibitishaji; kujaribu kuelewa ni nini wanapaswa kuwa (na ni orodha gani ya jumla ya aina ambazo hazihitaji uthibitisho) imeachwa kwa msomaji kama kazi ya nyumbani (kumbuka: habari katika msimbo wa chanzo wa Eneo-kazi la Telegram haujakamilika).

Madawa ya kulevya: hali ya ujumbe

Kwa ujumla, maeneo mengi katika TL, MTProto na Telegram kwa ujumla huacha hisia ya ukaidi, lakini kutokana na adabu, busara na wengine. ujuzi wa laini Tulinyamaza kimya juu yake kwa upole, na tukadhibiti matusi kwenye mijadala. Walakini, mahali hapaОzaidi ya ukurasa ni kuhusu ujumbe kuhusu ujumbe Inashangaza hata kwangu, ambaye amekuwa akifanya kazi na itifaki za mtandao kwa muda mrefu na ameona baiskeli za viwango tofauti vya upotovu.

Inaanza bila hatia, na uthibitisho. Ijayo wanatuambia kuhusu

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;

Kweli, kila mtu anayeanza kufanya kazi na MTProto atalazimika kushughulika nazo; katika mzunguko "uliosahihishwa - uliojumuishwa - uliozinduliwa", kupata makosa ya nambari au chumvi ambayo imeweza kuwa mbaya wakati wa uhariri ni jambo la kawaida. Walakini, kuna mambo mawili hapa:

  1. Hii ina maana kwamba ujumbe wa awali umepotea. Tunahitaji kuunda baadhi ya foleni, tutaangalia hilo baadaye.
  2. Nambari hizi za makosa ya ajabu ni nini? 16, 17, 18, 19, 20, 32, 33, 34, 35, 48, 64 ... nambari zingine ziko wapi, Tommy?

Nyaraka zinasema:

Kusudi ni kwamba maadili ya msimbo wa makosa yamewekwa katika vikundi (msimbo wa makosa >> 4): kwa mfano, misimbo 0x40 β€” 0x4f inalingana na makosa katika mtengano wa kontena.

lakini, kwanza, mabadiliko katika mwelekeo mwingine, na pili, haijalishi, wapi kanuni nyingine? Katika kichwa cha mwandishi? .. Walakini, hizi ni vitapeli.

Uraibu huanza katika ujumbe kuhusu hali za ujumbe na nakala za ujumbe:

  • Ombi la Taarifa ya Hali ya Ujumbe
    Ikiwa upande wowote haujapokea taarifa kuhusu hali ya ujumbe wake unaotoka kwa muda, inaweza kuiomba kwa njia dhahiri kutoka kwa mhusika mwingine:
    msgs_state_req#da69fb52 msg_ids:Vector long = MsgsStateReq;
  • Ujumbe wa Taarifa kuhusu Hali ya Ujumbe
    msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
    Hapa, info ni mfuatano ambao una baiti moja ya hali ya ujumbe kwa kila ujumbe kutoka kwa orodha ya msg_ids inayoingia:

    • 1 = hakuna kinachojulikana kuhusu ujumbe (msg_id chini sana, mtu mwingine anaweza kuwa ameisahau)
    • 2 = ujumbe haujapokelewa (msg_id iko ndani ya anuwai ya vitambulishi vilivyohifadhiwa; hata hivyo, upande mwingine hakika haujapokea ujumbe kama huo)
    • 3 = ujumbe haujapokelewa (msg_id juu sana; hata hivyo, upande mwingine bado haujaupokea)
    • 4 = ujumbe uliopokelewa (kumbuka kuwa jibu hili pia wakati huo huo ni uthibitisho wa risiti)
    • +8 = ujumbe tayari umekubaliwa
    • +16 = ujumbe usiohitaji kukiri
    • +32 = Hoja ya RPC iliyo katika ujumbe unaochakatwa au kuchakatwa tayari kumekamilika
    • +64 = jibu linalohusiana na maudhui kwa ujumbe ambao tayari umetolewa
    • +128 = mhusika mwingine anajua kwa hakika kwamba ujumbe tayari umepokelewa
      Jibu hili halihitaji kukiri. Ni uthibitisho wa msgs_state_req husika, ndani na yenyewe.
      Kumbuka kwamba ikiwa itatokea ghafla kwamba upande mwingine hauna ujumbe unaoonekana kama umetumwa kwake, ujumbe unaweza kutumwa tena. Hata kama mhusika mwingine atapokea nakala mbili za ujumbe kwa wakati mmoja, nakala hiyo itapuuzwa. (Ikiwa muda mwingi umepita, na msg_id asili si halali tena, ujumbe unapaswa kufungwa kwa msg_copy).
  • Mawasiliano ya Hiari ya Hali ya Ujumbe
    Mhusika yeyote anaweza kufahamisha upande mwingine kwa hiari kuhusu hali ya ujumbe unaotumwa na upande mwingine.
    msgs_all_info#8cc0d131 msg_ids:Vector long info:string = MsgsAllInfo
  • Mawasiliano Iliyoongezwa ya Hiari ya Hali ya Ujumbe Mmoja
    ...
    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;
  • Ombi la Dhahiri la Kutuma Ujumbe Upya
    msg_resend_req#7d861a08 msg_ids:Vector long = MsgResendReq;
    Chama cha mbali kinajibu mara moja kwa kutuma tena ujumbe ulioombwa […]
  • Ombi la Dhahiri la Kutuma Majibu Tena
    msg_resend_ans_req#8610baeb msg_ids:Vector long = MsgResendReq;
    Chama cha mbali kinajibu mara moja kwa kutuma tena majibu kwa ujumbe ulioombwa […]
  • Nakala za Ujumbe
    Katika hali zingine, ujumbe wa zamani ulio na msg_id ambao sio halali unahitaji kutumwa tena. Kisha, imefungwa kwenye chombo cha nakala:
    msg_copy#e06046b2 orig_message:Message = MessageCopy;
    Mara baada ya kupokea, ujumbe huchakatwa kana kwamba kanga haipo. Hata hivyo, ikiwa inajulikana kwa hakika kwamba ujumbe orig_message.msg_id ulipokelewa, basi ujumbe mpya haujachakatwa (wakati huo huo, ni na orig_message.msg_id zinakubaliwa). Thamani ya orig_message.msg_id lazima iwe chini ya msg_id ya chombo.

Hata tunyamaze nini msgs_state_info tena masikio ya TL ambayo haijakamilika yanajitokeza (tulihitaji vector ya byte, na katika bits mbili za chini kulikuwa na enum, na katika bits mbili za juu kulikuwa na bendera). Hatua ni tofauti. Kuna mtu anaelewa kwa nini haya yote yanafanyika? katika mteja halisi ni lazima? .. Kwa shida, lakini mtu anaweza kufikiria faida fulani ikiwa mtu anahusika katika kurekebisha, na katika hali ya maingiliano - kuuliza seva nini na jinsi gani. Lakini hapa maombi yanaelezwa safari ya kwenda na kurudi.

Inafuata kwamba kila chama lazima si tu kuficha na kutuma ujumbe, lakini pia kuhifadhi data kuhusu wao wenyewe, kuhusu majibu kwao, kwa muda usiojulikana. Nyaraka hazielezei nyakati au utumiaji wa vipengele hivi. hakuna njia. Kinachoshangaza zaidi ni kwamba zinatumika katika nambari za wateja rasmi! Inaonekana waliambiwa kitu ambacho hakikujumuishwa kwenye nyaraka za umma. Kuelewa kutoka kwa kanuni kwa nini, sio rahisi tena kama ilivyo kwa TL - sio (kiasi) sehemu iliyotengwa kimantiki, lakini kipande kilichofungwa kwenye usanifu wa maombi, i.e. itahitaji muda zaidi kuelewa msimbo wa programu.

Pings na nyakati. Foleni.

Kutoka kwa kila kitu, ikiwa tunakumbuka makisio juu ya usanifu wa seva (usambazaji wa maombi kwenye sehemu za nyuma), jambo la kusikitisha linafuata - licha ya dhamana zote za uwasilishaji katika TCP (ama data itawasilishwa, au utaarifiwa juu ya pengo, lakini data itawasilishwa kabla ya shida kutokea), kwamba uthibitisho katika MTProto yenyewe - hakuna dhamana. Seva inaweza kupoteza au kutupa ujumbe wako kwa urahisi, na hakuna kinachoweza kufanywa kuhusu hilo, tumia tu aina tofauti za magongo.

Na kwanza kabisa - foleni za ujumbe. Kweli, kwa jambo moja kila kitu kilikuwa dhahiri tangu mwanzo - ujumbe ambao haujathibitishwa lazima uhifadhiwe na upelekwe. Na baada ya saa ngapi? Na mcheshi anamjua. Labda ujumbe huo wa huduma ya uraibu kwa njia fulani hutatua shida hii na vijiti, sema, kwenye Desktop ya Telegraph kuna takriban foleni 4 zinazolingana nao (labda zaidi, kama ilivyotajwa tayari, kwa hili unahitaji kuzama ndani ya nambari na usanifu wake kwa umakini zaidi; wakati huo huo. wakati, sisi Tunajua kuwa haiwezi kuchukuliwa kama sampuli; idadi fulani ya aina kutoka kwa mpango wa MTProto haitumiki ndani yake).

Kwa nini hii inatokea? Pengine, watayarishaji programu wa seva hawakuweza kuhakikisha kutegemewa ndani ya nguzo, au hata kuakibisha kwenye sawazisha la mbele, na kuhamisha tatizo hili kwa mteja. Kwa kukata tamaa, Vasily alijaribu kutekeleza chaguo mbadala, na foleni mbili tu, kwa kutumia algorithms kutoka TCP - kupima RTT kwa seva na kurekebisha ukubwa wa "dirisha" (katika ujumbe) kulingana na idadi ya maombi ambayo hayajathibitishwa. Hiyo ni, mtazamo mbaya kama huo wa kutathmini mzigo wa seva ni maombi yetu mengi ambayo inaweza kutafuna kwa wakati mmoja na sio kupoteza.

Kweli, hiyo ni, unaelewa, sawa? Iwapo itabidi utekeleze TCP tena juu ya itifaki inayoendesha TCP, hii inaonyesha itifaki iliyoundwa vibaya sana.

Ndio, kwa nini unahitaji zaidi ya foleni moja, na hii inamaanisha nini kwa mtu anayefanya kazi na API ya kiwango cha juu? Angalia, unaomba ombi, ulisasishe, lakini mara nyingi huwezi kutuma mara moja. Kwa nini? Kwa sababu jibu litakuwa msg_id, ambayo ni ya mudaΠ°Mimi ni lebo, mgawo wake ambao ni bora kuahirishwa hadi kuchelewa iwezekanavyo - ikiwa seva itaikataa kwa sababu ya kutolingana kwa wakati kati yetu na yeye (bila shaka, tunaweza kutengeneza mkongojo ambao hubadilisha wakati wetu kutoka kwa sasa. kwa seva kwa kuongeza delta iliyokokotwa kutoka kwa majibu ya seva - wateja rasmi hufanya hivi, lakini ni ghafi na si sahihi kwa sababu ya kuakibisha). Kwa hivyo, unapofanya ombi na simu ya kazi ya ndani kutoka kwa maktaba, ujumbe hupitia hatua zifuatazo:

  1. Imewekwa kwenye foleni moja na inangoja usimbaji fiche.
  2. Imeteuliwa msg_id na ujumbe ulikwenda kwenye foleni nyingine - uwezekano wa usambazaji; tuma kwa tundu.
  3. a) Seva ilijibu MsgsAck - ujumbe uliwasilishwa, tunaufuta kutoka kwa "foleni nyingine".
    b) Au kinyume chake, hakupenda kitu, alijibu badmsg - tuma tena kutoka kwa "foleni nyingine"
    c) Hakuna kinachojulikana, ujumbe unahitaji kukasirishwa kutoka kwa foleni nyingine - lakini haijulikani ni lini haswa.
  4. Seva hatimaye ilijibu RpcResult - jibu halisi (au kosa) - sio tu iliyotolewa, lakini pia kusindika.

Labda, matumizi ya makontena yanaweza kutatua tatizo kwa sehemu. Hii ni wakati rundo la ujumbe huwekwa kwenye moja, na seva ikajibu kwa uthibitisho kwa zote mara moja, kwa moja. msg_id. Lakini pia atakataa pakiti hii, ikiwa kitu kilikwenda vibaya, kwa ukamilifu.

Na katika hatua hii masuala yasiyo ya kiufundi yanaingia. Kutokana na uzoefu, tumeona magongo mengi, na kwa kuongeza, sasa tutaona mifano zaidi ya ushauri mbaya na usanifu - katika hali hiyo, ni thamani ya kuamini na kufanya maamuzi hayo? Swali ni balagha (bila shaka sivyo).

Tunazungumzia nini? Ikiwa kwenye mada ya "ujumbe wa dawa za kulevya kuhusu ujumbe" bado unaweza kubashiri kwa pingamizi kama vile "wewe ni mjinga, hukuelewa mpango wetu mzuri!" (kwa hivyo andika nyaraka kwanza, kama watu wa kawaida wanapaswa, kwa busara na mifano ya kubadilishana pakiti, kisha tutazungumza), kisha muda / muda wa kumaliza ni swali la vitendo na maalum, kila kitu hapa kimejulikana kwa muda mrefu. Nyaraka zinatuambia nini kuhusu kuisha kwa muda?

Seva kwa kawaida hukubali kupokea ujumbe kutoka kwa mteja (kawaida, swala la RPC) kwa kutumia jibu la RPC. Ikiwa jibu linakuja kwa muda mrefu, seva inaweza kwanza kutuma uthibitisho wa risiti, na kwa kiasi fulani baadaye, jibu la RPC lenyewe.

Mteja kwa kawaida hukubali kupokea ujumbe kutoka kwa seva (kawaida, jibu la RPC) kwa kuongeza uthibitisho kwa swali linalofuata la RPC ikiwa haujatumwa kwa kuchelewa (ikiwa umetolewa, sema, sekunde 60-120 baada ya kupokelewa. ya ujumbe kutoka kwa seva). Hata hivyo, ikiwa kwa muda mrefu hakuna sababu ya kutuma ujumbe kwa seva au ikiwa kuna idadi kubwa ya ujumbe usiokubaliwa kutoka kwa seva (sema, zaidi ya 16), mteja hupeleka kukiri kwa kujitegemea.

... Ninatafsiri: sisi wenyewe hatujui ni kiasi gani na jinsi tunavyohitaji, basi hebu tuchukue kwamba basi iwe hivi.

Na kuhusu pings:

Ujumbe wa Ping (PING/PONG)

ping#7abe77ec ping_id:long = Pong;

Jibu kawaida hurejeshwa kwa muunganisho sawa:

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

Ujumbe huu hauhitaji shukrani. Pong hupitishwa tu kwa kukabiliana na ping wakati ping inaweza kuanzishwa na pande zote mbili.

Kufungwa kwa Muunganisho Ulioahirishwa + PING

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

Inafanya kazi kama ping. Zaidi ya hayo, baada ya hili kupokelewa, seva itaanzisha kipima muda ambacho kitafunga muunganisho wa sasa wa disconnect_delay sekunde baadaye isipokuwa ipokee ujumbe mpya wa aina sawa ambao huweka upya kipima muda kiotomatiki. Ikiwa mteja atatuma ping hizi mara moja kila sekunde 60, kwa mfano, inaweza kuweka disconnect_delay sawa na sekunde 75.

Una wazimu?! Katika sekunde 60, treni itaingia kwenye kituo, kushuka na kuchukua abiria, na tena kupoteza mawasiliano kwenye handaki. Baada ya sekunde 120, unapoisikia, itafika kwenye nyingine, na kuna uwezekano mkubwa wa kuunganisha. Kweli, ni wazi ambapo miguu inatoka - "Nilisikia mlio, lakini sijui ni wapi", kuna algorithm ya Nagl na chaguo la TCP_NODELAY, iliyokusudiwa kwa kazi ya maingiliano. Lakini, samahani, shikilia dhamana yake ya msingi - 200 Millisekunde Ikiwa ungependa kuonyesha kitu sawa na kuhifadhi kwenye pakiti kadhaa zinazowezekana, basi ziweke kwa sekunde 5, au chochote kile "Mtumiaji anaandika..." muda wa ujumbe umeisha sasa. Lakini hakuna zaidi.

Na hatimaye, pings. Hiyo ni, kuangalia uhai wa muunganisho wa TCP. Inachekesha, lakini kama miaka 10 iliyopita niliandika maandishi muhimu juu ya mjumbe wa chumba cha kulala cha kitivo chetu - waandishi huko pia waliweka seva kutoka kwa mteja, na sio kinyume chake. Lakini wanafunzi wa mwaka wa 3 ni jambo moja, na ofisi ya kimataifa ni nyingine, sivyo?

Kwanza, mpango mdogo wa elimu. Uunganisho wa TCP, bila kutokuwepo kwa kubadilishana pakiti, unaweza kuishi kwa wiki. Hii ni nzuri na mbaya, kulingana na kusudi. Ni vizuri ikiwa ulikuwa na muunganisho wa SSH wazi kwa seva, uliinuka kutoka kwa kompyuta, ukawasha tena kipanga njia, ukarudi mahali pako - kikao kupitia seva hii hakikuchanika (haukuandika chochote, hapakuwa na pakiti) , ni rahisi. Ni mbaya ikiwa kuna maelfu ya wateja kwenye seva, kila mmoja akichukua rasilimali (hujambo, Postgres!), na mwenyeji wa mteja anaweza kuwa amewasha tena muda mrefu uliopita - lakini hatutajua kuihusu.

Mifumo ya gumzo/IM huangukia katika hali ya pili kwa sababu moja ya ziada - hali za mtandaoni. Ikiwa mtumiaji "alianguka", unahitaji kuwajulisha waingiliaji wake kuhusu hili. Vinginevyo, utaishia na kosa ambalo waundaji wa Jabber walifanya (na kusahihishwa kwa miaka 20) - mtumiaji amekata mawasiliano, lakini wanaendelea kumwandikia ujumbe, wakiamini kuwa yuko mtandaoni (ambayo pia ilipotea kabisa katika haya. dakika chache kabla ya kukatwa kugunduliwa). Hapana, chaguo la TCP_KEEPALIVE, ambalo watu wengi ambao hawaelewi jinsi vipima saa vya TCP hufanya kazi hutupa kwa nasibu (kwa kuweka maadili ya pori kama makumi ya sekunde), haitasaidia hapa - unahitaji kuhakikisha kuwa sio tu kernel ya OS. ya mashine ya mtumiaji iko hai, lakini pia inafanya kazi kwa kawaida, katika uwezo wa kujibu, na programu yenyewe (unafikiri haiwezi kugandisha? Eneo-kazi la Telegramu kwenye Ubuntu 18.04 iliganda kwa ajili yangu zaidi ya mara moja).

Ndiyo sababu unapaswa kupiga ping seva mteja, na si kinyume chake - ikiwa mteja anafanya hivi, ikiwa uunganisho umevunjika, ping haitatolewa, lengo halitapatikana.

Tunaona nini kwenye Telegraph? Ni kinyume kabisa! Naam, hiyo ni. Rasmi, bila shaka, pande zote mbili zinaweza kupigia kila mmoja. Kwa mazoezi, wateja hutumia mkongojo ping_delay_disconnect, ambayo huweka kipima saa kwenye seva. Naam, samahani, sio kwa mteja kuamua muda gani anataka kuishi huko bila ping. Seva, kulingana na mzigo wake, inajua vizuri zaidi. Lakini, bila shaka, ikiwa hujali rasilimali, basi utakuwa Pinocchio yako mwenyewe mbaya, na crutch itafanya ...

Je, ilipaswa kuundwa vipi?

Ninaamini kwamba ukweli hapo juu unaonyesha wazi kabisa uwezo usio wa juu sana wa timu ya Telegram / VKontakte katika uwanja wa usafiri (na wa chini) wa kiwango cha mitandao ya kompyuta na sifa zao za chini katika masuala husika.

Kwa nini iligeuka kuwa ngumu sana, na wasanifu wa Telegraph wanawezaje kujaribu kupinga? Ukweli kwamba walijaribu kufanya kikao ambacho kinasalia mapumziko ya uunganisho wa TCP, yaani, kile ambacho hakijatolewa sasa, tutatoa baadaye. Labda pia walijaribu kutengeneza usafiri wa UDP, lakini walipata shida na kuiacha (ndiyo sababu hati hazina kitu - hakukuwa na kitu cha kujivunia). Lakini kwa sababu ya ukosefu wa ufahamu wa jinsi mitandao kwa ujumla na TCP inavyofanya kazi haswa, ambapo unaweza kuitegemea, na wapi unahitaji kuifanya mwenyewe (na jinsi), na jaribio la kuchanganya hii na cryptography "ndege wawili na jiwe moja”, haya ndiyo matokeo.

Ilikuwaje lazima? Kulingana na ukweli kwamba msg_id ni muhuri wa muda unaohitajika kutoka kwa mtazamo wa kriptografia ili kuzuia mashambulizi ya kucheza tena, ni makosa kuambatisha kitendakazi cha kipekee cha kitambulisho kwake. Kwa hivyo, bila kubadilisha kimsingi usanifu wa sasa (wakati mkondo wa Sasisho unatolewa, hiyo ni mada ya kiwango cha juu cha API kwa sehemu nyingine ya safu hii ya machapisho), mtu angehitaji:

  1. Seva iliyoshikilia muunganisho wa TCP kwa mteja inachukua jukumu - ikiwa imesoma kutoka kwenye soketi, tafadhali kubali, uchakata au urejeshe hitilafu, hakuna hasara. Halafu uthibitisho sio vekta ya vitambulisho, lakini kwa urahisi "ya mwisho kupokea seq_no" - nambari tu, kama ilivyo kwa TCP (nambari mbili - seq yako na iliyothibitishwa). Daima tuko ndani ya kikao, sivyo?
  2. Muhuri wa muda wa kuzuia mashambulizi ya kucheza tena inakuwa uwanja tofauti, la nonce. Inakaguliwa, lakini haiathiri kitu kingine chochote. Kutosha na uint32 - ikiwa chumvi yetu inabadilika angalau kila nusu ya siku, tunaweza kutenga bits 16 kwa bits za mpangilio wa chini wa sehemu kamili ya wakati wa sasa, iliyobaki - kwa sehemu ndogo ya sekunde (kama sasa).
  3. Imeondolewa msg_id kabisa - kutoka kwa mtazamo wa kutofautisha maombi kwenye sehemu za nyuma, kuna, kwanza, kitambulisho cha mteja, na pili, kitambulisho cha kikao, unganisha. Ipasavyo, kitu kimoja tu kinatosha kama kitambulisho cha ombi seq_no.

Hili pia sio chaguo lililofanikiwa zaidi; nasibu kamili inaweza kutumika kama kitambulisho - hii tayari imefanywa katika API ya kiwango cha juu wakati wa kutuma ujumbe, kwa njia. Ingekuwa bora kurekebisha usanifu kabisa kutoka kwa jamaa hadi kabisa, lakini hii ni mada ya sehemu nyingine, sio chapisho hili.

API?

Ta-daam! Kwa hivyo, baada ya kujitahidi kupitia njia iliyojaa maumivu na vijiti, hatimaye tuliweza kutuma maombi yoyote kwa seva na kupokea majibu yoyote kwao, na pia kupokea sasisho kutoka kwa seva (sio kwa kujibu ombi, lakini yenyewe. inatutumia, kama PUSH, ikiwa kuna mtu yeyote ni wazi zaidi kwa njia hiyo).

Makini, sasa kutakuwa na mfano pekee katika Perl katika makala! (kwa wale ambao hawajui syntax, hoja ya kwanza ya baraka ni muundo wa data ya kitu, ya pili ni darasa lake):

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

Ndio, sio mharibifu kwa makusudi - ikiwa bado haujaisoma, endelea na uifanye!

Oh, wai~~... hii inaonekanaje? Kitu kinachojulikana sana... labda huu ni muundo wa data wa API ya Wavuti ya kawaida katika JSON, isipokuwa kwamba madarasa pia yameambatishwa kwa vitu? ..

Kwa hivyo ndivyo inavyotokea... Inahusu nini, wandugu?.. Juhudi nyingi - na tulisimama kupumzika ambapo waandaaji wa programu za Wavuti. tu kuanza?...Je, JSON juu ya HTTPS haingekuwa rahisi zaidi?! Tulipata nini kwa kubadilishana? Je, jitihada hiyo ilistahili?

Wacha tutathmini kile TL+MTProto kilitupa na ni njia gani mbadala zinazowezekana. Kweli, HTTP, ambayo inazingatia mfano wa majibu ya ombi, inafaa vibaya, lakini angalau kitu juu ya TLS?

Kusasisha kompakt. Kuona muundo huu wa data, sawa na JSON, nakumbuka kuwa kuna matoleo yake ya binary. Wacha tuweke alama MsgPack kama isiyoweza kupanuka vya kutosha, lakini kuna, kwa mfano, CBOR - kwa njia, kiwango kilichoelezewa katika RFC 7049. Inajulikana kwa ukweli kwamba inafafanua vitambulisho, kama utaratibu wa upanuzi, na kati ya tayari sanifu inapatikana:

  • 25 + 256 - kuchukua nafasi ya mistari iliyorudiwa na kumbukumbu ya nambari ya mstari, njia ya bei nafuu ya compression.
  • 26 - kitu cha serialized Perl na jina la darasa na hoja za wajenzi
  • 27 - kitu kisichojitegemea cha lugha kilicho na jina la aina na hoja za wajenzi

Kweli, nilijaribu kusawazisha data sawa katika TL na katika CBOR na kamba na upakiaji wa kitu kuwezeshwa. Matokeo yalianza kutofautiana kwa kupendelea CBOR mahali fulani kutoka kwa megabyte:

cborlen=1039673 tl_len=1095092

Hivyo, hitimisho: Kuna miundo rahisi zaidi ambayo haiko chini ya tatizo la kutofaulu kwa ulandanishi au kitambulishi kisichojulikana, chenye ufanisi kulinganishwa.

Uanzishaji wa uunganisho wa haraka. Hii inamaanisha sifuri RTT baada ya kuunganishwa tena (wakati ufunguo tayari umetolewa mara moja) - inatumika kutoka kwa ujumbe wa kwanza wa MTProto, lakini kwa kutoridhishwa kidogo - piga chumvi sawa, kikao hakijaoza, nk. Je, TLS inatupa nini badala yake? Nukuu juu ya mada:

Unapotumia PFS katika TLS, tikiti za kikao cha TLS (RFC 5077) ili kuanza tena kipindi kilichosimbwa kwa njia fiche bila funguo za mazungumzo tena na bila kuhifadhi habari muhimu kwenye seva. Wakati wa kufungua muunganisho wa kwanza na kuunda vitufe, seva husimba hali ya muunganisho kwa njia fiche na kuisambaza kwa mteja (katika mfumo wa tikiti ya kipindi). Ipasavyo, muunganisho unaporejeshwa, mteja hutuma tikiti ya kipindi, ikijumuisha ufunguo wa kipindi, kurudi kwa seva. Tikiti yenyewe imesimbwa kwa njia fiche kwa ufunguo wa muda (ufunguo wa tikiti ya kipindi), ambao huhifadhiwa kwenye seva na lazima isambazwe kati ya seva zote za mbele zinazochakata SSL katika suluhu zilizounganishwa.[10]. Kwa hivyo, utangulizi wa tikiti ya kipindi unaweza kukiuka PFS ikiwa funguo za seva za muda zimeathiriwa, kwa mfano, zinapohifadhiwa kwa muda mrefu (OpenSSL, nginx, Apache huzihifadhi kwa chaguo-msingi kwa muda wote wa programu; tovuti maarufu hutumia. ufunguo kwa masaa kadhaa, hadi siku).

Hapa RTT sio sifuri, unahitaji kubadilishana angalau ClientHello na ServerHello, baada ya hapo mteja anaweza kutuma data pamoja na Finished. Lakini hapa tunapaswa kukumbuka kuwa hatuna Wavuti, na rundo lake la viunganisho vipya vilivyofunguliwa, lakini mjumbe, muunganisho wake ambao mara nyingi ni moja na zaidi au chini ya muda mrefu, maombi mafupi kwa kurasa za Wavuti - kila kitu kimeongezwa. ndani. Hiyo ni, inakubalika ikiwa hatukukutana na sehemu mbaya ya treni ya chini ya ardhi.

Umesahau kitu kingine? Andika kwenye maoni.

Ili kuendelea!

Katika sehemu ya pili ya mfululizo huu wa machapisho tutazingatia sio kiufundi, lakini masuala ya shirika - mbinu, itikadi, interface, mtazamo kwa watumiaji, nk. Kulingana, hata hivyo, juu ya maelezo ya kiufundi ambayo yaliwasilishwa hapa.

Sehemu ya tatu itaendelea kuchanganua kipengele cha kiufundi/uzoefu wa ukuzaji. Utajifunza, haswa:

  • muendelezo wa pandemonium na aina mbalimbali za TL
  • mambo yasiyojulikana kuhusu chaneli na vikundi vikubwa
  • kwa nini mazungumzo ni mabaya kuliko orodha
  • kuhusu anwani ya ujumbe wa jamaa kabisa dhidi ya jamaa
  • kuna tofauti gani kati ya picha na picha
  • jinsi emoji inavyoingilia maandishi ya italiki

na magongo mengine! Endelea kufuatilia!

Chanzo: mapenzi.com

Kuongeza maoni