Гузаронидани бозии мултипликатори аз C++ ба веб бо Cheerp, WebRTC ва Firebase

Муқаддима

Ширкати мо Технологияҳои такякунанда ҳалли худро барои интиқоли барномаҳои анъанавии мизи корӣ ба веб таъмин мекунад. Компилятори мо C++ шодоб маҷмӯи WebAssembly ва JavaScript-ро тавлид мекунад, ки ҳардуро таъмин мекунад ҳамкории оддии браузер, ва самараи баланд.

Ҳамчун намунаи татбиқи он, мо тасмим гирифтем, ки як бозии мултипликаторро ба веб интиқол диҳем ва интихоб кунем Дунёи иқтисод. Teeworlds як бозии мултипликатори ретро 2D бо ҷомеаи хурд, вале фаъоли бозигарон аст (аз ҷумла ман!). Он ҳам аз ҷиҳати захираҳои зеркашида ва талаботи CPU ва GPU хурд аст - як номзади беҳтарин.

Гузаронидани бозии мултипликатори аз C++ ба веб бо Cheerp, WebRTC ва Firebase
Дар браузери Teeworlds кор мекунад

Мо тасмим гирифтем, ки ин лоиҳаро барои таҷриба истифода барем ҳалли умумӣ барои интиқоли рамзи шабака ба веб. Ин одатан бо роҳҳои зерин анҷом дода мешавад:

  • XMLHttpRequest/fetch, агар қисми шабака танҳо аз дархостҳои HTTP иборат бошад, ё
  • WebSockets.

Ҳарду қарорҳо дар тарафи сервер ҷойгир кардани ҷузъи серверро талаб мекунанд ва ҳеҷ яке барои истифода ҳамчун протоколи интиқол иҷозат намедиҳад UDP. Ин барои барномаҳои воқеии вақт, ба монанди нармафзори видеоконфронс ва бозиҳо муҳим аст, зеро он интиқол ва фармоиши бастаҳои протоколиро кафолат медиҳад. TCP метавонад ба таъхири паст монеа шавад.

Роҳи сеюм вуҷуд дорад - шабакаро аз браузер истифода баред: WebRTC.

RTCDataChannel Он ҳам интиқоли боэътимод ва ҳам беэътимодро дастгирӣ мекунад (дар ҳолати охирин кӯшиш мекунад, ки то ҳадди имкон UDP-ро ҳамчун протоколи нақлиёт истифода барад) ва онро ҳам бо сервери дурдаст ва ҳам байни браузерҳо истифода бурдан мумкин аст. Ин маънои онро дорад, ки мо метавонем тамоми барномаро ба браузер, аз ҷумла ҷузъи сервер интиқол диҳем!

Аммо, ин як мушкили иловагӣ дорад: пеш аз он ки ду ҳамсолони WebRTC муошират кунанд, онҳо бояд барои пайвастшавӣ дастфишори нисбатан мураккабро анҷом диҳанд, ки барои пайвастшавӣ якчанд объектҳои тарафи сеюм (сервери сигнализатсия ва як ё якчанд серверҳо) лозим аст. ХАЙРОН/Гардиш).

Идеалӣ, мо мехоҳем API-и шабакавиеро созем, ки WebRTC-ро дар дохили худ истифода мебарад, аммо то ҳадди имкон ба интерфейси UDP Sockets наздик аст, ки ба таъсиси пайвастшавӣ ниёз надорад.

Ин ба мо имкон медиҳад, ки WebRTC-ро бидуни фош кардани тафсилоти мураккаб ба коди барнома истифода барем (ки мо мехостем, ки дар лоиҳаи худ то ҳадди имкон каме тағир диҳем).

Ҳадди ақали WebRTC

WebRTC маҷмӯи APIҳоест, ки дар браузерҳо дастрасанд, ки интиқоли аудио, видео ва маълумоти ихтиёриро таъмин мекунанд.

Пайвастшавӣ байни ҳамсолон (ҳатто агар дар як ё ҳарду ҷониб NAT мавҷуд бошад) бо истифода аз серверҳои STUN ва/ё TURN тавассути механизме бо номи ICE муқаррар карда мешавад. Ҳамсолон тавассути пешниҳод ва ҷавоби протоколи SDP маълумоти ICE ва параметрҳои каналро мубодила мекунанд.

Вой! Дар як вақт чанд ихтисорот? Биёед мухтасар шарҳ диҳем, ки ин истилоҳҳо чӣ маъно доранд:

  • Утилитҳои гузариш барои NAT (ХАЙРОН) — протокол барои гузаштан аз NAT ва гирифтани ҷуфт (IP, порт) барои мубодилаи маълумот мустақиман бо ҳост. Агар ӯ вазифаи худро иҷро кунад, пас ҳамсолон метавонанд мустақилона бо ҳамдигар маълумот мубодила кунанд.
  • Гузариш бо истифода аз релеҳо дар атрофи NAT (Гардиш) инчунин барои интиқоли NAT истифода мешавад, аммо онро тавассути интиқоли маълумот тавассути прокси, ки барои ҳарду ҳамсол намоён аст, амалӣ мекунад. Он таъхирро илова мекунад ва татбиқаш нисбат ба STUN гаронтар аст (зеро он дар тамоми сессияи муошират истифода мешавад), аммо баъзан ин ягона вариант аст.
  • Ташкили пайвасти интерактивӣ (ях) барои интихоби беҳтарин усули имконпазири пайваст кардани ду ҳамсол дар асоси маълумоте, ки аз пайваст кардани ҳамсолон мустақиман гирифта шудааст, инчунин маълумоте, ки аз ҷониби ҳама гуна шумораи серверҳои STUN ва TURN гирифта шудааст, истифода мешавад.
  • Протоколи тавсифи сессия (СДП) форматест барои тавсифи параметрҳои канали пайвастшавӣ, масалан, номзадҳои ICE, кодекҳои мултимедиявӣ (дар сурати канали аудио/видео) ва ғайра... Яке аз ҳамсолон пешниҳоди SDP мефиристад ва дуюмӣ бо ҷавоби SDP ҷавоб медиҳад. . Пас аз ин, як канал таъсис дода мешавад.

Барои эҷоди чунин пайвастагӣ, ҳамсолон бояд маълумотеро, ки аз серверҳои STUN ва TURN мегиранд, ҷамъоварӣ намуда, бо ҳамдигар мубодила кунанд.

Мушкилот дар он аст, ки онҳо то ҳол қобилияти муоширати мустақим надоранд, бинобар ин, барои мубодилаи ин маълумот бояд механизми берун аз банд мавҷуд бошад: сервери сигнализатсия.

Сервери сигнализатсия метавонад хеле содда бошад, зеро кори ягонаи он интиқоли маълумот байни ҳамсолон дар марҳилаи дастфишорӣ мебошад (тавре ки дар диаграммаи зер нишон дода шудааст).

Гузаронидани бозии мултипликатори аз C++ ба веб бо Cheerp, WebRTC ва Firebase
Диаграммаи пайдарпаии дастфишори соддакардашудаи WebRTC

Шарҳи модели шабакаи Teeworlds

Меъмории шабакаи Teeworlds хеле содда аст:

  • Ҷузъҳои муштарӣ ва сервер ду барномаи гуногун мебошанд.
  • Мизоҷон тавассути пайвастшавӣ ба яке аз якчанд серверҳо, ки ҳар яки онҳо дар як вақт танҳо як бозӣ доранд, вориди бозӣ мешаванд.
  • Ҳама интиқоли маълумот дар бозӣ тавассути сервер сурат мегирад.
  • Барои ҷамъоварии рӯйхати ҳама серверҳои ҷамъиятӣ, ки дар муштарии бозӣ намоиш дода мешаванд, сервери махсуси мастер истифода мешавад.

Бо шарофати истифодаи WebRTC барои мубодилаи маълумот, мо метавонем ҷузъи сервери бозиро ба браузере, ки муштарӣ ҷойгир аст, интиқол диҳем. Ин ба мо имконияти бузург медиҳад...

Аз серверҳо халос шавед

Набудани мантиқи сервер як бартарии хубе дорад: мо метавонем тамоми барномаро ҳамчун мундариҷаи статикӣ дар саҳифаҳои Github ё дар сахтафзори худамон дар паси Cloudflare ҷойгир кунем ва ҳамин тавр зеркашиҳои зуд ва вақти баландро ройгон таъмин кунем. Дар асл, мо метавонем онҳоро фаромӯш кунем ва агар бахти мо бошад ва бозӣ маъмул гардад, пас инфрасохторро навсозӣ кардан лозим нест.

Аммо, барои кор кардани система, мо бояд то ҳол як меъмории берунаро истифода барем:

  • Як ё якчанд серверҳои STUN: Мо якчанд имконоти ройгон дорем, ки аз онҳо интихоб кунем.
  • Ҳадди ақал як сервери TURN: дар ин ҷо имконоти ройгон вуҷуд надорад, аз ин рӯ мо метавонем ё худамонро насб кунем ё барои хидмат пардохт кунем. Хушбахтона, аксар вақт пайвастшавиро тавассути серверҳои STUN барқарор кардан мумкин аст (ва p2p ҳақиқиро таъмин мекунад), аммо TURN ҳамчун варианти бозгашт лозим аст.
  • Сервери сигнализатсия: Баръакси ду ҷанбаи дигар, сигнализатсия стандартизатсия карда нашудааст. Он чизе ки сервери сигнализатсия воқеан барои он масъул хоҳад буд, то андозае аз барнома вобаста аст. Дар ҳолати мо, пеш аз барқарор кардани пайвастшавӣ, мубодилаи миқдори ками маълумот лозим аст.
  • Teeworlds Master Server: Он аз ҷониби серверҳои дигар барои таблиғи мавҷудияти онҳо ва аз ҷониби муштариён барои пайдо кардани серверҳои ҷамъиятӣ истифода мешавад. Гарчанде ки ин талаб карда намешавад (мизоҷон ҳамеша метавонанд ба сервере, ки дар бораи онҳо медонанд, дастӣ пайваст шаванд), хуб мебуд, ки бозигарон дар бозиҳо бо одамони тасодуфӣ иштирок кунанд.

Мо тасмим гирифтем, ки серверҳои STUN-и ройгони Google-ро истифода барем ва худамон як сервери TURNро ҷойгир кардем.

Барои ду нуқтаи охир мо истифода бурдем Сӯхтор:

  • Сервери асосии Teeworlds хеле содда иҷро карда мешавад: ҳамчун рӯйхати объектҳо дорои маълумот (ном, IP, харита, режим, ...) ҳар як сервери фаъол. Серверҳо объекти худро нашр ва навсозӣ мекунанд ва муштариён тамоми рӯйхатро гирифта, ба плеер намоиш медиҳанд. Мо инчунин рӯйхатро дар саҳифаи хонагӣ ҳамчун HTML намоиш медиҳем, то бозигарон танҳо ба сервер клик кунанд ва мустақиман ба бозӣ баранд.
  • Сигнал бо татбиқи розеткаҳои мо, ки дар боби оянда тавсиф шудааст, зич алоқаманд аст.

Гузаронидани бозии мултипликатори аз C++ ба веб бо Cheerp, WebRTC ва Firebase
Рӯйхати серверҳо дар дохили бозӣ ва дар саҳифаи хонагӣ

Амалисозии розеткаҳо

Мо мехоҳем як API эҷод кунем, ки то ҳадди имкон ба Posix UDP Sockets наздик бошад, то шумораи тағиротҳои лозимиро кам кунад.

Мо инчунин мехоҳем ҳадди ақали заруриро барои мубодилаи соддатарин тавассути шабака амалӣ созем.

Масалан, ба мо масири воқеӣ лозим нест: ҳамаи ҳамсолон дар ҳамон "LAN виртуалӣ" ҳастанд, ки бо мисоли мушаххаси пойгоҳи додаҳои Firebase алоқаманданд.

Аз ин рӯ, мо ба суроғаҳои IP-и беназир ниёз надорем: арзишҳои калидии ягонаи Firebase (монанд ба номҳои домейнҳо) барои ба таври беназир муайян кардани ҳамсолон кифояанд ва ҳар як ҳамсол ба таври маҳаллӣ ба ҳар як калид, ки бояд тарҷума шавад, суроғаҳои IP-и "қалбакӣ" таъин мекунад. Ин эҳтиёҷоти таъини суроғаи IP-и умумиро, ки кори ғайриоддӣ аст, комилан аз байн мебарад.

Ин аст ҳадди ақали API, ки мо бояд татбиқ кунем:

// Create and destroy a socket
int socket();
int close(int fd);
// Bind a socket to a port, and publish it on Firebase
int bind(int fd, AddrInfo* addr);
// Send a packet. This lazily create a WebRTC connection to the 
// peer when necessary
int sendto(int fd, uint8_t* buf, int len, const AddrInfo* addr);
// Receive the packets destined to this socket
int recvfrom(int fd, uint8_t* buf, int len, AddrInfo* addr);
// Be notified when new packets arrived
int recvCallback(Callback cb);
// Obtain a local ip address for this peer key
uint32_t resolve(client::String* key);
// Get the peer key for this ip
String* reverseResolve(uint32_t addr);
// Get the local peer key
String* local_key();
// Initialize the library with the given Firebase database and 
// WebRTc connection options
void init(client::FirebaseConfig* fb, client::RTCConfiguration* ice);

API оддӣ ва ба Posix Sockets API шабоҳат дорад, аммо якчанд фарқиятҳои муҳим дорад: сабти зангҳои бозгашт, таъини IP-ҳои маҳаллӣ ва пайвастҳои танбал.

Бақайдгирии зангҳои бозгашт

Ҳатто агар барномаи аслӣ I/O-ро истифода набарад, код бояд барои кор дар браузери веб рефакторатсия карда шавад.

Сабаби ин дар он аст, ки гардиши ҳодиса дар браузер аз барнома пинҳон аст (хоҳ JavaScript ё WebAssembly).

Дар муҳити маҳаллӣ мо метавонем чунин код нависем

while(running) {
  select(...); // wait for I/O events
  while(true) {
    int r = readfrom(...); // try to read
    if (r < 0 && errno == EWOULDBLOCK) // no more data available
      break;
    ...
  }
  ...
}

Агар ҳалқаи ҳодиса барои мо пинҳон бошад, пас мо бояд онро ба чунин чизе табдил диҳем:

auto cb = []() { // this will be called when new data is available
  while(true) {
    int r = readfrom(...); // try to read
    if (r < 0 && errno == EWOULDBLOCK) // no more data available
      break;
    ...
  }
  ...
};
recvCallback(cb); // register the callback

Таъиноти IP маҳаллӣ

ID-ҳои гиреҳ дар "шабакаи" мо суроғаҳои IP нестанд, балки калидҳои Firebase мебошанд (онҳо сатрҳое мебошанд, ки чунинанд: -LmEC50PYZLCiCP-vqde ).

Ин қулай аст, зеро ба мо механизми таъини IP-ҳо ва санҷиши нотакрории онҳо (инчунин нобуд кардани онҳо пас аз қатъ шудани муштарӣ) лозим нест, аммо аксар вақт зарур аст, ки ҳамсолонро бо арзиши ададӣ муайян кунем.

Маҳз барои ҳамин функсияҳо истифода мешаванд. resolve и reverseResolve: Ариза арзиши сатри калидро ба тариқи муайян қабул мекунад (тавассути вуруди корбар ё тавассути сервери асосӣ) ва метавонад онро ба суроғаи IP барои истифодаи дохилӣ табдил диҳад. Қисми боқимондаи API низ ин арзишро ба ҷои сатр барои содда қабул мекунад.

Ин ба ҷустуҷӯи DNS монанд аст, аммо дар мизоҷ ба таври маҳаллӣ иҷро карда мешавад.

Яъне, суроғаҳои IP-ро дар байни муштариёни гуногун тақсим кардан мумкин нест ва агар ягон намуди идентификатори глобалӣ лозим шавад, он бояд бо роҳи дигар тавлид шавад.

Пайвастшавӣ суст

UDP ба пайвастшавӣ ниёз надорад, аммо тавре ки мо дидем, WebRTC пеш аз он ки интиқоли маълумот байни ду ҳамсолро оғоз кунад, раванди тӯлонии пайвастшавиро талаб мекунад.

Агар мо хоҳем, ки ҳамон сатҳи абстраксияро таъмин кунем, (sendto/recvfrom бо ҳамсолони худсарона бидуни пайвасти пешакӣ), пас онҳо бояд дар дохили API пайвасти "танбал" (таъхир) анҷом диҳанд.

Ин аст он чизе ки ҳангоми муоширати муқаррарии байни "сервер" ва "мизоҷ" ҳангоми истифодаи UDP рӯй медиҳад ва китобхонаи мо бояд чӣ кор кунад:

  • Зангҳои сервер bind()то ба системаи оператсионӣ бигӯяд, ки вай мехоҳад бастаҳоро дар бандари муайяншуда қабул кунад.

Ба ҷои ин, мо дар зери калиди сервер бандари кушодаро ба Firebase нашр мекунем ва рӯйдодҳоро дар зер дарахти он гӯш мекунем.

  • Зангҳои сервер recvfrom(), қабули бастаҳое, ки аз ҳама гуна ҳост дар ин порт меоянд.

Дар ҳолати мо, мо бояд навбати воридотии бастаҳои ба ин порт фиристодашударо тафтиш кунем.

Ҳар як порт навбати худро дорад ва мо ба оғози датаграммаҳои WebRTC манба ва бандарҳои таъинотро илова мекунем, то бидонем, ки ҳангоми расидани бастаи нав ба кадом навбат фиристодан лозим аст.

Занг баста намешавад, бинобар ин, агар бастаҳо мавҷуд набошанд, мо танҳо -1-ро бармегардонем ва танзим мекунем errno=EWOULDBLOCK.

  • Мизоҷ IP ва бандари серверро бо ягон воситаи беруна қабул мекунад ва занг мезанад sendto(). Ин инчунин занги дохилӣ мекунад. bind(), бинобар ин минбаъд recvfrom() ҷавобро бидуни иҷрои возеҳ иҷро хоҳад кард.

Дар ҳолати мо, муштарӣ калиди сатрро берун аз он қабул мекунад ва функсияро истифода мебарад resolve() Барои гирифтани суроғаи IP.

Дар ин лаҳза, мо дастфишори WebRTC-ро оғоз мекунем, агар ду ҳамсол то ҳол ба ҳамдигар пайваст нашуда бошанд. Пайвастшавӣ ба бандарҳои гуногуни ҳамсол аз ҳамон WebRTC DataChannel истифода мебаранд.

Мо инчунин бавосита иҷро мекунем bind()то ки сервер дар оянда дубора пайваст шавад sendto() агар бо ягон сабаб баста шавад.

Вақте ки муштарӣ пешниҳоди SDP-и худро дар зери маълумоти порти сервер дар Firebase менависад, сервер аз пайвасти муштарӣ огоҳ карда мешавад ва сервер бо посухи худ дар он ҷо посух медиҳад.

Дар диаграммаи зер намунаи ҷараёни паёмҳо барои схемаи розетка ва интиқоли паёми аввал аз муштарӣ ба сервер нишон дода шудааст:

Гузаронидани бозии мултипликатори аз C++ ба веб бо Cheerp, WebRTC ва Firebase
Диаграммаи пурраи марҳилаи пайвастшавӣ байни муштарӣ ва сервер

хулоса

Агар шумо то ин вақт хонда бошед, эҳтимол шумо ба дидани назария дар амал таваҷҷӯҳ доред. Бозиро дар он бозӣ кардан мумкин аст teeworlds.leaningtech.com, кӯшиш кун!


Вохурии рафикона байни хамкорон

Рамзи китобхонаи шабакавӣ ба таври ройгон дастрас аст Github. Ба сӯҳбат дар канали мо ҳамроҳ шавед Gitter!

Манбаъ: will.com

Илова Эзоҳ