Протоколду жана Telegramдын уюштуруучулук мамилесин сынга алуу. 1-бөлүк, техникалык: нөлдөн баштап кардарды жазуу тажрыйбасы - TL, MT

Акыркы убакта Habré сайтында Telegram канчалык жакшы экени, бир тууган Дуровдор тармактык системаларды курууда канчалык мыкты жана тажрыйбалуу экени жана башкалар тууралуу посттор көп чыга баштады. Ошол эле учурда, өтө аз адамдар чындап эле техникалык түзүлүшкө чөмүлдүрүлгөн - көбүнчө, алар бир топ жөнөкөй (жана MTProtoдон такыр башкача) JSON негизиндеги Bot API колдонушат жана адатта жөн гана кабыл алышат. ишеним боюнча мессенджердин айланасында айланган бардык мактоо жана пиар. Дээрлик бир жарым жыл мурун, менин Эшелон бейөкмөт уюмундагы кесиптешим Василий (тилекке каршы, анын Хабредеги аккаунту долбоор менен кошо өчүрүлгөн) Перлде нөлдөн баштап өзүнүн Telegram кардарын жаза баштаган жана кийинчерээк бул саптардын автору кошулган. Эмне үчүн Перл, кээ бирлери дароо сурашат? Анткени мындай долбоорлор башка тилдерде бар.Чындыгында кеп бул эмес, жок жерде башка тил болушу мүмкүн. даяр китепкана, жана ошого жараша автор бардык жол менен барышы керек нөлдөн баштап. Анын үстүнө, криптография ишеним маселеси, бирок текшерүү. Коопсуздукка багытталган продукт менен сиз жөн гана өндүрүүчүнүн даяр китепканасына ишене албайсыз жана ага сокур ишене албайсыз (бирок бул экинчи бөлүктүн темасы). Учурда китепкана “орточо” деңгээлде жакшы иштейт (ар кандай API сурамдарын жасоого мүмкүндүк берет).

Бирок, бул посттордо криптография же математика көп болбойт. Бирок башка көптөгөн техникалык деталдар жана архитектуралык балдактар ​​болот (ошондой эле нөлдөн жазбай, китепкананы каалаган тилде колдоно тургандар үчүн да пайдалуу). Ошентип, негизги максат кардарды нөлдөн баштап ишке ашырууга аракет кылуу болду расмий документтерге ылайык. Башкача айтканда, расмий кардарлардын баштапкы коду жабык деп коёлу (кайра, экинчи бөлүктө биз бул чындык деген теманы кененирээк чагылдырабыз болот ошондой), бирок, эски күндөрдөгүдөй, мисалы, RFC сыяктуу стандарт бар - кардарды спецификацияга ылайык, баштапкы кодду "карабастан" жазууга болобу, расмий болобу (Telegram Desktop, мобилдик), же расмий эмес Telethon?

Мазмуну:

Документация... ал бар, туурабы? Чынбы?..

Бул макаланын фрагменттери өткөн жылдын жайында жыйнала баштаган. Мунун баары расмий сайтында https://core.telegram.org Документтер 23-кабатка карата болгон, б.а. 2014-жылы бир жерге тыгылып калган (эстесиңби, ал кезде каналдар да жок болчу?). Албетте, теориялык жактан алганда, бул бизге 2014-жылы ошол убактагы функционалдуу кардарды ишке ашырууга мүмкүндүк бериши керек болчу. Бирок мындай абалда деле документация биринчиден, толук эмес, экинчиден, айрым жерлерде өзүнө карама-каршы келген. Болгону бир айдан ашык убакыт мурун, 2019-жылы сентябрда болгон кокустан Бул толугу менен акыркы Layer 105 үчүн сайтта документтердин ири жаңыртуу бар экени аныкталды, азыр баарын кайра окуу керек деген эскертүү менен. Чынында эле, көптөгөн беренелер кайра каралып чыккан, бирок көбү өзгөрүүсүз калган. Ошондуктан, төмөндөгү документтер жөнүндө сынды окуп жатканда, бул нерселердин кээ бирлери актуалдуу эмес, бирок кээ бирлери дагы эле актуалдуу экенин эстен чыгарбоо керек. Анткени, азыркы дүйнөдө 5 жыл жөн эле көп убакыт эмес, бирок абдан көп. Ошол убактан бери (айрыкча, ошондон бери ташталган жана кайра жанданган геочат сайттарын эске албасаңыз), схемадагы API ыкмаларынын саны жүздөн эки жүз элүүдөн ашкан!

Жаш жазуучу катары эмнеден баштоо керек?

Сиз нөлдөн баштап жазасызбы же жокпу, маанилүү эмес, мисалы, даяр китепканалар сыяктуу Python үчүн телетон же PHP үчүн Madeline, кандай болгон күндө да, биринчи керек болот арызыңызды каттаңыз - параметрлерди алуу api_id и api_hash (VKontakte API менен иштегендер дароо түшүнүшөт) сервер аркылуу колдонмону аныктайт. Бул керек муну юридикалык себептерден улам жаса, бирок биз китепкананын авторлору эмне үчүн аны экинчи бөлүктө жарыялай албастыгы жөнүндө көбүрөөк сүйлөшөбүз. Сиз тесттик баалуулуктарга ыраазы болушуңуз мүмкүн, бирок алар өтө чектелүү болсо да - чындыгында сиз азыр каттала аласыз бир гана колдонмо, андыктан ага шашылбаңыз.

Эми, техникалык көз караштан алганда, биз каттоодон өткөндөн кийин биз Telegramдан документацияга, протоколго ж. Башкача айтканда, док менен сайт жөн эле таштап кеткен жана кардарларды жасай баштагандар менен атайын иштөөнү уланткан деп божомолдоого болот, анткени бул жеңилирээк. Бирок жок, андай эч нерсе байкалган жок, маалымат келген жок.

Эгерде сиз нөлдөн баштап жазсаңыз, анда алынган параметрлерди колдонуу чындыгында дагы эле алыс. Бирок https://core.telegram.org/ жана алар жөнүндө биринчи кезекте Баштоо бөлүмүндө айтылат, чындыгында, сиз адегенде ишке ашырышыңыз керек MTProto протоколу - бирок ишенсеңер OSI моделине ылайык жайгаштыруу протоколдун жалпы сүрөттөлүшү үчүн беттин аягында, анда ал толугу менен бекер.

Чынында, MTProtoго чейин да, андан кийин да, бир эле учурда бир нече деңгээлде (OS ядросунда иштеген чет элдик сетевойлор айткандай, катмардын бузулушу), чоң, азаптуу жана коркунучтуу тема тоскоол болот ...

Бинардык сериялаштыруу: TL (Type Language) жана анын схемасы, катмарлары жана башка көптөгөн коркунучтуу сөздөр

Бул тема, чындыгында, Telegram көйгөйлөрүнүн ачкычы болуп саналат. А эгер сиз ага тереңдеп кирүүгө аракет кылсаңыз, көптөгөн коркунучтуу сөздөр болот.

Ошентип, бул жерде диаграмма. Ушул сөз оюңа келсе, айт, JSON схемасы, Туура ойлодуң. Максаты бир: берилүүчү маалыматтардын мүмкүн болгон топтомун сүрөттөө үчүн кандайдыр бир тил. Окшоштуктар ушул жерден бүтөт. Барактан болсо MTProto протоколу, же расмий кардардын булак дарагынан биз схеманы ачууга аракет кылабыз, биз төмөнкүдөй нерсени көрөбүз:

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

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

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

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

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

---functions---

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

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

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

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

Муну биринчи жолу көргөн адам интуитивдик түрдө жазылгандын бир бөлүгүн гана тааный алат - бул, сыягы, структуралар (бирок аты кайда, сол жактабы же оң жактабы?), аларда талаалар бар, андан кийин жоон ичегиден кийин тип келет... кыязы. Бул жерде бурчтуу кашааларда C++ тилиндегидей калыптар бар болушу мүмкүн (чынында, жок эле). Ал эми башка бардык символдор эмнени билдирет, суроо белгилери, илеп белгилери, пайыздар, хэш белгилери (жана алар ар кандай жерлерде ар кандай нерселерди билдирет), кээде бар, кээде жок, он алтылык сандар - жана эң негизгиси, мындан кантип алууга болот туура (бул сервер тарабынан четке кагылбайт) байт агымы? Сиз документтерди окушуңуз керек (ооба, жакын жерде JSON версиясында схемага шилтемелер бар - бирок бул аны ачык-айкын кылбайт).

Баракты ач Binary Data Serialization жана козу карындардын сыйкырдуу дүйнөсүнө жана дискреттик математикага сүңгүп, 4-курстагы матанга окшош. Алфавит, тип, маани, комбинатор, функционалдык комбинатор, нормалдуу форма, курама тип, полиморфтук тип... жана мунун баары биринчи бет! Кийинки сизди күтөт TL тили, ал буга чейин эле арзыбаган суроо-талаптын жана жооптун мисалын камтыса да, типтүү учурларга такыр жооп бербейт, демек, орус тилинен англис тилине которулган математиканы дагы сегиз кыналган боюнча өтүүгө туура келет. беттер!

Функционалдык тилдер жана автоматтык түрдөгү тыянак менен тааныш окурмандар, албетте, бул тилдеги сыпаттоо тилин, атүгүл мисалдан да, алда канча тааныш болуп көрүшөт жана бул иш жүзүндө жаман эмес деп айта алышат. Буга каршы болгондор:

  • Ооба, цель жакшы угулат, бирок тилекке каршы, ал жетишилген эмес
  • Россиянын ЖОЖдорунда билим IT адистиктер арасында да айырмаланат - баары эле тиешелүү курсту өткөн эмес
  • Акыр-аягы, биз көрүп тургандай, иш жүзүндө ошондой кереги жок, анткени сүрөттөлгөн TLдин чектелген бөлүгү гана колдонулат

Жатып, мындай деди: LeoNerd каналында #perl FreeNode IRC тармагында, алар Telegramдан Matrixка дарбазаны ишке ашырууга аракет кылышкан (цитатанын котормосу эс тутумдан так эмес):

Кимдир бирөө типтин теориясы менен биринчи жолу таанышып, толкунданып, аны менен ойноого аракет кылып баштагандай сезилет, ал практикада керекпи же жокпу, чындап эле ойлонбой.

Өзүңүз карап көрүңүз, эгерде жылаңач типтердин (int, long, ж. вектор. Бул, чынында, массив, эгерде сиз пайда болгон нерселерди өз аттары менен атасаңыз.

Бирок мурун

Расмий документтерди окубагандар үчүн TL синтаксисинин кыскача сүрөттөмөсү

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

fixed#abcdef34 id:int = Type2;

fixedVec set:Vector<Type2> = FixedVec;

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

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

Аныктоо дайыма башталат дизайнер аял, андан кийин ыктыярдуу түрдө (иш жүзүндө - ар дайым) символ аркылуу # болушу керек CRC32 ушул типтеги нормалдаштырылган сүрөттөмө сабынан. Андан кийин талаалардын сүрөттөлүшү келет; алар бар болсо, түрү бош болушу мүмкүн. Мунун баары бирдей белги менен аяктайт, бул конструктор таандык болгон типтин аталышы - башкача айтканда, субтип -. Теңдик белгисинин оң жагындагы жигит полиморфтук - башкача айтканда, ага бир нече конкреттүү түрлөрү туура келиши мүмкүн.

Эгерде аныктама саптан кийин пайда болсо ---functions---, анда синтаксис өзгөрүүсүз калат, бирок мааниси башкача болот: конструктор RPC функциясынын аталышы болуп калат, талаалар параметрлерге айланат (жакшы, б.а., төмөндө сүрөттөлгөндөй, так ошол эле түзүм бойдон калат) , бул жөн гана берилген маани болот) жана "полиморфтук тип" - кайтарылган натыйжанын түрү. Ырас, ал дагы эле полиморфтук бойдон кала берет - жөн гана бөлүмдө аныкталган ---types---, бирок бул конструктор "каралбайт". Чакырылган функциялардын түрлөрүн аргументтери боюнча ашыкча жүктөө, б.а. Эмнегедир С++ тилдериндегидей ат менен, бирок кол тамгалары ар башка болгон бир нече функциялар TLде каралган эмес.

Эгерде ал OOP болбосо, эмне үчүн "конструктор" жана "полиморфтук"? Чынында, кимдир бирөө бул жөнүндө OOP терминдеринде ойлонуу оңой болот - абстракттуу класс катары полиморфтук тип, ал эми конструкторлор анын түздөн-түз тукум класстары жана final бир катар тилдердин терминологиясында. Чынында, албетте, бул жерде гана окшоштук OO программалоо тилдеринде реалдуу ашыкча конструктор ыкмалары менен. Бул жерде жөн гана маалымат структуралары болгондуктан, эч кандай методдор жок (бирок, функциялардын жана методдордун сүрөттөлүшү алардын бар экенине байланыштуу баш аламандыктарды жаратууга толук жөндөмдүү, бирок бул башка маселе) - конструкторду төмөнкү мааниден баалуулук катары ойлосоңуз болот. кайсы курулуп жатат байт агымын окуп жатканда териңиз.

Бул кантип болот? Ар дайым 4 байт окуган deserializer маанисин көрөт 0xcrc32 - жана андан ары эмне болорун түшүнөт field1 түрү менен int, б.а. так 4 байт окуйт, бул боюнча түрү менен үстүнкү талаа PolymorType окуу. Көрөт 0x2crc32 жана андан ары эки талаа бар экенин түшүнөт, биринчи long, бул биз 8 байт окуйбуз дегенди билдирет. Анан дагы ошол эле жол менен сериядан ажыратылган татаал түрү. Мисалы, Type3 Тиешелүүлүгүнө жараша эки конструктор катары схемада жарыяланышы мүмкүн, анда алар бири-бирине жолугууга тийиш 0x12abcd34, андан кийин дагы 4 байт окуу керек intже 0x6789cdef, андан кийин эч нерсе болбойт. Башка нерсе - сиз өзгөчө ыргытышыңыз керек. Эмнеси болсо да, мындан кийин биз 4 байт окууга кайтып барабыз int талаалар field_c в constructorTwo ушуну менен биз биздин окууну бүтүрөбүз PolymorType.

Акыры, эгер кармалып калсаң 0xdeadcrc үчүн constructorThree, анда баары татаал болуп калат. Биздин биринчи талаабыз bit_flags_of_what_really_present түрү менен # - чындыгында, бул түр үчүн жөн гана лакап ат nat, "табигый сан" дегенди билдирет. Башкача айтканда, чындыгында, unsigned int, демек, чыныгы схемаларда кол коюлбаган сандар кездешкен жалгыз учур. Ошентип, кийинки суроо белгиси бар конструкция, бул талаа - ал зымга тиешелүү бит айтылган талаага коюлганда гана болот (болжол менен үчтүк оператор сыяктуу). Ошентип, бул бит коюлган деп коёлу, демек, мындан ары биз сыяктуу талааны окуу керек Type, биздин мисалда 2 конструктор бар. Бири бош (идентификатордон гана турат), экинчисинде талаа бар ids түрү менен ids:Vector<long>.

Сиз шаблондор да, генериктер да профессионалдарда же Javaда деп ойлошуңуз мүмкүн. Бирок жок. Дээрлик. Бул гана реалдуу схемаларда бурчтуу кашааларды колдонуу учуру жана ал Вектор үчүн ГАНА колдонулат. Байт агымында булар Вектор түрүнүн өзү үчүн 4 CRC32 байт болот, ар дайым бирдей, андан кийин 4 байт - массив элементтеринин саны, анан бул элементтердин өздөрү.

Буга сериялаштыруу ар дайым 4 байт сөздөрдө пайда болоорун кошуңуз, бардык түрлөр ага эселенген - орнотулган түрлөрү да сүрөттөлөт bytes и string узундукту кол менен сериялаштыруу жана бул тегиздөө 4 менен - ​​бул нормалдуу жана ал тургай салыштырмалуу натыйжалуу окшойт? TL эффективдүү бинардык сериализация деп айтылып жатканына карабастан, алар менен тозокко түшүү үчүн, бардык нерсенин, атүгүл логикалык маанилердин жана бир символдук саптардын 4 байтка чейин кеңейиши менен, JSON дагы эле жоонраак болобу? Карачы, керексиз талааларды да бит желекчелери менен өткөрүп жиберсе болот, баары жакшы, жада калса келечек үчүн кеңейтилет, андыктан кийинчерээк конструкторго жаңы факультативдик талааларды кошууга болбойт?..

Бирок, жок, эгер сиз менин кыскача баяндамамды эмес, толук документацияны окуп чыгып, ишке ашыруу жөнүндө ойлонсоңуз. Биринчиден, конструктордун CRC32 схемасынын текст сыпаттамасынын нормалдаштырылган сызыгына ылайык эсептелет (кошумча боштуктарды алып салуу, ж.б.) - демек, жаңы талаа кошулса, типтин сүрөттөмө сабы өзгөрөт, демек, анын CRC32 жана , демек, сериялаштыруу. Ал эми эски кардар жаңы желектер коюлган талааны алса, анан эмне кыларын билбей, эмне кылат?..

Экинчиден, эстеп көрөлү CRC32, бул жерде негизинен катары колдонулат таштанды функциялары кандай түрүн (де) сериялаштырылып жатканын уникалдуу аныктоо. Бул жерде биз кагылышуу көйгөйүнө туш болуп жатабыз - жана жок, ыктымалдык 232де бир эмес, бирок андан да чоң. CRC32 байланыш каналындагы каталарды аныктоо (жана оңдоо) үчүн иштелип чыкканын жана ошого жараша башкалардын зыянына бул касиеттерди жакшыртаарын ким эстеди? Мисалы, байттарды кайра иретке салууга маани бербейт: эгерде сиз CRC32ди эки саптан эсептесеңиз, экинчисинде биринчи 4 байтты кийинки 4 байт менен алмаштырасыз - бул бирдей болот. Биздин киргизүү латын алфавитиндеги текст саптары (жана бир аз тыныш белгилери) болгондо жана бул аталыштар өзгөчө кокустук эмес болсо, мындай кайра түзүү ыктымалдыгы абдан жогорулайт.

Айтмакчы, ал жерде эмне бар экенин ким текшерди? чындыгында CRC32? Алгачкы баштапкы коддордун биринде (Уолтманга чейин да) хэш-функция болгон, ал ар бир символду 239 санына көбөйткөн, бул адамдар абдан жакшы көрүшөт, ха ха!

Акыры, макул, биз конструкторлордун талаа түрү бар экенин түшүндүк Vector<int> и Vector<PolymorType> ар кандай CRC32 болот. Онлайн аткаруу жөнүндө эмне айтууга болот? Ал эми теориялык көз караштан алганда, бул түрүнүн бир бөлүгү болуп калат? Биз он миң сандан турган массивден өттүк дейли Vector<int> баары түшүнүктүү, узундугу жана дагы 40000 XNUMX байт. Бул болсо эмне болот Vector<Type2>, ал бир гана талаадан турат int жана бул типте жалгыз - биз 10000xabcdef0 34 4 жолу, андан кийин XNUMX байт кайталашыбыз керекпи int, же тил аны биз үчүн конструктордон КӨЗ КАРАШТУУ кыла алат fixedVec жана 80000 40000 байттын ордуна кайра XNUMX XNUMX гана которушат?

Бул таптакыр бош теориялык суроо эмес – сиз топ колдонуучуларынын тизмесин алдыңыз деп элестетиңиз, алардын ар биринин идентификатору, аты, фамилиясы бар – мобилдик байланыш аркылуу берилүүчү маалыматтардын көлөмүндөгү айырма олуттуу болушу мүмкүн. Дал ушул Telegram сериализациясынын эффективдүүлүгү бизге жарнамаланат.

Демек, ...

Эч качан чыгарылган вектор

Эгерде сиз комбинаторлорду жана башкаларды сыпаттоо барактарын аралап өтүүгө аракет кылсаңыз, анда вектордун (жана матрицанын) формалдуу түрдө бир нече барактын кортеждери аркылуу чыгууга аракет кылып жатканын көрөсүз. Бирок, акыры алар унутуп, акыркы кадам өткөрүп жиберилет, ал эми вектордун аныктамасы жөн гана берилет, ал азырынча типке байланбайт. Эмне болду? тилдерде программалоо, өзгөчө функционалдык структураларды рекурсивдүү түрдө сүрөттөп берүү абдан мүнөздүү - компилятор өзүнүн жалкоо баалоосу менен баарын түшүнөт жана өзү кылат. Тилде маалыматтарды сериялаштыруу зарыл болгон нерсе НАТЫЙЖАЛУУ: жөн эле сүрөттөп коюу жетиштүү тизме, б.а. эки элементтин структурасы - биринчиси маалымат элементи, экинчиси - ошол эле структуранын өзү же куйрук үчүн бош орун (пакет) (cons) Лиспте). Бирок бул, албетте, талап кылат ар биринин элемент түрүн сүрөттөө үчүн кошумча 4 байтты (TLде CRC32) сарптайт. Массивди оңой эле сүрөттөсө болот белгиленген өлчөмү, бирок алдын ала белгисиз узундуктагы массивде биз үзөбүз.

Демек, TL векторду чыгарууга жол бербегендиктен, аны капталга кошуу керек болчу. Акыр-аягы, документтер мындай дейт:

Сериялаштыруу ар дайым t түрүндөгү өзгөрмөнүн өзгөчө маанисине көз каранды болбогон бир эле конструкторду колдонот (const 0x1cb5c415 = crc32(“вектор t:Тип # [ t ] = Vector t”).

Кошумча параметр t мааниси сериялаштырууга катышпайт, анткени ал натыйжанын түрүнөн алынган (сериялаштырууга чейин ар дайым белгилүү).

Жакшыраак карап көрүңүз: vector {t:Type} # [ t ] = Vector t - бирок эч жерде Бул аныктама өзү биринчи сан вектордун узундугуна барабар болушу керек деп айтпайт! Анан эч жерден келбейт. Бул эстен чыгарбоо жана колуңуз менен ишке ашыруу керек болгон нерсе. Башка жерде, документтер чынчылдык менен бул түрү реалдуу эмес деп айтылат:

Vector t полиморфтук псевдотипи "түрү" болуп саналат, анын мааниси кутучага салынган же жылаңач t түрүнүн маанилеринин ырааттуулугу.

... бирок ага көңүл бурбайт. Математиканы багынтуудан чарчаганыңызда (балким сизге университеттин курсунан да белгилүү) баш тартууну чечип, аны менен иш жүзүндө кантип иштөөнү карап чыгууну чечкениңизде, сиздин башыңызда бул олуттуу деген ой калган. Негизги математика, аны эч ким эмес, Cool People (эки математик - ACM жеңүүчүсү) ойлоп тапкан. Максат - көргөзүү - ишке ашты.

Айтмакчы, саны жөнүндө. Ошону эске сала кетели # бул синоним nat, натурал сан:

түрү туюнтмалар бар (type-expr) жана сандык туюнтмалар (nat-expr). Бирок, алар бир эле жол менен аныкталат.

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

бирок грамматикада алар бирдей сүрөттөлөт, б.а. Бул айырманы дагы бир жолу эстеп, кол менен ишке ашыруу керек.

Ооба, шаблон түрлөрү (vector<int>, vector<User>) жалпы идентификатору бар (#1cb5c415), б.а. деп жарыяланганын билсеңиз

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

анда сиз мындан ары жөн гана векторду эмес, колдонуучулардын векторун күтүп жатасыз. Тагыраак, керек күтө тургула - реалдуу коддо, ар бир элемент, эгерде жылаңач тип болбосо, конструктор болот жана ишке ашырууда жакшы жол менен текшерүү керек болот - бирок биз бул вектордун ар бир элементине так жөнөтүлгөнбүз. ошол түрү? Ал массив ар кандай элементтерде ар кандай типтерди камтышы мүмкүн болгон PHPдин кандайдыр бир түрү болсочу?

Ушул учурда сиз ойлоно баштайсыз - мындай TL керекпи? Балким, араба үчүн адамдын сериализаторун, ошол кездеги протобуфты колдонсо болот? Бул теория болчу, практиканы карап көрөлү.

Коддогу учурдагы TL ишке ашыруулары

TL Дуровдун үлүшүн сатуу менен атактуу окуяларга чейин ВКонтактенин тереңинде төрөлгөн жана (Акыйкатта,), Telegram иштеп баштаганга чейин эле. Жана ачык булакта биринчи ишке ашыруунун баштапкы коду көп күлкүлүү балдактарды таба аласыз. Ал эми тилдин өзү азыр Telegramдагыга караганда ал жерде толук ишке ашырылган. Мисалы, хэштер схемада такыр колдонулбайт (девианттык жүрүм-туруму бар орнотулган псевдотип (вектор сыяктуу) дегенди билдирет). Же

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

бирок ойдун гигантынын эволюциясын, мындайча айтканда, изилдвв учун, толуктугу учун карап чыгалы.

#define ZHUKOV_BYTES_HACK

#ifdef ZHUKOV_BYTES_HACK

/* dirty hack for Zhukov request */

Же бул сулуу:

    static const char *reserved_words_polymorhic[] = {

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

      };

Бул фрагмент төмөнкүдөй шаблондор жөнүндө:

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

Бул int - Type жуптарынын вектору катары хэшмап шаблон түрүнүн аныктамасы. C++ тилинде ал төмөнкүдөй көрүнөт:

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

Мына ушундай, alpha - ачкыч сөз! Бирок C++ тилинде гана T жаза аласыз, бирок альфа, бета деп жазышыңыз керек... Бирок 8 параметрден көп эмес, фантазия ушуну менен бүтөт. Качандыр бир кезде Санкт-Петербургда мындай диалогдор болгон окшойт:

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

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

Бирок бул "жалпысынан" TL биринчи жарыяланган ишке ашыруу жөнүндө болду. Келгиле, Telegram кардарларынын өзүндө ишке ашырууну карап көрөлү.

Василийге сөз:

Василий, [09.10.18 17:07] Баарынан да эшек ысык, анткени алар бир топ абстракцияларды жаратып, анан аларга болт уруп, код генераторун балдак менен жаап коюшкан.
Натыйжада, биринчи док pilot.jpg
Андан кийин dzhekichan.webp кодунан

Албетте, алгоритмдерди жана математиканы жакшы билген адамдардан алар Aho, Ullmannды окуп, DSL компиляторлорун жазуу үчүн ондогон жылдар бою тармакта де-факто стандартка айланган куралдар менен тааныш экенин күтсөк болот, туурабы? ..

By телеграмма-cli Виталий Валтман, TLO форматынын анын (cli) чегинен тышкары пайда болушунан көрүнүп тургандай, команданын мүчөсү - азыр TL талдоо үчүн китепкана бөлүнгөн өзүнчө, ал кандай таасир калтырды TL талдоочу? ..

16.12 04:18 Василий: Менимче, кимдир бирөө lex+yaccти өздөштүргөн жок
16.12 04:18 Василий: Башкача түшүндүрө албайм
16.12 04:18 Василий: жакшы, же алар ВКдагы саптардын саны үчүн төлөнгөнбү
16.12 04:19 Василий: 3k+ сызыктар ж.б.<censored> талдоочунун ордуна

Балким, өзгөчө? Келгиле, кантип карап көрөлү кылат Бул РАСМИЙ кардар - Telegram Desktop:

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

Pythonдо 1100+ саптар, бир нече туруктуу туюнтмалар + вектор сыяктуу өзгөчө учурлар, алар, албетте, схемада TL синтаксисине ылайык болушу керек деп жарыяланган, бирок алар аны талдоо үчүн ушул синтаксиске таянышкан... Суроо туулат, эмне үчүн мунун баары керемет болду?иЭч ким аны документацияга ылайык талдап чыкпаса, бул дагы катмарлуубу?!

Баса... Эсиңиздеби, биз CRC32 текшерүүсү жөнүндө сүйлөшкөнбүз? Ошентип, Telegram Desktop код генераторунда CRC32 эсептелген түрлөр үчүн өзгөчөлүктөрдүн тизмеси бар. дал келбейт диаграммада көрсөтүлгөн менен!

Василий, [18.12/22 49:XNUMX] жана бул жерде мен мындай ТЖ керекпи же жокпу деп ойлонот элем.
эгерде мен альтернативалуу ишке ашыруулар менен баш аламандык кылгым келсе, сызыктарды киргизе баштамакмын, талдоочулардын жарымы көп саптуу аныктамаларда бузулат
tdesktop, бирок, ошондой эле

Бир лайнер тууралуу ойду унутпаңыз, ага бир аздан кийин кайрылабыз.

Макул, telegram-cli расмий эмес, Telegram Desktop расмий, бирок башкалар жөнүндө эмне айтууга болот? Ким билет?.. Android кардар кодунда такыр схема талдоочу болгон эмес (бул ачык булак жөнүндө суроолорду жаратат, бирок бул экинчи бөлүк үчүн), бирок дагы бир нече күлкүлүү коддор бар болчу, бирок алар жөнүндө көбүрөөк маалымат төмөнкү бөлүм.

Сериялаштыруу иш жүзүндө дагы кандай суроолорду жаратат? Мисалы, алар бит талаалары жана шарттуу талаалар менен, албетте, көп нерселерди жасашты:

Василий: flags.0? true
талаа бар экенин жана желек коюлган болсо, чындыкка барабар экенин билдирет

Василий: flags.1? int
талаа бар экенин жана сериядан ажыратылышы керектигин билдирет

Василий: Эшек, эмне кылып жатканыңды ойлобо!
Василий: Документтин бир жеринде чындыктын жылаңач нөл узундуктагы түрү деп айтылат, бирок алардын документинен эч нерсе чогултуу мүмкүн эмес.
Василий: Ачык ишке ашырууда да андай нерсе жок, бирок балдак, таянычтар көп

Telethon жөнүндө эмне айтууга болот? MTProto темасын алдыга карап, мисалы - документацияда мындай бөлүктөр бар, бирок белги % ал "берилген жылаңач типке туура келген" деп гана сүрөттөлөт, б.а. төмөнкү мисалдарда ката же документсиз нерсе бар:

Василий, [22.06.18 18:38] Бир жерде:

msg_container#73f1f8dc messages:vector message = MessageContainer;

Башкача айтканда:

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

Жана бул эки чоң айырма, реалдуу жашоодо кандайдыр бир жылаңач вектор келет

Мен жылаңач вектордук аныктаманы көргөн жокмун жана бир да жолуккан жокмун

Анализ телемарафондо кол менен жазылат

Анын диаграммасында аныктама түшүндүрүлөт msg_container

Дагы, суроо % жөнүндө бойдон калууда. Бул сүрөттөлгөн эмес.

Вадим Гончаров, [22.06.18 19:22] жана tdesktop?

Василий, [22.06.18 19:23] Бирок кадимки кыймылдаткычтардагы TL талдоочу муну да жебейт.

// parsed manually

TL - бул сонун абстракция, аны эч ким толугу менен ишке ашырбайт

Жана % схеманын версиясында жок

Бирок бул жерде документация өзүнө карама-каршы келет, ошондуктан idk

Бул грамматикадан табылган, алар жөн гана семантиканы сүрөттөөнү унутуп коюшса болмок

Документти TLден көрдүңүз, жарым литрсиз түшүнө албайсыз

"Макул, айталы," дейт дагы бир окурман, "сен бир нерсени сындап жатасың, аны кантип кылуу керектигин көрсөт".

Василий мындай деп жооп берет: «Талдоочуга келсек, мага окшош нерселер жагат

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

караганда жакшыраак жакты

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

же

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

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

бул БҮТҮН лексире:

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

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

ошол. Жөнөкөйлүгү жумшак айтканда».

Жалпысынан, натыйжада, TLдин чындыгында колдонулган чакан топтому үчүн талдоочу жана код генератору болжол менен 100 грамматиканын саптарына жана генератордун ~300 саптарына туура келет (баарын эсепке алуу менен). printнын түзүлгөн коду), анын ичинде ар бир класста интроспекция үчүн маалымат булочкалары. Ар бир полиморфтук тип бош абстракттуу базалык класска айланат жана конструкторлор андан мурастап, сериялаштыруу жана сериядан чыгаруу ыкмаларына ээ.

Тип тилинде типтердин жоктугу

Күчтүү терүү жакшы нерсе, туурабы? Жок, бул холивар эмес (бирок мен динамикалык тилдерди жактырам), бирок TL алкагындагы постулат. Анын негизинде тил бизге ар кандай текшерүүлөрдү бериши керек. Ооба, макул, балким, ал өзү эмес, бирок ишке ашыруу, бирок, жок дегенде, аларды сүрөттөп бериши керек. Жана биз кандай мүмкүнчүлүктөрдү каалайбыз?

Биринчиден, чектөөлөр. Бул жерде биз файлдарды жүктөө үчүн документтерди көрөбүз:

Файлдын бинардык мазмуну андан кийин бөлүктөргө бөлүнөт. Бардык бөлүктөр бирдей өлчөмдө болушу керек ( бөлүк_размер ) жана төмөнкү шарттар аткарылышы керек:

  • part_size % 1024 = 0 (1КБга бөлүнөт)
  • 524288 % part_size = 0 (512KB part_size боюнча бирдей бөлүнүшү керек)

Акыркы бөлүк бул шарттарды аткарууга милдеттүү эмес, эгерде анын өлчөмү part_sizeден аз болсо.

Ар бир бөлүктүн катар номери болушу керек, file_part, мааниси 0дөн 2,999га чейин.

Файлды бөлгөндөн кийин, сиз аны серверде сактоо ыкмасын тандап алышыңыз керек. Колдонуу upload.saveBigFilePart файлдын толук көлөмү 10 МБ ашык болгон учурда жана upload.saveFilePart кичинекей файлдар үчүн.
[…] төмөнкү маалыматтарды киргизүү каталарынын бири кайтарылышы мүмкүн:

  • FILE_PARTS_INVALID — Бөлүктөрдүн саны жараксыз. Баасы ортосунда эмес 1..3000

Диаграммада мунун бирөө барбы? Бул TL аркылуу кандайдыр бир түрдө чагылдырылабы? Жок. Бирок, кечиресиз, атүгүл чоң атамдын Турбо Паскаль көрсөтүлгөн түрлөрүн сүрөттөп бере алган диапазондору. Ал эми дагы бир нерсени билген, азыр жакшыраак белгилүү enum - белгиленген (аз) сандагы маанилердин тизмесинен турган тип. C сыяктуу тилдерде - сандык, биз буга чейин бир гана түрлөрү жөнүндө сүйлөштүк сандар. Бирок массивдер, саптар да бар... мисалы, бул сап телефон номерин гана камтышы мүмкүн экенин сүрөттөп берсек жакшы болот эле, туурабы?

Булардын бири да ТТда жок. Бирок, мисалы, JSON схемасында бар. Эгер кимдир бирөө 512 КБнын бөлүнүүчүлүгү жөнүндө талаша турган болсо, бул дагы эле код менен текшерилиши керек, анда кардар жөн эле мүмкүн эмес диапазондон тышкары номер жөнөтүү 1..3000 (жана тиешелүү ката пайда болушу мүмкүн эмес) мүмкүн болмок, туурабы?..

Айтмакчы, каталар жана баалуулуктарды кайтаруу жөнүндө. TL менен иштегендер да көздөрүн бүдөмүктөшөт - бул бизге дароо эле түшүнгөн жок ар бир TLдеги функция иш жүзүндө сүрөттөлгөн кайтаруу түрүн гана эмес, катаны да кайтара алат. Бирок муну TLдин өзүн колдонуу менен кандайдыр бир түрдө чыгарууга болбойт. Албетте, бул түшүнүктүү жана иш жүзүндө эч нерсеге муктаж эмес (чындыгында, RPC ар кандай жолдор менен жасалышы мүмкүн, бирок биз буга кийинчерээк кайрылабыз) - бирок абстракттуу типтердин математикасынын түшүнүктөрүнүн тазалыгы жөнүндө эмне айтууга болот? бейиш дүйнөсүнөн?.. Түйшүктү көтөрдүм – дал кел.

Акыр-аягы, окулушу жөнүндө эмне айтууга болот? Ооба, ошол жерде, жалпысынан, мен каалайт элем баяндоо ал схемада туурабы (JSON схемасында, дагы эле ошондой), бирок сиз буга чейин эле кыйналып жатсаңыз, анда практикалык жагы жөнүндө эмне айтууга болот - жок дегенде жаңыртуулар учурунда айырмачылыктарды карап көрүү анча маанилүүбү? Өзүңүз караңыз реалдуу мисалдар:

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

же

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

Бул ар бир адамга көз каранды, бирок GitHub, мисалы, мындай узун саптардын ичиндеги өзгөрүүлөрдү баса көрсөтүүдөн баш тартат. "10 айырмачылыкты тап" оюну жана мээ дароо көргөн нерсе, эки мисалдын башталышы менен аягы бирдей экенин, ортодогу бир жерден тажатма окуу керек... Менимче, бул теориялык жактан эле эмес, бирок таза визуалдык кир жана шалаакы.

Айтмакчы, теориянын тазалыгы жөнүндө. Эмне үчүн бизге бит талаалары керек? Алар окшойт жыт тип теориясынын көз карашынан алганда жаманбы? Түшүндүрмө диаграмманын мурунку версияларында көрүүгө болот. Башында, ооба, ушундай болгон, ар бир чүчкүрүү үчүн жаңы түрү жаралган. Бул негиздер дагы эле ушул формада бар, мисалы:

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;

Бирок азыр элестетиңиз, эгер сиздин структураңызда 5 кошумча талаа бар болсо, анда бардык мүмкүн болгон варианттар үчүн 32 түр керек болот. Комбинатордук жарылуу. Ошентип, TL теориясынын кристаллдык тазалыгы сериалдаштыруунун катаал чындыгынын чоюн эшегине каршы дагы бир жолу талкаланды.

Мындан тышкары, кээ бир жерлерде бул балдар өздөрү өздөрүнүн типологиясын бузушат. Мисалы, MTProtoдо (кийинки бөлүмдө) жооп Gzip тарабынан кысылышы мүмкүн, баары жакшы - катмарлар жана схемалар бузулгандан башка. Дагы бир жолу, RpcResult өзү эмес, анын мазмуну жыйналды. Мейли, эмнеге мындай кыласың?.. Кысуу каалаган жерде иштесин деп балдак кесип салууга туура келди.

Же дагы бир мисал, биз бир жолу ката таптык - ал жөнөтүлдү InputPeerUser ордуна InputUser. Же тескерисинче. Бирок ал иштеди! Башкача айтканда, сервер түрүнө маани берген эмес. Бул кантип болушу мүмкүн? Жооп бизге 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);

Башкача айтканда, бул жерде сериализация жасалат КОЛ МЕНЕН, түзүлгөн код эмес! Балким сервер дагы ушундай жол менен ишке ашырылып жаткандыр?.. Негизи бул бир жолу жасалса иштейт, бирок кийинчерээк жаңыртуу учурунда аны кантип колдоого алса болот? Бул схема эмне үчүн ойлоп табылган? Бул жерде биз кийинки суроого өтөбүз.

Версиялоо. катмарлар

Эмне үчүн схемалык версиялар катмарлар деп аталат, бул жарыяланган схемалардын тарыхынын негизинде гана божомолдоого болот. Сыягы, адегенде авторлор негизги нерселерди өзгөртүүсүз схеманы колдонуу менен жасоого болот деп ойлошкон жана зарыл болгон учурда гана, конкреттүү суроо-талаптар үчүн, алар башка версияны колдонуу менен жасалып жатканын көрсөтүшөт. Негизи, ал тургай жакшы идея - жана жаңысы, эскинин үстүнө "аралаш" болуп калат. Бирок, анын кантип жасалганын карап көрөлү. Ырас, мен аны башынан эле карай алган жокмун - бул күлкүлүү, бирок негизги катмардын диаграммасы жок. Катмарлар 2 менен башталган. Документте TL өзгөчөлүгү жөнүндө айтылат:

Эгерде кардар 2-кабатты колдосо, анда төмөнкү конструктор колдонулушу керек:

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

Иш жүзүндө, бул ар бир API чалуусунун алдында мааниси менен int дегенди билдирет 0x289dd1f6 ыкманын номеринен мурун кошулушу керек.

Кадимки угулат. Бирок андан кийин эмне болду? Анан пайда болду

invokeWithLayer3#b7475268 query:!X = X;

Анан? Сиз ойлогондой,

invokeWithLayer4#dea0d430 query:!X = X;

Кызыкпы? Жок, күлүүгө али эрте, ошону ойлон ар бир башка катмардан келген суроо-талап ушундай өзгөчө түргө оролуу керек - эгерде алардын бардыгы сиз үчүн башкача болсо, аларды кантип айырмалай аласыз? Ал эми алдына болгону 4 байт кошуу абдан натыйжалуу ыкма. Ошентип,

invokeWithLayer5#417a57ae query:!X = X;

Бирок, бир аз убакыт өткөндөн кийин бул кандайдыр бир бакканалия болуп калаары анык. Ошондо чечим келди:

Жаңыртуу: 9-кабаттан баштап, жардамчы ыкмалары invokeWithLayerN менен бирге гана колдонсо болот initConnection

Жашасын! 9 версиядан кийин, биз акыры 80-жылдары Интернет протоколдорунда жасалган нерсеге келдик - туташуунун башында бир жолу версияга макул болдук!

Анан?..

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

Бирок азыр деле күлөсүң. Дагы 9 катмардан кийин гана версия номери бар универсалдуу конструктор кошулду, аны туташуунун башында бир гана жолу чакыруу керек, ал эми катмарлардын мааниси жоголуп кеткендей сезилди, эми бул жөн гана шарттуу версия, мисалы башка бардык жерде. Көйгөй чечилди.

Такпы?..

Василий, [16.07.18 14:01] Жума күнү да ойлодум:
Телесервер окуяларды сурамсыз жөнөтөт. Сурамдар InvokeWithLayer ичинде оролгон болушу керек. Сервер жаңыртууларды оробойт; жоопторду жана жаңыртууларды таңуу үчүн структура жок.

Ошол. кардар жаңыртууну каалаган катмарды көрсөтө албайт

Вадим Гончаров, [16.07.18 14:02] InvokeWithLayer негизи балдак эмеспи?

Василий, [16.07.18 14:02] Бул жалгыз жол

Вадим Гончаров, [16.07.18 14:02] бул сессиянын башталышында катмар боюнча макулдашууну билдирет

Айтмакчы, бул кардар төмөндөтүү камсыз кылынбайт

Жаңыртуулар, б.а. түрү Updates схемада бул сервер кардарга API сурамына жооп катары эмес, окуя болгондо өз алдынча жөнөтөт. Бул башка постто талкуулана турган татаал тема, бирок азырынча сервер жаңыртууларды кардар оффлайн режиминде болсо да сактай турганын билүү маанилүү.

Ошентип, ороп баш тартса ар биринин анын версиясын көрсөтүү үчүн пакет, логикалык жактан төмөнкү мүмкүн болгон көйгөйлөргө алып келет:

  • сервер жаңыртууларды кардар кайсы версияны колдой турганын билдире электе эле жөнөтөт
  • кардарды жаңырткандан кийин эмне кылышым керек?
  • ким кепилдиктерПроцесс учурунда сервердин катмардын номери тууралуу пикири өзгөрбөйт деп?

Сиздин оюңузча, бул жөн гана теориялык спекуляция жана иш жүзүндө мындай болушу мүмкүн эмес, анткени сервер туура жазылган (жок дегенде, ал жакшы сыналган)? Ха! Кандай болбосун!

Август айында дал ушул нерсеге туш болдук. 14-августта Telegram серверлеринде бир нерсе жаңыланып жатканы тууралуу билдирүүлөр пайда болду... анан журналдарда:

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

жана андан кийин бир нече мегабайт стек издери (жакшы, ошол эле учурда журнал жазуусу бекитилген). Акыры, эгер сиздин TLиңизде бир нерсе таанылбаса, анда ал кол коюу боюнча бинардык болуп саналат БААРЫ барат, декоддоо мүмкүн эмес болуп калат. Мындай кырдаалда эмне кылуу керек?

Ооба, кимдир бирөөнүн оюна келген биринчи нерсе - ажыратып, кайра аракет кылуу. Жардам берген жок. Биз Google CRC32 - булар 73-схемадагы объекттер болуп чыкты, бирок биз 82 боюнча иштедик. Биз журналдарды кылдаттык менен карайбыз - эки башка схеманын идентификаторлору бар!

Балким, маселе биздин расмий эмес кардарыбыздадыр? Жок, биз Telegram Desktop 1.2.17 (бир катар Linux дистрибуцияларында берилген версия) ишке киргизебиз, ал Exception журналына жазат: MTP Күтүлбөгөн түрү id #b5223b0f MTPMessageMediaда окулат…

Протоколду жана Telegramдын уюштуруучулук мамилесин сынга алуу. 1-бөлүк, техникалык: нөлдөн баштап кардарды жазуу тажрыйбасы - TL, MT

Google ушундай эле көйгөй бейрасмий кардарлардын биринде болгонун көрсөттү, бирок андан кийин версия номерлери жана, ошого жараша, божомолдор ар кандай болду...

Анда биз эмне кылышыбыз керек? Василий экөөбүз бөлүнүп кеттик: ал схеманы 91ге жаңыртууга аракет кылды, мен бир нече күн күтүп, 73тү сынап көрүүнү чечтим. Эки ыкма тең иштеди, бирок алар эмпирикалык болгондуктан, сизге канча өйдө же төмөн версиялар керек экенин түшүнгөн жок. секирүү, же канча күтүш керек.

Кийинчерээк мен кырдаалды кайталай алдым: биз кардарды ишке киргизебиз, аны өчүрөбүз, схеманы башка катмарга кайра компиляциялайбыз, кайра иштетебиз, көйгөйдү кайрадан чечебиз, мурункусуна кайтабыз - оо, схеманы которуунун саны жок жана кардар бир нече убакытка чейин кайра иштетилбейт. бир нече мүнөт жардам берет. Сиз ар кандай катмарлардан маалымат структураларынын аралашмасын аласыз.

Түшүндүрмө? Ар кандай кыйыр симптомдордон болжогондой, сервер ар кандай машиналарда ар кандай типтеги көптөгөн процесстерден турат. Сыягы, "буферлөө" үчүн жооптуу сервер, анын жетекчилери ага эмне берсе, кезекке койгон жана алар аны муун учурунда болгон схема боюнча беришкен. Ал эми бул кезек "чирип" чейин эч нерсе кыла алмак эмес.

Мүмкүн... бирок бул коркунучтуу балдак эмеспи?!.. Жок, жинди идеяларды ойлонуудан мурун, келгиле, расмий кардарлардын кодун карап көрөлү. Android версиясында биз эч кандай TL талдоочу таба албайбыз, бирок (де) сериялаштыруу менен чоң файлды табабыз (GitHub ага тийүүдөн баш тартат). Бул жерде код үзүндүлөрү:

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;

же

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

Ммм... жапайы көрүнөт. Бирок, балким, бул түзүлгөн код, анда макулбу?.. Бирок, албетте, бардык версияларды колдойт! Ырас, эмне үчүн баары аралашып кеткени, жашыруун баарлашуулар жана ар кандай нерселер түшүнүксүз _old7 эмнегедир машина муунуна окшобойт... Бирок, баарынан да мени таң калтырды

TL_message_layer104
TL_message_layer104_2
TL_message_layer104_3

Балдар, бир катмардын ичинде эмне бар экенин да чече албайсыңарбы?! Мейли, макул, “эки” ката менен чыгарылды дейли, ошондой болот, бирок ҮЧӨ?.. Ошол замат кайра баягы тырмоо? Бул кандай порнография, кечиресизби?..

Telegram Desktop булак кодунда, демекчи, ушуга окшош нерсе болот - эгер ошондой болсо, схемага катары менен бир нече милдеттенмелер анын катмарынын номерин өзгөртпөйт, бирок бир нерсени оңдошот. Схема боюнча расмий маалымат булагы жок шарттарда, расмий кардардын баштапкы кодун кошпогондо, аны кайдан алса болот? Жана эгер сиз аны ошол жерден алсаңыз, бардык ыкмаларды сынамайынча схеманын толук туура экенине ишене албайсыз.

Муну кантип сынаса болот? Бирдиктин, функционалдык жана башка тесттердин күйөрмандары комментарийлерде бөлүшөт деп үмүттөнөм.

Макул, келгиле коддун дагы бир бөлүгүн карап көрөлү:

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;

Бул "кол менен түзүлгөн" комментарий бул файлдын бир гана бөлүгү кол менен жазылганын (сиз бүтүндөй тейлөө коркунучун элестете аласызбы?), ал эми калганы машинада түзүлгөнүн көрсөтүп турат. Бирок, андан кийин дагы бир суроо туулат - булактары бар толук эмес (Linux ядросунда la GPL блбдору), бирок бул экинчи бөлүктүн темасы.

Бирок жетиштүү. Келгиле, протоколго өтөбүз, анын үстүнө бул сериалдаштыруунун баары аткарылат.

MT Proto

Ошентип, ачалы жалпы сүрөттөмө и протоколдун толук сүрөттөлүшү жана биз эң биринчи мүдүрүлгөн нерсе – бул терминология. Жана бардык нерсенин молчулугу менен. Жалпысынан алганда, бул Telegramдын проприетардык өзгөчөлүгү окшойт - ар кайсы жерлерде ар кандай нерселерди чакыруу, же бир сөз менен ар кандай нерселерди чакыруу, же тескерисинче (мисалы, жогорку деңгээлдеги APIде, эгер сиз чаптамалар пакетин көрсөңүз, анда ал эмес. сен эмне деп ойлоду).

Мисалы, "билдирүү" жана "сеанс" бул жерде кадимки Telegram кардар интерфейсине караганда башка нерсени билдирет. Ооба, билдирүүдө баары түшүнүктүү, аны OOP терминдеринде чечмелесе болот, же жөн эле "пакет" деген сөз деп атаса болот - бул төмөн, транспорттук деңгээл, интерфейстегидей билдирүүлөр жок, көптөгөн кызматтык билдирүүлөр бар . Бирок сессия... бирок биринчиден.

транспорттук катмар

Биринчи нерсе транспорт болуп саналат. Алар бизге 5 вариантты айтып беришет:

  • TCP
  • Websocket
  • HTTPS аркылуу вебсокет
  • HTTP
  • HTTPS

Василий, [15.06.18 15:04] UDP транспорту дагы бар, бирок документтештирилген эмес

Жана TCP үч вариантта

Биринчиси TCP аркылуу UDP менен окшош, ар бир пакет ырааттуулугун жана crc номерин камтыйт
Эмне үчүн арабада документтерди окуу мынчалык кыйналат?

Ооба, ал азыр бар TCP мурунтан эле 4 вариантта:

  • ыдык
  • арадагы
  • Толгон орто
  • толук

Ооба, макул, MTProxy үчүн Padded intermediate, бул кийинчерээк белгилүү окуялардан улам кошулган. Бирок, эмне үчүн дагы эки версия (жалпысынан үчөө) бир версияны колдонсоңуз болот? Төртөө тең негизги MTProtoнун узундугун жана пайдалуу жүгүн кантип орнотууда гана айырмаланат, алар мындан ары талкууланат:

  • Кыскача айтканда, бул 1 же 4 байт, бирок 0xef эмес, анда дене
  • Intermediate бул 4 байт узундук жана талаа жана биринчи жолу кардар жөнөтүшү керек 0xeeeeeeee анын орто экенин көрсөтүү үчүн
  • Толугу менен эң көз каранды, сетевой көз карашы боюнча: узундук, катар номери жана БИР ЭМЕС, бул негизинен MTProto, дене, CRC32. Ооба, мунун баары TCP үстүндө. Бул бизге ырааттуу байт агымы түрүндө ишенимдүү транспорт менен камсыз кылат; эч кандай ырааттуулуктун, өзгөчө текшерүү суммасынын кереги жок. Макул, азыр кимдир бирөө мага TCP 16-бит текшерүү суммасына ээ деп каршы болот, ошондуктан маалымат бузулат. Мыкты, бирок бизде хэштери 16 байттан узунураак криптографиялык протокол бар, бул каталардын бардыгы - жана андан да көп - SHA дал келбестиги жогору деңгээлде кармалат. Мунун үстүнө CRC32де эч кандай маани жок.

Келгиле, бир байт узундугу мүмкүн болгон Кыскартылганды Intermediate менен салыштырып көрөлү, ал “4 байттык маалыматтарды тегиздөө керек болгон учурда” негиздөөчү, бул өтө тантык. Эмне, Telegram программисттери ушунчалык жөндөмсүз болгондуктан, алар розеткадан маалыматтарды тегизделген буферге окуй алышпайт деп ойлошот? Сиз дагы эле муну кылышыңыз керек, анткени окуу сизге каалаган сандагы байттарды кайтарып бере алат (жана ошондой эле прокси серверлер бар, мисалы...). Же болбосо, экинчи жагынан, 16 байттын үстүнө дагы эле чоң толтургуч болсо, эмне үчүн Кыскартылган блоктош керек - 3 байтты үнөмдөңүз кээде ?

Николай Дуров чындап эле дөңгөлөктөрдү, анын ичинде тармактык протоколдорду эч кандай практикалык муктаждыксыз кайра ойлоп тапканды жакшы көрөт деген ой пайда болот.

Башка транспорт параметрлери, анын ичинде. Web жана MTProxy, биз азыр, балким, башка постто, суроо-талап бар болсо, каралбайбыз. Ушул эле MTProxy жөнүндө, 2018-жылы чыккандан көп өтпөй провайдерлер аны бөгөт коюуну тез үйрөнүшкөнүн эстейли. бөгөт коюубоюнча пакет өлчөмү! Ошондой эле C тилинде жазылган MTProxy сервери (кайра Уолтман тарабынан) Linux спецификаларына өтө байланган, бирок бул такыр талап кылынбаса да (Фил Кулин ырастайт) жана Go же Node.js ичинде окшош сервер жүзгө жетпеген сапка туура келет.

Бирок бул адамдардын техникалык сабаттуулугу тууралуу башка маселелерди карап чыккандан кийин бөлүмдүн аягында жыйынтык чыгарабыз. Азырынча MTProto сессиясын жайгаштырган OSI катмарынын 5-сессиясына өтөбүз.

Ачкычтар, билдирүүлөр, сессиялар, Диффи-Хеллман

Алар аны ал жерге такыр туура эмес жайгаштырышкан... Сеанс жигердүү сессиялар астындагы интерфейсте көрүнгөн сессия эмес. Бирок тартипте.

Протоколду жана Telegramдын уюштуруучулук мамилесин сынга алуу. 1-бөлүк, техникалык: нөлдөн баштап кардарды жазуу тажрыйбасы - TL, MT

Ошентип, биз транспорттук катмардан белгилүү узундуктагы байт сапты алдык. Бул же шифрленген билдирүү же ачык текст - эгерде биз дагы эле негизги макулдашуу стадиясында болсок жана аны чындыгында аткарып жатабыз. Биз "ачкыч" деп аталган түшүнүктөрдүн кайсынысы жөнүндө айтып жатабыз? Келгиле, бул маселени Telegram командасынын өзү үчүн тактап алалы (саат 4тө чарчаган мээ менен өзүмдүн документтеримди англис тилинен которгонум үчүн кечирим сурайм, кээ бир фразаларды ошол бойдон калтыруу оңой болду):

деп аталган эки уюм бар жыйналыш - ар бир сеанс бүтүндөй бир түзмөккө / ОСке туура келген "учурдагы сеанстар" астындагы расмий кардарлардын интерфейсинде бири.
Экинчи - MTProto сессиясы, анда кабардын (төмөнкү мааниде) катар номери бар жана кайсы ар кандай TCP байланыштары ортосунда созулушу мүмкүн. Бир эле учурда бир нече MTProto сессиясын орнотсо болот, мисалы, файлды жүктөөнү тездетүү.

Бул экөөнүн ортосунда сессиялар деген түшүнүк бар укук берүү. бузулган учурда, биз деп айта алабыз UI сессиясы менен бирдей укук берүү, бирок тилекке каршы, баары татаал. Кел карайбыз:

  • Жаңы түзмөктөгү колдонуучу биринчи түзөт auth_key жана аны эсепке байланыштырат, мисалы, SMS аркылуу - ошондуктан укук берүү
  • Бул биринчи ичинде болгон MTProto сессиясы, бар session_id өзүңүздүн ичинде.
  • Бул этапта, айкалыштыруу укук берүү и session_id чакырса болот мисалы, - бул сөз кээ бир кардарлардын документтеринде жана кодунда кездешет
  • Андан кийин, кардар ача алат бир топ MTProto сессиялары ошол эле астында auth_key - ошол эле DC.
  • Андан кийин, бир күнү кардар файлды сурашы керек болот башка DC - жана бул DC үчүн жаңысы түзүлөт auth_key !
  • Системага жаңы колдонуучу эмес, катталып жаткандыгы жөнүндө маалымдоо укук берүү (UI сессиясы), кардар API чалууларын колдонот auth.exportAuthorization үйдө DC auth.importAuthorization жаңы DC.
  • Баары бирдей, бир нечеси ачык болушу мүмкүн MTProto сессиялары (ар бири өз алдынча session_id) бул жаңы DC, астында анын auth_key.
  • Акыр-аягы, кардар Perfect Forward сырды каалашы мүмкүн. ар auth_key Ал эле туруктуу ачкыч - DC боюнча - жана кардар чала алат auth.bindTempAuthKey колдонуу үчүн убактылуу auth_key - жана дагы, бир гана temp_auth_key DC үчүн, бардыгы үчүн жалпы MTProto сессиялары бул DC.

байкагыла, ошол туз (жана келечектеги туздар) да бири болуп саналат auth_key ошол. ар кимдин ортосунда бөлүштү MTProto сессиялары ошол эле DC.

"Ар кандай TCP байланыштарынын ортосунда" деген эмнени билдирет? Демек бул дегенди билдирет сыяктуу бир нерсе веб-сайттагы авторизация куки - ал берилген серверге көптөгөн TCP байланыштарын сактайт (аман), бирок бир күнү ал начарлап кетет. HTTP'ден айырмаланып, MTProtoдо сеанс ичиндеги билдирүүлөр ырааттуу түрдө номерленет жана ырасталат; эгерде алар туннелге кирсе, байланыш үзүлгөн - жаңы туташууну орноткондон кийин, сервер бул сессияда мурунку жеткирбеген нерселердин бардыгын боорукердик менен жөнөтөт. TCP байланышы.

Бирок, жогорудагы маалыматтар көп айга созулган иликтөөдөн кийин жалпыланган. Бул арада биз кардарыбызды нөлдөн баштап ишке ашырып жатабызбы? - башына кайрылалы.

Ошентип, генерациялайлы auth_key боюнча Telegram'дан Diffie-Hellman версиялары. Келгиле, документтерди түшүнүүгө аракет кылалы ...

Василий, [19.06.18 20:05] data_with_hash := SHA1(маалымат) + маалыматтар + (ар кандай кокустук байт); узундугу 255 байтка барабар болгон;
шифрленген_маалыматтар := RSA(хэш менен_маалыматтар, сервер_жалпы_ачкычы); 255 байт узун сан (чоң индиан) керектүү модулдун үстүнөн керектүү күчкө көтөрүлөт жана натыйжа 256 байт сан катары сакталат.

Аларда бир аз DH бар

Дени сак адамдын DH окшошпойт
dxде эки ачык ачкыч жок

Акыр-аягы, бул иретке келтирилди, бирок калдык калды - иштин далили кардар тарабынан санды факторлой алган. DoS чабуулдарынан коргоо түрү. Ал эми RSA ачкычы бир багытта бир жолу гана колдонулат, негизинен шифрлөө үчүн new_nonce. Бирок бул жөнөкөй көрүнгөн операция ийгиликтүү аяктаса да, сиз эмнеге туш болушуңуз керек?

Василий, [20.06.18 00:26] Мен азырынча колдонмо сурамына келе элекмин

Мен бул суроону DHге жибердим

Ал эми транспорттук докто 4 байт ката коду менен жооп бере алат деп айтылат. Болду

Ооба, ал мага -404 деп айтты, анда эмне?

Ошентип, мен ага: "Мындай манжа изи менен сервер ачкычы менен шифрленген шылдыңды карма, мен DH каалайм" дедим, ал келесоо 404 менен жооп берди.

Бул сервердин жообу жөнүндө кандай ойдосуз? Эмне кылуу керек? Сурай турган эч ким жок (бирок бул тууралуу экинчи бөлүктө).

Бул жерде бардык кызыкчылык докто жасалат

Башка кыла турган ишим жок, жөн гана сандарды алдыга-артка которууну кыялданчумун

32 биттик эки сан. Мен аларды башкаларга окшоп жыйнадым

Бирок, жок, бул экөөнү BE катары биринчи сапка кошуу керек

Вадим Гончаров, [20.06.18 15:49] жана ушундан улам 404?

Василий, [20.06.18 15:49] ООБА!

Вадим Гончаров, [20.06.18 15:50] андыктан ал эмнени "таба алганын" түшүнбөйм

Василий, [20.06.18 15:50] жөнүндө

Мен негизги факторлорго мындай бөлүнүүнү таба алган жокмун%)

Ката жөнүндө кабарлоону да башкара алган жокпуз

Василий, [20.06.18 20:18] О, MD5 да бар. Буга чейин үч түрдүү хэштер

Негизги манжа изи төмөнкүдөй эсептелет:

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

SHA1 жана sha2

Ошентип, аны коёлу auth_key Биз Diffie-Hellman аркылуу 2048 бит өлчөмүн алдык. Кийинкиси эмне? Андан кийин биз бул ачкычтын төмөнкү 1024 биттери эч кандай түрдө колдонулбагандыгын аныктайбыз... бирок бул жөнүндө азыр ойлонуп көрөлү. Бул кадамда сервер менен бөлүшүлгөн сырыбыз бар. TLS сессиясынын аналогу түзүлдү, бул өтө кымбат процедура. Бирок сервер дагы эле биздин ким экенибизди билбейт! Азырынча эмес, чындыгында. уруксат. Ошол. эгер сиз бир жолу ICQдо кылгандай "логин-пароль" деп ойлосоңуз, же жок дегенде SSHдегидей "логин-ачкыч" (мисалы, кээ бир gitlab/githubда). Биз анонимдүү кат алдык. Эгер сервер бизге "бул телефон номерлерин башка DC тейлейт" десе эмне болот? Же ал тургай "тел номериңизге тыюу салынган"? Колубуздан келгендин эң жакшысы бул ачкычты пайдалуу болот жана ал убакта чирип кетпейт деген үмүт менен сактап калуу.

Баса, биз аны резервациялар менен “кабыл алдык”. Мисалы, биз серверге ишенебизби? Ал жасалма болсочы? Криптографиялык текшерүүлөр зарыл:

Василий, [21.06.18 17:53] Алар мобилдик кардарларга 2кбит номерди биринчиликти текшерүүнү сунушташат%)

Бирок такыр түшүнүксүз, nafeijoa

Василий, [21.06.18 18:02] Документте жөнөкөй эмес болуп чыкса эмне кылуу керектиги айтылган эмес.

Айтылган эмес. Келгиле, бул учурда расмий Android кардары эмне кылаарын карап көрөлү? А ал эмне (жана ооба, бүт файл кызыктуу) - алар айткандай, мен муну бул жерге калтырам:

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

Жок, албетте, дагы эле бар кээ бирлери Сандын биринчилиги үчүн тесттер бар, бирок жеке менин математика боюнча жетиштүү билимим жок.

Макул, биз башкы ачкычты алдык. Кирүү үчүн, б.а. суроо-талаптарды жөнөтүү үчүн, AES колдонуп, андан ары шифрлөө керек.

Кабардын ачкычы билдирүүнүн негизги SHA128 256 орто биттери катары аныкталат (анын ичинде сеанс, билдирүү ID ж.б.), анын ичинде авторизация ачкычынан алынган 32 байт менен коштолгон толтуруучу байт.

Василий, [22.06.18 14:08] Орточо, канчык, бит

Кабыл алынган auth_key. Баары. Алардан ары... документтен так эмес. Ачык кодду изилдөөдөн тартынбаңыз.

Эскерте кетсек, MTProto 2.0 12ден 1024 байтка чейин толтурууну талап кылат, бирок алынган билдирүүнүн узундугу 16 байтка бөлүнөт деген шарт менен.

Ошентип, канча толтургуч кошуу керек?

Ооба, ката болгон учурда 404 да бар

Эгер кимдир-бирөө документациянын диаграммасын жана текстин кылдат изилдеп чыкса, анда ал жерде MAC жок экенин байкашкан. Жана ошол AES башка жерде колдонулбаган белгилүү IGE режиминде колдонулат. Алар, албетте, бул тууралуу өздөрүнүн FAQ бөлүмүндө жазышат... Бул жерде, мисалы, билдирүү ачкычынын өзү да шифрленген маалыматтардын SHA хэши, бүтүндүгүн текшерүү үчүн колдонулат - жана дал келбеген учурда, кандайдыр бир себептерден улам документтер унчукпай аларга көңүл бурбай коюуну сунуштайт (бирок коопсуздук жөнүндө эмне айтууга болот, алар бизди бузуп салсачы?).

Мен криптограф эмесмин, балким, бул учурда теориялык көз караштан алганда бул режимде эч кандай ката жок. Бирок мен мисал катары Telegram Desktop аркылуу практикалык көйгөйдү ачык айта алам. Ал жергиликтүү кэшти шифрлейт (ушулардын бардыгы D877F783D5D3EF8C) MTProtoдогу билдирүүлөр сыяктуу (бул учурда гана 1.0 версиясы), б.а. адегенде билдирүү ачкычы, андан кийин маалыматтардын өзү (жана бир жерде негизги чоң auth_key 256 байт, ансыз msg_key пайдасыз). Ошентип, көйгөй чоң файлдарда байкалат. Тактап айтканда, сиз маалыматтын эки көчүрмөсүн сакташыңыз керек - шифрленген жана шифрленген. Ал эми, мисалы, мегабайттар, же агымдык видео бар болсо?.. Шифрленген тексттен кийин MAC менен классикалык схемалар аны дароо өткөрүп берүү менен агымды окууга мүмкүндүк берет. Бирок MTProto менен сизге туура келет биринчи бардык билдирүүнү шифрлөө же чечмелөө, андан кийин гана аны тармакка же дискке өткөрүү. Ошондуктан, Telegram Desktop акыркы версияларында кэште user_data Башка формат да колдонулат - CTR режиминде AES менен.

Василий, [21.06.18 01:27] О, мен IGE деген эмне экенин билдим: IGE алгач Kerberos үчүн "аныктыгын текшерүүчү шифрлөө режиминин" биринчи аракети болгон. Бул ишке ашпай калган аракет (ал бүтүндүктү коргобойт) жана алынып салынышы керек болчу. Бул жакында OCB жана GCM сыяктуу режимдер менен аяктап, иштеп жаткан аутентификациялоочу шифрлөө режимине 20 жылдык издөөнүн башталышы болду.

Эми араба тараптан аргументтер:

Николай Дуров жетектеген Telegram командасынын курамында алты ACM чемпиону бар, алардын жарымы математика илимдеринин кандидаттары. MTProtoнун учурдагы версиясын чыгаруу үчүн аларга эки жылдай убакыт кетти.

Бул күлкүлүү. Төмөнкү деңгээлде эки жыл

Же tls ала аласыз

Макул, биз шифрлөө жана башка нюанстарды жасадык дейли. Акыры TLде серияланган суроо-талаптарды жөнөтүү жана жоопторду сериядан чыгаруу мүмкүнбү? Анда эмне жана кантип жөнөтүү керек? Бул жерде, айталы, ыкма initConnection, балким ушулдыр?

Василий, [25.06.18 18:46] Колдонуучунун түзмөгүндө жана тиркемесинде туташууну инициализациялайт жана маалыматты сактайт.

Ал app_id, device_model, system_version, app_version жана lang_code кабыл алат.

Жана кээ бир суроо

Кадимкидей эле документ. Ачык булакты изилдөөдөн тартынбаңыз

Эгерде invokeWithLayer менен бардыгы так болсо, анда бул жерде эмне болду? Көрсө, бизде бар дейли - кардар серверден сурай турган нерсеси бар болчу - биз жөнөткүбүз келген өтүнүч бар:

Василий, [25.06.18 19:13] Кодго караганда, биринчи чалуу бул бок менен оролуп, ал эми өзү invokewithlayer менен оролуп калган.

Эмне үчүн initConnection өзүнчө чалуу боло алган жок, бирок орогуч болушу керек? Ооба, белгилүү болгондой, бул негизги ачкыч сыяктуу бир жолу эмес, ар бир сессиянын башында жасалышы керек. Бирок! Аны уруксатсыз колдонуучу чакыра албайт! Азыр биз аны колдонууга боло турган баскычка жеттик Бул документтер барагы - жана бул бизге мындай дейт ...

API ыкмаларынын аз гана бөлүгү уруксатсыз колдонуучулар үчүн жеткиликтүү:

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

Алардын эң биринчиси, auth.sendCode, жана биз api_id жана api_hash жөнөткөн, андан кийин биз код менен SMS кабыл алган биринчи өтүнүч бар. Жана эгер биз туура эмес DCде болсок (мисалы, бул өлкөдөгү телефон номерлери башка тарабынан тейленет), анда биз каалаган DC номери менен ката алабыз. DC номери боюнча кайсы IP дарекке туташуу керек экенин билүү үчүн бизге жардам бериңиз help.getConfig. Бир убакта 5 гана жазуу болгон, бирок 2018-жылдагы белгилүү окуялардан кийин алардын саны кыйла көбөйгөн.

Эми серверде анонимдүү түрдө ушул этапка жеткенибизди эстейли. Жөн эле IP дарегин алуу өтө кымбат эмеспи? Эмне үчүн муну жана башка операцияларды MTProto шифрленбеген бөлүгүндө жасоого болбойт? Мен каршы пикирди угуп жатам: "Кантип жалган даректер менен жооп бере турган РКН эмес экенине ынанабыз?" Бул үчүн биз, жалпысынан, расмий кардарлар экенин эстеп RSA ачкычтары камтылган, б.а. жөн эле аласызбы жазылуу бул маалымат. Чындыгында, бул кардарлар башка каналдар аркылуу алган бөгөттөөлөрдү кыйгап өтүү жөнүндө маалымат үчүн жасалып жатат (логикалык жактан алганда, муну MTProtoдо жасоого болбойт; сиз каякка туташтырууну да билишиңиз керек).

Макул анда. Кардардын авторизациясынын бул этабында биз азырынча ыйгарым укукка ээ эмеспиз жана арызыбызды каттай элекпиз. Биз азырынча уруксатсыз колдонуучуга жеткиликтүү ыкмаларга сервер кандай жооп берерин көргүбүз келет. Жана бул жерде…

Василий, [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;

Схемада биринчи экинчи орунда турат

Tdesktop схемасында үчүнчү маани болуп саналат

Ооба, андан бери, албетте, документация жаңыланды. Бул жакында кайрадан маанисиз болуп калышы мүмкүн да. Жаңыдан иштеп чыгуучу кантип билиши керек? Мүмкүн, арызыңызды каттасаңыз, сизге кабарлап коюшат? Василий муну кылды, бирок тилекке каршы, алар ага эч нерсе жөнөтүшкөн жок (дагы, биз бул тууралуу экинчи бөлүктө сүйлөшөбүз).

...Сиз байкадыңыз, биз кандайдыр бир жол менен APIге өткөнбүз, б.а. кийинки деңгээлге өтүп, MTProto темасында бир нерсени өткөрүп жибердиңизби? Сюрприз жок:

Василий, [28.06.18 02:04] Мм, алар e2eдеги кээ бир алгоритмдерди карап жатышат

Mtproto эки домен үчүн шифрлөө алгоритмдерин жана ачкычтарын, ошондой эле бир аз орогуч структурасын аныктайт

Бирок алар дайыма стектин ар кандай деңгээлдерин аралаштырып турушат, андыктан mtproto кайда аяктап, кийинки деңгээл качан башталганы дайыма эле түшүнүксүз.

Алар кантип аралашат? Ооба, бул жерде, мисалы, PFS үчүн ошол эле убактылуу ачкыч бар (баса, Telegram Desktop муну кыла албайт). Ал API сурамы менен аткарылат auth.bindTempAuthKey, б.а. жогорку деңгээлден. Бирок, ошол эле учурда ал төмөнкү деңгээлде шифрлөөгө тоскоол болот - андан кийин, мисалы, сиз аны кайра кылышыңыз керек. initConnection ж.б., бул эмес так кадимки өтүнүч. Дагы бир өзгөчөлүгү, талаага карабастан, бир DC үчүн бир гана убактылуу ачкыч болушу мүмкүн auth_key_id ар бир билдирүүдө ачкычты жок дегенде ар бир билдирүүнү өзгөртүүгө мүмкүндүк берет жана сервер каалаган убакта убактылуу ачкычты "унутуп коюуга" укуктуу - бул учурда эмне кылуу керектиги документацияда айтылбайт... жакшы, эмне үчүн Келечектеги туздардын топтому сыяктуу сизде бир нече ачкычтар жок жана?..

MTProto темасы жөнүндө белгилей кете турган бир нече башка нерселер бар.

Билдирүү билдирүүлөрү, msg_id, msg_seqno, ырастоолор, туура эмес багытта пингдер жана башка өзгөчөлүктөр

Эмне үчүн алар жөнүндө билишиң керек? Анткени алар жогорку деңгээлге чейин "агып", API менен иштөөдө алардан кабардар болушуңуз керек. Бизди msg_key кызыктырбайт дейли; төмөнкү деңгээл биз үчүн бардыгын чечмелеп койду. Бирок шифрленген маалыматтардын ичинде бизде төмөнкү талаалар бар (ошондой эле берилиштердин узундугу, ошондуктан биз толтуруунун кайда экенин билебиз, бирок бул маанилүү эмес):

  • туз - int64
  • session_id - int64
  • message_id — int64
  • seq_no - int32

Бүткүл DC үчүн бир гана туз бар экенин эскерте кетели. Эмне үчүн ал жөнүндө билебиз? Сураныч болгон үчүн эле эмес get_future_salts, кайсы интервалдар жарактуу болорун айтат, бирок ошондой эле тузуңуз "чирип" болсо, анда билдирүү (суроо) жөн эле жоголот. Сервер, албетте, чыгаруу менен жаңы тузду кабарлайт new_session_created - бирок эскиси менен, мисалы, кандайдыр бир жол менен кайра жөнөтүшүңүз керек болот. Жана бул маселе колдонмо архитектурасына таасир этет.

Серверге сеанстарды толугу менен таштоого жана көптөгөн себептерден улам ушундай жол менен жооп берүүгө уруксат берилет. Чынында, кардар тараптан MTProto сессиясы деген эмне? Бул эки сан session_id и seq_no бул сессиянын ичиндеги билдирүүлөр. Албетте, негизги TCP байланышы. Биздин кардар дагы эле көп нерсени кантип жасоону билбейт дейли, ал ажыратып, кайра кошулду. Бул тез болуп кетсе - эски сессия жаңы TCP байланышы уланды, көбөйтүү seq_no андан ары. Эгер ал көп убакытты талап кылса, сервер аны өчүрүп салышы мүмкүн, анткени анын тарабында ал дагы кезекте турат, биз билгендей.

Кандай болушу керек seq_no? О, бул татаал суроо. Эмнени билдирерин чынчылдык менен түшүнүүгө аракет кыл:

Мазмунга байланыштуу билдирүү

Ачык ырастоону талап кылган билдирүү. Аларга бардык колдонуучу жана көптөгөн кызматтык билдирүүлөр кирет, дээрлик бардыгы контейнерлерден жана ыраазычылыктардан башка.

Кабардын катар номери (msg_seqno)

Жөнөтүүчү тарабынан ушул билдирүүгө чейин түзүлгөн жана кийинчерээк бирге көбөйтүлгөн "мазмунга байланыштуу" билдирүүлөрдүн эки эселенген санына барабар 32 биттик сан (тастыктоону талап кылгандар, айрыкча, контейнерлер эмес). мазмунга байланыштуу билдирүү. Контейнер ар дайым бүт мазмунунан кийин түзүлөт; демек, анын катар номери анда камтылган кабарлардын катар номерлеринен чоң же барабар.

Бул 1ге, анан дагы 2ге өсүү менен бул кандай цирк?.. Мен алар башында "ACK үчүн эң аз маанилүү бит, калганы - сан" дегенди билдирген деп ойлойм, бирок натыйжа такыр эле окшош эмес - атап айтканда, ал чыгат, жиберилиши мүмкүн бир топ тастыктамалар бирдей seq_no! Кантип? Маселен, сервер бизге бир нерсе жөнөтөт, жөнөтөт, биз болсо унчукпай калабыз, анын билдирүүлөрүн алганын тастыктаган кызматтык билдирүүлөр менен гана жооп беребиз. Бул учурда, биздин чыгуучу ырастоолор бирдей чыгуучу номерге ээ болот. Эгер сиз TCP менен тааныш болсоңуз жана бул кандайдыр бир жапайы угулат деп ойлосоңуз, бирок ал анча деле жапайы эмес көрүнөт, анткени TCPде seq_no өзгөрбөйт, бирок ырастоо төмөнкүгө барат seq_no экинчи жагынан, мен сени капа кылууга шашам. Ырастоолор MTProtoдо берилген НЕ боюнча seq_no, TCPдегидей, бирок тарабынан msg_id !

Бул эмне msg_id, бул талаалардын эң негизгиси? Аты айтып тургандай, билдирүүнүн уникалдуу идентификатору. Бул 64 биттик сан катары аныкталат, анын эң төмөнкү биттери кайрадан “сервер эмес-сервер” сыйкырына ээ, ал эми калганы Unix убакыт белгиси, анын ичинде бөлчөк бөлүгү, 32 бит солго жылды. Ошол. убакыт белгиси (жана убакыттары өтө айырмаланган билдирүүлөр сервер тарабынан четке кагылат). Ушундан көрүнүп тургандай, бул жалпысынан кардар үчүн глобалдуу идентификатор. Ушуну эске алуу менен - ​​эстеп көрөлү session_id - биз кепилдик беребиз: Эч кандай шартта бир сессияга арналган билдирүү башка сессияга жөнөтүлбөйт. Башкача айтканда, ансыз деле бар экени белгилүү болду үч деңгээл - сессия, сеанс номери, билдирүү ID. Эмне үчүн мындай ашыкча татаалдык, бул сыр абдан чоң.

Ошентип, msg_id үчүн керек ...

RPC: суроо-талаптар, жооптор, каталар. Ырастоолор.

Сиз байкагандай, жооптор бар болсо да, диаграммада эч кандай атайын "RPC өтүнүчүн жасоо" түрү же функциясы жок. Анткени, бизде мазмунга байланыштуу билдирүүлөр бар! Башкача айтканда, кандайдыр билдирүү өтүнүч болушу мүмкүн! Же болбосо. Кийин баары, ар биринин болуп саналат msg_id. Бирок жооптор бар:

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

Бул кайсы билдирүүгө жооп экени көрсөтүлөт. Ошондуктан, API'нин жогорку деңгээлинде сиз сурамыңыздын номери кандай болгонун эстеп турушуңуз керек болот - менин оюмча, иш асинхрондуу экенин түшүндүрүүнүн кереги жок жана бир эле учурда бир нече сурам аткарылышы мүмкүн, жоопторду каалаган тартипте кайтарса болот? Принцибинде, ушундан жана эч кандай жумушчу сыяктуу ката билдирүүлөрүнөн, мунун артында турган архитектураны байкоого болот: сиз менен TCP байланышын камсыз кылган сервер - бул алдыңкы баланстоочу, ал суроо-талаптарды бэкенддерге жөнөтөт жана аларды кайра чогултат. message_id. Бул жерде баары түшүнүктүү, логикалуу жана жакшы көрүнөт.

Ооба?.. Анан ойлонуп көрсөң? Анткени, RPC жоопунун өзү да талаага ээ msg_id! Серверге "сен менин жообума жооп бербей жатасың!" деп кыйкырышыбыз керекпи? Ооба, ырастоолор жөнүндө эмне болду? Барак жөнүндө билдирүүлөр жөнүндө билдирүүлөр эмне экенин бизге айтып берет

msgs_ack#62d6b459 msg_ids:Vector long = MsgsAck;

жана муну ар бир тарап жасоо керек. Бирок дайыма эмес! Эгер сиз RpcResult алган болсоңуз, ал өзү ырастоо катары кызмат кылат. Башкача айтканда, сервер сиздин сурооңузга MsgsAck менен жооп бере алат, мисалы, "мен аны алдым". RpcResult дароо жооп бере алат. Бул экөө тең болушу мүмкүн.

Ооба, сиз дагы эле жооп беришиңиз керек! Ырастоо. Болбосо, сервер аны жеткирилбейт деп эсептеп, кайра сизге кайра жөнөтөт. Кайра кошулгандан кийин да. Бирок бул жерде, албетте, тайм-аут маселеси келип чыгат. Келгиле, аларды бир аз кийинчерээк карап көрөлү.

Ошол эле учурда, келгиле, мүмкүн болгон суроо аткаруу каталарын карап көрөлү.

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

Ой, кимдир бирөө кыйкырып жиберет, бул жерде дагы гумандуу формат – сызык бар! Шашылба. Мына каталардын тизмеси, бирок, албетте, толук эмес. Андан биз код экенин билебиз сыяктуу бир нерсе HTTP каталары (албетте, жооптордун семантикасы сакталбайт, кээ бир жерлерде алар коддордун арасында туш келди бөлүштүрүлөт) жана сызык окшош CAPITAL_LETTERS_AND_NUMBERS. Мисалы, PHONE_NUMBER_OCCUPIED же FILE_PART_Х_MISSING. Ооба, башкача айтканда, сизге дагы эле бул линия керек болот талдоо. Мисалы, FLOOD_WAIT_3600 бир саат күтүш керек дегенди билдирет, жана PHONE_MIGRATE_5, бул префикс менен телефон номери 5-ДКда катталышы керек. Бизде типтүү тил бар, туурабы? Бизге жиптен аргументтин кереги жок, кадимки эле аргумент болот, макул.

Дагы, бул кызмат билдирүүлөр бетинде эмес, бирок, бул долбоордо адаттагыдай эле, маалыматты табууга болот башка документ бетинде. же шектенүү. Биринчиден, караңыз, терүү/катмар бузуу - RpcError ичине салса болот RpcResult. Эмнеге сыртта эмес? Эмнени эске алган жокпуз?.. Ошого жараша кепилдик кайда RpcError камтылбашы мүмкүн RpcResult, бирок түздөн-түз же башка түргө уя болуп жатабы?.. Ал эми мүмкүн эмес болсо, эмне үчүн жогорку деңгээлде эмес, б.а. ал жок req_msg_id ? ..

Бирок, келгиле, кызматтык билдирүүлөр жөнүндө уланталы. Кардар сервер көптөн бери ойлонуп жатат деп ойлоп, бул сонун өтүнүчтү жасашы мүмкүн:

rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;

Бул суроого үч мүмкүн болгон жооп бар, алар дагы эле ырастоо механизми менен кесилишет; алар эмне болушу керек экенин түшүнүүгө аракет кылуу (жана тастыктоону талап кылбаган типтердин жалпы тизмеси кандай) үй тапшырмасы катары окурманга калтырылган (эскертүү: маалымат Telegram Desktop баштапкы коду толук эмес).

Наркомания: билдирүү статустары

Жалпысынан алганда, TL, MTProto жана Telegram'дагы көп жерлер өжөрлүк сезимин калтырат, бирок сылык, сылыктык жана башкалар жумшак көндүмдөр Биз сылык-сыпаа унчукпай, диалогдордогу уят сөздөрдү цензурадан өткөрдүк. Бирок, бул жерОбеттин көбү жөнүндө билдирүүлөр жөнүндө билдирүүлөр Тармактык протоколдор менен көптөн бери иштеп келе жаткан жана ар кандай деңгээлдеги ийри велосипеддерди көргөн мен үчүн да таң калыштуу.

Бул ырастоо менен, зыянсыз башталат. Андан кийин алар бизге айтып беришет

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;

Ооба, MTProto менен иштей баштагандардын баары алар менен күрөшүүгө туура келет; "түзөтүлгөн - кайра түзүлгөн - ишке киргизилген" циклде сан каталары же түзөтүүлөр учурунда начарлап кеткен туздар кадимки көрүнүш. Бирок, бул жерде эки пункт бар:

  1. Бул баштапкы билдирүү жоголгон дегенди билдирет. Кээ бир кезектерди түзүшүбүз керек, муну кийинчерээк карайбыз.
  2. Бул таң калыштуу ката сандары кандай? 16, 17, 18, 19, 20, 32, 33, 34, 35, 48, 64... башка сандар кайда, Томми?

Документте мындай деп айтылат:

Максат ката_кодунун маанилери топтоштурулган (ката_коду >> 4): мисалы, 0x40 — 0x4f коддору контейнерди ажыратуудагы каталарга туура келет.

бирок, биринчиден, башка багытта жылыш, экинчиден, бул маанилүү эмес, башка коддору кайда? Автордун башына?.. Бирок, булар майда-чүйдө нерселер.

Көз карандылык билдирүү статустары жана билдирүүлөрдүн көчүрмөлөрү жөнүндө билдирүүлөрдөн башталат:

  • Кабардын статусу тууралуу маалымат алуу
    Эгерде тараптардын бири бир аз убакытка чейин өзүнүн чыгуучу билдирүүлөрүнүн абалы жөнүндө маалымат албаса, ал аны экинчи тараптан ачык сурай алат:
    msgs_state_req#da69fb52 msg_ids:Vector long = MsgsStateReq;
  • Кабарлардын абалы жөнүндө маалыматтык билдирүү
    msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
    Бул жерде, info келген msg_ids тизмесинен ар бир билдирүү үчүн билдирүү статусунун так бир байт камтыган сап болуп саналат:

    • 1 = билдирүү жөнүндө эч нерсе белгилүү эмес (msg_id өтө төмөн, экинчи тарап аны унутуп калган болушу мүмкүн)
    • 2 = билдирүү алынган жок (msg_id сакталган идентификаторлордун диапазонуна кирет; бирок, экинчи тарап, албетте, мындай билдирүүнү алган эмес)
    • 3 = билдирүү кабыл алынган жок (msg_id өтө жогору; бирок, экинчи тарап аны али ала элек)
    • 4 = билдирүү кабыл алынды (бул жооп ошол эле учурда дүмүрчөк экенине көңүл буруңуз)
    • +8 = билдирүү мурунтан эле кабыл алынган
    • +16 = тастыктоону талап кылбаган билдирүү
    • +32 = Билдирүүдө камтылган RPC сурамы иштелип жатат же иштетилүүдө
    • +64 = буга чейин түзүлгөн билдирүүгө мазмунга байланыштуу жооп
    • +128 = башка тарап билдирүү мурунтан эле кабыл алынганын билет
      Бул жооп моюнга алууну талап кылбайт. Бул өзүнөн өзү тиешелүү msgs_state_req кабыл алуу болуп саналат.
      Көңүл буруңуз, эгер күтүлбөгөн жерден экинчи тараптын ага жөнөтүлгөндөй билдирүүсү жок экени билинсе, билдирүү жөн гана кайра жөнөтүлүшү мүмкүн. Эгерде экинчи тарап бир эле учурда билдирүүнүн эки нускасын алыш керек болсо да, анын дубликаты көңүлгө алынбайт. (Эгер өтө көп убакыт өтүп кетсе жана баштапкы msg_id жарактуу болбой калса, билдирүү msg_copy менен оролот).
  • Кабарлардын абалын ыктыярдуу билдирүү
    Тараптардын бири экинчи тарапка жөнөтүлгөн билдирүүлөрдүн абалы жөнүндө ыктыярдуу түрдө билдире алат.
    msgs_all_info#8cc0d131 msg_ids:Vector long info:string = MsgsAllInfo
  • Бир билдирүүнүн статусун кеңейтилген ыктыярдуу билдирүү
    ...
    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;
  • Кабарларды кайра жөнөтүү үчүн ачык өтүнүч
    msg_resend_req#7d861a08 msg_ids:Vector long = MsgResendReq;
    Алыскы тарап суралган билдирүүлөрдү кайра жөнөтүү менен дароо жооп берет [...]
  • Жоопторду кайра жөнөтүү үчүн ачык өтүнүч
    msg_resend_ans_req#8610baeb msg_ids:Vector long = MsgResendReq;
    Алыскы тарап дароо кайра жөнөтүү менен жооп берет жооп суралган билдирүүлөргө […]
  • Кабардын көчүрмөлөрү
    Кээ бир учурларда, msg_id жараксыз болгон эски билдирүү кайра жөнөтүлүшү керек. Андан кийин, ал көчүрмө контейнерге оролгон:
    msg_copy#e06046b2 orig_message:Message = MessageCopy;
    Кабыл алынгандан кийин, билдирүү орогуч жок сыяктуу иштетилет. Бирок, эгерде orig_message.msg_id билдирүү кабыл алынганы белгилүү болсо, анда жаңы билдирүү иштетилбейт (ошол эле учурда ал жана orig_message.msg_id кабыл алынат). orig_message.msg_id мааниси контейнердин msg_id маанисинен төмөн болушу керек.

Эмне деп унчукпай эле коёлу msgs_state_info гайтадан тамамланмадык ТЛ-нин гулаклары япышяр (бизге байт векторы герек, ашакы ики битде энум, ёкары ики битинде болса байраклар бар). Кеп башкада. Мунун баары иш жүзүндө эмне үчүн экенин кимдир бирөө түшүнөбү? чыныгы кардарда зарылбы?.. Кыйынчылык менен, бирок адам мүчүлүштүктөрдү оңдоо менен жана интерактивдүү режимде болсо, кандайдыр бир пайданы элестете алабыз - серверден эмне жана кантип сураңыз. Бирок бул жерде өтүнүчтөр сүрөттөлөт тегерек сапар.

Демек, ар бир тарап билдирүүлөрдү шифрлеп, жөнөтүү менен гана чектелбестен, өздөрү жөнүндө, аларга жооптор жөнүндө маалыматтарды белгисиз убакытка сактоого тийиш. Документте бул функциялардын мөөнөттөрү да, практикалык колдонулушу да сүрөттөлбөйт. эч кандай. Эң таң калыштуусу, алар чындыгында расмий кардарлардын кодунда колдонулат! Сыягы, аларга мамлекеттик документацияга кирбеген нерсени айтышкан окшойт. коддон түшүнүү эмне үчүн, TLдегидей жөнөкөй эмес - бул (салыштырмалуу) логикалык жактан обочолонгон бөлүк эмес, бирок колдонмонун архитектурасына байланган кесим, б.а. колдонмо кодун түшүнүү үчүн кыйла көбүрөөк убакыт талап кылынат.

Пингдер жана убакыттар. Кезектер.

Баарынан, эгерде биз сервердин архитектурасы жөнүндө божомолдорду эстесек (суроо-талаптарды бэкенддерде бөлүштүрүү), TCPдеги бардык жеткирүү кепилдиктерине карабастан (же маалыматтар жеткирилет, же сиз боштук жөнүндө маалымат аласыз, бирок маалымат көйгөй пайда болгонго чейин жеткирилет), MTProto өзүндө ырастоолор - кепилдик жок. Сервер сиздин билдирүүңүздү оңой эле жоготуп же ыргытып жибериши мүмкүн жана бул тууралуу эч нерсе кыла албайсыз, жөн гана ар кандай түрдөгү балдактарды колдонуңуз.

Ал эми биринчи кезекте - билдирүү кезеги. Ооба, бир нерсе менен баары башынан эле айкын болгон - ырасталбаган билдирүү сакталып, кайра жөнөтүлүшү керек. Анан канчадан кийин? Ал эми тамашачы аны билет. Балким, ошол көз каранды кызматтык билдирүүлөр кандайдыр бир жол менен балдак менен бул көйгөйдү чечет, айталы, Telegram Desktopто аларга туура келген 4кө жакын кезек бар (балким, жогоруда айтылгандай, бул үчүн анын кодун жана архитектурасын олуттуураак изилдеш керек; ошол эле учурда убакыт, биз аны үлгү катары кабыл алуу мүмкүн эмес экенин билебиз; MTProto схемасынын белгилүү бир түрлөрү анда колдонулган эмес).

Эмне үчүн бул болуп жатат? Мүмкүн, сервердик программисттер кластердин ичинде ишенимдүүлүктү камсыздай албай, жада калса алдыңкы балансизатордо буферлөө менен бул көйгөйдү кардарга өткөрүп беришкен. Василий үмүтсүздүктөн TCP алгоритмдерин колдонуп, эки гана кезекте турган альтернативдүү вариантты ишке ашырууга аракет кылды - серверге RTT өлчөө жана ырасталбаган суроо-талаптардын санына жараша "терезенин" өлчөмүн (билдирүүлөрдө) тууралоо. Башкача айтканда, сервердин жүгүн баалоо үчүн ушундай одоно эвристика бул биздин канча сурообузду бир эле учурда чайнап, жоготуп албашы.

Ооба, сен түшүнөсүң, туурабы? Эгер сиз TCP үстүнөн иштеген протоколдун үстүнө кайра TCP киргизишиңиз керек болсо, бул абдан начар иштелип чыккан протоколду көрсөтөт.

Ооба, эмне үчүн сизге бирден ашык кезек керек жана бул баары бир жогорку деңгээлдеги API менен иштеген адам үчүн эмнени билдирет? Караңыз, сиз сурам жасайсыз, аны сериялайсыз, бирок көп учурда аны дароо жөнөтө албайсыз. Неге? Анткени жооп болот msg_id, бул убактылууаМен энбелгимин, аны тапшыруу мүмкүн болушунча кечирээк кийинкиге калтырылган - эгерде сервер экөөбүздүн ортобуздагы убакыттын дал келбегендигинен улам аны четке кагып салса (албетте, биз убакытты азыркыдан башка жакка бурган балдак жасай алабыз. сервердин жоопторунан эсептелген дельтаны кошуу менен серверге - расмий кардарлар муну жасашат, бирок буферлөөдөн улам бул одоно жана так эмес). Демек, сиз китепканадан жергиликтүү функция чалуу менен суроо-талап кылганда, билдирүү төмөнкү этаптардан өтөт:

  1. Ал бир кезекте турат жана шифрлөөнү күтөт.
  2. дайындалды msg_id жана билдирүү башка кезекке кетти - мүмкүн багыттоо; розеткага жөнөтүү.
  3. a) Сервер MsgsAck деп жооп берди - билдирүү жеткирилди, биз аны “башка кезектен” жок кылабыз.
    б) Же тескерисинче, ага бир нерсе жакпады, ал badmsg деп жооп берди - "башка кезектен" кайра жөнөтүү
    в) Эч нерсе белгилүү эмес, билдирүү башка кезекте кайра жөнөтүлүшү керек - бирок так качан белгилүү эмес.
  4. Сервер акыры жооп берди RpcResult - иш жүзүндөгү жооп (же ката) - жөн гана жеткирилбестен, кайра иштетилген.

мүмкүн, контейнерлерди колдонуу маселени жарым-жартылай чечиши мүмкүн. Бул бир топ билдирүүлөр бирине топтолгондо жана сервер алардын бардыгына бир эле учурда ырастоо менен жооп бергенде. msg_id. Бирок, эгер бир нерсе туура эмес болуп кетсе, ал бул пакетти толугу менен четке кагат.

Жана бул учурда техникалык эмес ойлор пайда болот. Тажрыйбабыздан биз көптөгөн балдактарды көрдүк, мындан тышкары, биз азыр жаман кеңештердин жана архитектуранын дагы көп мисалдарын көрөбүз - ушундай шарттарда ишенип, ушундай чечимдерди кабыл алуу керекпи? Суроо риторикалык (албетте эмес).

Биз эмне жөнүндө сүйлөшүп жатабыз? Эгерде "билдирүүлөр жөнүндө баңги билдирүүлөрү" темасында сиз дагы эле "сен келесоосуң, биздин сонун планыбызды түшүнгөн жоксуң!" (ошондуктан, адегенде документацияны кадимки адамдар керек болсо, пакет алмашуунун негиздери жана мисалдары менен жазыңыз, анан сүйлөшөбүз), анда тайм-ауттар - бул таза практикалык жана конкреттүү суроо, бул жерде бардыгы көптөн бери белгилүү. Документтер бизге тайм-ауттар жөнүндө эмне дейт?

Сервер, адатта, RPC жообун колдонуу менен кардардан (адатта, RPC сурамы) билдирүүнү кабыл алганын ырастайт. Эгер жооп көптөн бери келе турган болсо, сервер алгач квитанцияны, андан кийин бир аздан кийин RPC жоопту өзү жөнөтүшү мүмкүн.

Кардар адатта серверден кабарды (адатта, RPC жообу), эгерде ал өтө кеч берилбесе, кийинки RPC суроосуна тастыктоо кошуу менен ырастайт (эгерде ал кабыл алынгандан кийин 60-120 секунддан кийин түзүлсө, айталы. серверден келген билдирүү). Бирок, узак убакыт бою серверге билдирүүлөрдү жөнөтүүгө эч кандай себеп жок болсо же серверден таанылбаган билдирүүлөрдүн көп саны болсо (мисалы, 16дан ашык), кардар өзүнчө ырастоо жөнөтөт.

...Мен котором: биз өзүбүзгө канча жана кандай керек экенин билбейбиз, андыктан ушундай болсун дейли.

Жана пингдер жөнүндө:

Пинг билдирүүлөрү (PING/PONG)

ping#7abe77ec ping_id:long = Pong;

Жооп адатта ошол эле байланышка кайтарылат:

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

Бул билдирүүлөр ырастоону талап кылбайт. Понг пингге жооп катары гана берилет, ал эми пинг эки тараптан да башталышы мүмкүн.

Кийинкиге калтырылган туташуу + PING

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

Пинг сыяктуу иштейт. Кошумчалай кетсек, бул кабыл алынгандан кийин сервер таймерди иштетет, ал учурдагы туташууну disconnect_delay секундадан кийин жабат, эгерде ал бардык мурунку таймерлерди автоматтык түрдө баштапкы абалга келтирүүчү ошол эле типтеги жаңы билдирүүнү албаса. Эгерде кардар бул пингдерди 60 секундада бир жолу жөнөтсө, мисалы, disconnect_delay 75 секундага барабар болушу мүмкүн.

Сен жиндисиңби?! 60 секунддан кийин поезд станцияга кирип, жүргүнчүлөрдү түшүрүп алып, туннелде кайрадан байланыш үзүлөт. 120 секунданын ичинде, сиз аны укканда, ал башкасына келип, байланыш үзүлүп калышы мүмкүн. Ооба, буттар кайдан келип жатканы түшүнүктүү - "Мен коңгуроо уктум, бирок кайда экенин билбейм", Наглдын алгоритми жана интерактивдүү иштөө үчүн арналган TCP_NODELAY опциясы бар. Бирок, кечиресиз, анын демейки маанисин кармаңыз - 200 Milliсекунд Эгер сиз чындап эле окшош нерсени сүрөттөп, бир нече пакетти үнөмдөөнү кааласаңыз, анда аны 5 секундага өчүрүңүз же "Колдонуучу терип жатат..." билдирүү күтүү убакыты азыр. Бирок мындан ары жок.

Акыр-аягы, пингдер. Башкача айтканда, TCP байланышынын жандуулугун текшерүү. Күлкүлүү, бирок 10 жыл мурун мен биздин факультеттин жатаканасынын мессенджери тууралуу сын текст жазгам - ал жердеги авторлор да серверге кардардан пинг алышкан, тескерисинче эмес. Бирок 3-курстун студенттери башка, эл аралык кеңсе башка нерсе, туурабы?..

Биринчиден, бир аз билим берүү программасы. TCP туташуусу, пакет алмашуу жок болсо, бир нече жума жашай алат. Бул максатка жараша жакшы да, жаман да. Эгер сизде серверге SSH туташуусу ачык болсо, анда жакшы болот, сиз компьютерден туруп, роутерди кайра жүктөдүңүз, ордуңузга кайтып келдиңиз - бул сервер аркылуу сессия үзүлгөн жок (сиз эч нерсе жазган жоксуз, пакеттер жок) , бул ыңгайлуу. Серверде миңдеген кардарлар бар болсо, алардын ар бири ресурстарды ээлеп жатса (саламатсызбы, Postgres!) жана кардардын хосту көп убакыт мурун кайра жүктөлгөн болушу мүмкүн - бирок биз бул тууралуу билбейбиз.

Чат/IM системалары бир кошумча себеп менен экинчи учурга кирет - онлайн статустары. Эгер колдонуучу "жыгылып кетсе", анда бул тууралуу анын маектештерине билдирүү керек. Болбосо, сиз Jabberдин жаратуучулары кетирген ката менен аяктайсыз (жана 20 жыл бою оңдогон) - колдонуучу ажырап калды, бирок алар аны онлайн деп эсептеп, ага билдирүү жазууну уланта беришет (алар да бул жерде толугу менен жоголуп кеткен). ажыратуу табылганга чейин бир нече мүнөт мурун). Жок, TCP таймерлеринин кантип иштешин түшүнбөгөн көптөгөн адамдар туш келди киргизген TCP_KEEPALIVE опциясы бул жерде жардам бербейт - сиз OS ядросу гана эмес экенине ынанышыңыз керек. Колдонуучунун машинасы тирүү, бирок ошондой эле нормалдуу иштеп, жооп берүүгө жөндөмдүү жана тиркеменин өзү (ал тоңбойт деп ойлойсузбу? Ubuntu 18.04теги Telegram Desktop мен үчүн бир нече жолу катып калган).

Ошон үчүн пинг кылышыңыз керек Server кардар, тескерисинче эмес - эгерде кардар муну кылса, байланыш үзүлсө, пинг жеткирилбейт, максатка жетпейт.

Telegramдан эмнени көрүп жатабыз? Бул так карама-каршы! Ооба, ошол. Формалдуу түрдө, албетте, эки тарап бири-бирин пингге алат. Иш жүзүндө кардарлар балдак колдонушат ping_delay_disconnect, сервердеги таймерди орнотот. Кечиресиз, кардар ал жерде канча убакытка пингсиз жашагысы келерин чече албайт. Сервер өзүнүн жүктөмүнө жараша жакшы билет. Бирок, албетте, ресурстарга каршы болбосоңуз, анда сиз өзүңүздүн жаман Пиноккио болосуз, ал эми балдак эмне кылат ...

Ал кантип иштелип чыгышы керек эле?

Мен жогоруда келтирилген фактылар Telegram/VKontakte командасынын компьютердик тармактардын транспорттук (жана төмөнкү) деңгээлинде өтө компетенттүү эместигин жана тиешелүү маселелерде алардын квалификациясынын төмөндүгүн ачык көрсөтүп турат деп эсептейм.

Эмне үчүн бул ушунчалык татаал болуп чыкты жана Telegram архитекторлору кантип каршылык көрсөтүүгө аракет кылышы мүмкүн? Алар TCP туташуусу үзгүлтүккө учураган сеанс жасоого аракет кылгандыгы, б.а., азыр жеткирилбеген нерсе, биз кийинчерээк жеткиребиз. Алар, балким, UDP транспортун жасоого аракет кылышкан, бирок алар кыйынчылыктарга туш болуп, андан баш тартышкан (ошондуктан документация бош - мактана турган эч нерсе жок). Бирок жалпысынан тармактардын жана өзгөчө TCP кандай иштээрин түшүнбөгөндүктөн, сиз ага таяна аласыз жана аны кайдан өзүңүз (жана кантип) жасашыңыз керек жана муну криптография менен айкалыштыруу аракети “эки куш бир таш», бул жыйынтык.

Бул кандай зарыл болгон? Ошонун негизинде msg_id кайталоо чабуулдарын алдын алуу үчүн криптографиялык көз караштан зарыл болгон убакыт белгиси, ага уникалдуу идентификатор функциясын тиркөө ката. Ошондуктан, учурдагы архитектураны кескин өзгөртпөстөн (Жаңыртуулар агымы түзүлгөндө, бул посттордун башка бөлүгү үчүн жогорку деңгээлдеги API темасы), кимге керек болот:

  1. Кардар менен TCP байланышын кармап турган сервер жоопкерчиликти өзүнө алат - эгер ал розеткадан окуган болсо, катаны моюнга алыңыз, иштетиңиз же кайтарыңыз, жоготуу жок. Анда ырастоо идентификаторлордун вектору эмес, жөн гана “акыркы кабыл алынган seq_no” – TCPдегидей жөн гана сан (эки сан – сиздин сек жана ырасталган). Биз ар дайым сессиянын ичинде болобуз, туурабы?
  2. Кайталоо чабуулдарына жол бербөө үчүн убакыт белгиси өзүнчө талаага айланат. Ал текшерилет, бирок башка эч нерсеге таасир этпейт. Жетиштүү жана uint32 - эгерде биздин тузубуз жок дегенде ар бир жарым күндө өзгөрсө, анда биз 16 битти учурдагы убакыттын бүтүн бөлүгүнүн төмөнкү тартиптеги биттерине, калганын - секунданын бөлчөк бөлүгүнө (азыркыдай) бөлө алабыз.
  3. Өчүрүлдү msg_id дегеле - серверлерде суроо-талаптарды айырмалоо көз карашынан алганда, биринчиден, кардар идентификатору, экинчиден, сеанс идентификатору аларды бириктирет. Демек, суроо-талаптын идентификатору катары бир гана нерсе жетиштүү seq_no.

Бул дагы эң ийгиликтүү вариант эмес; толук кокус идентификатор катары кызмат кыла алат - бул билдирүү жөнөтүүдө жогорку деңгээлдеги APIде жасалган. Архитектураны салыштырмалуу абсолюттукка чейин толугу менен кайра жасаса жакшы болмок, бирок бул башка бөлүктүн темасы, бул пост.

API?

Та-дам! Ошентип, ооруга жана балдактарга толгон жолду басып өтүп, акыры серверге каалаган суроо-талаптарды жөнөтүп, аларга жооп ала алдык, ошондой эле серверден жаңыртууларды ала алдык (суроого жооп катары эмес, анын өзү бизге жөнөтөт, PUSH сыяктуу, эгер кимдир бирөө так ушундай болсо).

Көңүл буруңуз, азыр макалада Perlдеги жалгыз мисал болот! (синтаксис менен тааныш болбогондор үчүн бата берүүнүн биринчи аргументи объекттин маалымат структурасы, экинчиси анын классы):

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

Ооба, атайын спойлер эмес - эгер сиз аны окуй элек болсоңуз, улантыңыз!

О, wai~~... бул эмнеге окшош? Абдан тааныш нерсе... балким бул JSONдеги типтүү Web API маалымат структурасы, класстар дагы объекттерге тиркелет?..

Мына ошентип чыгат... Кеп эмнеде, жолдоштор?.. Ушунча аракет — Веб программисттер турган жерге эс алууга токтодук. жаңыдан баштап?..HTTPS аркылуу JSON жөн гана жөнөкөйрөөк болбойт беле?! Анын ордуна эмне алдык? Аракеттин акыбети кайттыбы?

Келгиле, TL+MTProto бизге эмне берди жана кандай альтернативалар мүмкүн экенин карап көрөлү. Ооба, суроо-жооп моделине басым жасаган HTTP, туура эмес, бирок жок дегенде TLS үстүнөн бир нерсе барбы?

Компакттуу сериялаштыруу. JSONга окшош бул маалымат структурасын көрүп, анын экилик версиялары бар экенин эстейм. Келгиле, MsgPack жетишсиз кеңейтилүүчү деп белгилейли, бирок, мисалы, CBOR бар - демек, стандартта сүрөттөлгөн. RFC 7049. аныктаганы менен өзгөчөлөнөт тэгдер, кеңейтүү механизми катары жана арасында буга чейин стандартташтырылган жеткиликтүү:

  • 25 + 256 - сап номерине шилтеме менен кайталанган саптарды алмаштыруу, мындай арзан кысуу ыкмасы
  • 26 - класстын аталышы жана конструктор аргументтери менен серияланган Perl объектиси
  • 27 - типтин аталышы жана конструктор аргументтери менен серияланган тилден көз карандысыз объект

Мен ошол эле маалыматтарды TL жана CBOR ичинде сап жана объект пакеттөө иштетилген сериялаштырууга аракет кылдым. Жыйынтык бир мегабайттан CBOR пайдасына өзгөрө баштады:

cborlen=1039673 tl_len=1095092

Ошентип, жыйынтыктоо: Бир кыйла жөнөкөй форматтар бар, алар синхрондоштуруу катасы же белгисиз идентификатор көйгөйүнө дуушарланбайт, салыштырмалуу эффективдүүлүк менен.

Тез байланыш түзүү. Бул кайра туташтыруудан кийин нөл RTT дегенди билдирет (ачкыч буга чейин бир жолу түзүлгөн) - биринчи MTProto билдирүүсүнөн тарта колдонулат, бирок кээ бир эскертүүлөр менен - ​​бир эле тузду басып, сессия чириген эмес, ж.б. Анын ордуна TLS бизге эмнени сунуш кылат? Тема боюнча цитата:

TLSде PFSди колдонгондо, TLS сессия билеттери (RFC 5077) ачкычтарды кайра сүйлөшүүсүз жана серверде негизги маалыматты сактабастан шифрленген сессияны улантуу. Биринчи байланышты ачууда жана ачкычтарды түзүүдө сервер байланыш абалын шифрлейт жана аны кардарга берет (сеанс билети түрүндө). Тиешелүү түрдө, байланыш кайра жанданганда, кардар серверге кайра сеанс билетин, анын ичинде сессия ачкычын жөнөтөт. Билеттин өзү убактылуу ачкыч (сеанс билетинин ачкычы) менен шифрленген, ал серверде сакталат жана кластердик чечимдерде SSL иштеткен бардык фронт-серверлер арасында бөлүштүрүлүшү керек.[10]. Ошентип, сеанс билетин киргизүү, эгерде убактылуу сервер ачкычтары бузулса, мисалы, алар узак убакытка сакталганда (OpenSSL, nginx, Apache аларды программанын бүткүл мөөнөтүндө демейки боюнча сактайт; популярдуу сайттар колдонушат) PFSти бузушу мүмкүн. ачкыч бир нече саатка, күнгө чейин).

Бул жерде RTT нөл эмес, сиз жок дегенде ClientHello жана ServerHello менен алмашууңуз керек, андан кийин кардар Даяр болгон менен бирге маалыматтарды жөнөтө алат. Бирок бул жерде бизде жаңы ачылган туташуулар менен Интернет жок экенин эстен чыгарбашыбыз керек, бирок байланышы көп учурда бир жана бир нече же азыраак узак мөөнөттүү, веб-баракчаларга салыштырмалуу кыска суроо-талаптар болгон мессенжер - бардыгы мультиплекстелген. ички. Башкача айтканда, биз чындап эле жаман метро бөлүгүн жолуктурбасак, бул абдан алгылыктуу.

Дагы бир нерсени унутуп калдыңызбы? Комментарийге жазыңыз.

Уландысы бар!

Посттордун бул сериясынын экинчи бөлүгүндө биз техникалык эмес, уюштуруучулук маселелерди - ыкмалар, идеология, интерфейс, колдонуучуларга болгон мамиле ж.б.у.с. карайбыз. Бирок, бул жерде берилген техникалык маалыматка негизделген.

Үчүнчү бөлүгү техникалык компонентти / өнүктүрүү тажрыйбасын талдоону улантат. Сиз, атап айтканда:

  • ТТ типтеринин ар түрдүүлүгү менен пандемониянын уландысы
  • каналдар жана супергруппалар жөнүндө белгисиз нерселер
  • эмне үчүн диалогдор тизмеден жаман
  • абсолюттук жана салыштырмалуу билдирүү дареги жөнүндө
  • сүрөт менен сүрөттүн ортосунда кандай айырма бар
  • быйтыкчалар курсив текстке кандайча тоскоолдук кылат

жана башка балдактар! Байланыштуу болуңуз!

Source: www.habr.com

Комментарий кошуу