හැඳින්වීම
අපේ සමාගම
එහි යෙදුමේ උදාහරණයක් ලෙස, අපි බහු ක්රීඩක ක්රීඩාවක් වෙබයට ගෙන යාමට තීරණය කර තෝරා ගත්තෙමු
Teeworlds බ්රවුසරයේ ධාවනය වේ
අත්හදා බැලීම සඳහා මෙම ව්යාපෘතිය භාවිතා කිරීමට අපි තීරණය කළා ජාල කේතය අන්තර්ජාලයට ගෙනයාම සඳහා පොදු විසඳුම්. මෙය සාමාන්යයෙන් පහත දැක්වෙන ක්රම වලින් සිදු කෙරේ:
- XMLHttpRequest/fetch, ජාල කොටස සමන්විත වන්නේ HTTP ඉල්ලීම් වලින් පමණක් නම්, හෝ
- වෙබ් සොකට්.
විසඳුම් දෙකටම සේවාදායක පැත්තේ සේවාදායක සංරචකයක් සත්කාරකත්වය අවශ්ය වන අතර ප්රවාහන ප්රොටෝකෝලයක් ලෙස භාවිතා කිරීමට ඉඩ නොදේ
තුන්වන ක්රමයක් තිබේ - බ්රවුසරයෙන් ජාලය භාවිතා කරන්න:
කෙසේ වෙතත්, මෙය අතිරේක දුෂ්කරතාවයකින් යුක්ත වේ: WebRTC සම වයසේ මිතුරන් දෙදෙනෙකුට සන්නිවේදනය කිරීමට පෙර, ඔවුන් සම්බන්ධ වීමට සාපේක්ෂව සංකීර්ණ අතට අත දීමක් සිදු කළ යුතුය, ඒ සඳහා තෙවන පාර්ශවීය ආයතන කිහිපයක් (සංඥා සේවාදායකයක් සහ සේවාදායක එකක් හෝ කිහිපයක් අවශ්ය වේ.
ඉතා මැනවින්, අපි WebRTC අභ්යන්තරව භාවිතා කරන ජාල API නිර්මාණය කිරීමට කැමතියි, නමුත් සම්බන්ධතාවයක් පිහිටුවීමට අවශ්ය නොවන UDP Sockets අතුරුමුහුණතකට හැකි තරම් සමීප වේ.
යෙදුම් කේතයට (අපගේ ව්යාපෘතියේ හැකිතාක් අඩුවෙන් වෙනස් කිරීමට අපට අවශ්ය වූ) සංකීර්ණ තොරතුරු හෙළිදරව් කිරීමකින් තොරව WebRTC හි ප්රයෝජන ගැනීමට මෙය අපට ඉඩ සලසයි.
අවම WebRTC
WebRTC යනු ශ්රව්ය, දෘශ්ය සහ අත්තනෝමතික දත්ත peer-to-peer සම්ප්රේෂණය සපයන බ්රවුසරවල තිබෙන API කට්ටලයකි.
ICE නම් යාන්ත්රණයක් හරහා STUN සහ/හෝ TURN සේවාදායක භාවිතයෙන් සම වයසේ මිතුරන් අතර සම්බන්ධතාවය (එක් හෝ දෙපැත්තේ NAT තිබුණත්) ස්ථාපිත වේ. SDP ප්රොටෝකෝලය පිරිනැමීම සහ පිළිතුරු හරහා සම වයසේ මිතුරන් ICE තොරතුරු සහ නාලිකා පරාමිතීන් හුවමාරු කර ගනී.
වාව්! එක් වරකට කෙටි යෙදුම් කීයක් තිබේද? මෙම පදවල තේරුම කෙටියෙන් පැහැදිලි කරමු:
NAT සඳහා සැසි ගමන් උපයෝගිතා (STUN) — NAT මගහැරීම සහ ධාරකය සමඟ සෘජුවම දත්ත හුවමාරු කිරීම සඳහා යුගලයක් (IP, port) ලබා ගැනීම සඳහා වූ ප්රොටෝකෝලය. ඔහු තම කාර්යය සම්පූර්ණ කිරීමට සමත් වුවහොත්, සම වයසේ මිතුරන්ට ස්වාධීනව එකිනෙකා සමඟ දත්ත හුවමාරු කර ගත හැකිය.NAT වටා රිලේ භාවිතා කරමින් ගමන් කිරීම (ටර්න් කරන්න) NAT traversal සඳහා ද භාවිතා වේ, නමුත් එය සම වයසේ මිතුරන් දෙදෙනාටම පෙනෙන ප්රොක්සියක් හරහා දත්ත යොමු කිරීමෙන් මෙය ක්රියාත්මක කරයි. එය ප්රමාදය එකතු කරන අතර STUN ට වඩා ක්රියාත්මක කිරීමට මිල අධික වේ (එය මුළු සන්නිවේදන සැසිය පුරාවටම යොදන නිසා), නමුත් සමහර විට එය එකම විකල්පය වේ.අන්තර් ක්රියාකාරී සම්බන්ධතා පිහිටුවීම (අයිස්ක්රීම්) සම වයසේ මිතුරන් සෘජුවම සම්බන්ධ කිරීමෙන් ලබාගත් තොරතුරු මෙන්ම ඕනෑම STUN සහ TURN සේවාදායක සංඛ්යාවකට ලැබෙන තොරතුරු මත පදනම්ව මිතුරන් දෙදෙනෙකු සම්බන්ධ කිරීමේ හොඳම ක්රමය තෝරා ගැනීමට භාවිතා කරයි.සැසි විස්තර ප්රොටෝකෝලය (එස්ඩීපී) සම්බන්ධතා නාලිකා පරාමිතීන් විස්තර කිරීම සඳහා ආකෘතියකි, උදාහරණයක් ලෙස, ICE අපේක්ෂකයින්, බහුමාධ්ය කෝඩෙක්ස් (ශ්රව්ය/වීඩියෝ නාලිකාවක් සම්බන්ධයෙන්) යනාදිය... සම වයසේ මිතුරන්ගෙන් එක් අයෙකු SDP පිරිනැමීමක් යවන අතර, දෙවැන්න SDP පිළිතුරක් සමඟ ප්රතිචාර දක්වයි .. මෙයින් පසු, නාලිකාවක් නිර්මාණය වේ.
එවැනි සම්බන්ධතාවයක් ඇති කිරීම සඳහා, සම වයසේ මිතුරන් STUN සහ TURN සේවාදායක වෙතින් ලැබෙන තොරතුරු රැස් කර ඒවා එකිනෙකා සමඟ හුවමාරු කර ගත යුතුය.
ගැටලුව වන්නේ ඔවුන්ට තවමත් සෘජුව සන්නිවේදනය කිරීමේ හැකියාවක් නොමැති වීමයි, එබැවින් මෙම දත්ත හුවමාරු කිරීම සඳහා සංගීත කණ්ඩායමෙන් පිටත යාන්ත්රණයක් පැවතිය යුතුය: සංඥා සේවාදායකයක්.
සංඥා සේවාදායකයක් ඉතා සරල විය හැක, මන්ද එහි එකම කාර්යය වන්නේ අතට අත දීමේදී සම වයසේ මිතුරන් අතර දත්ත යැවීමයි (පහත රූප සටහනේ පෙන්වා ඇති පරිදි).
සරල කළ WebRTC අතට අත දීමේ අනුපිළිවෙල රූප සටහන
Teeworlds Network Model දළ විශ්ලේෂණය
Teeworlds ජාල ගෘහ නිර්මාණ ශිල්පය ඉතා සරල ය:
- සේවාදායක සහ සේවාදායක සංරචක එකිනෙකට වෙනස් වැඩසටහන් දෙකකි.
- සේවාදායකයන් ක්රීඩාවට ඇතුළු වන්නේ එක් එක් සේවාදායක කිහිපයකින් එකකට සම්බන්ධ වීමෙනි, ඒ සෑම එකක්ම වරකට එක් ක්රීඩාවක් පමණක් සත්කාරකත්වය දරයි.
- ක්රීඩාවේ සියලුම දත්ත හුවමාරුව සේවාදායකය හරහා සිදු කෙරේ.
- ක්රීඩා සේවාලාභියා තුළ ප්රදර්ශනය වන සියලුම පොදු සේවාදායක ලැයිස්තුවක් එකතු කිරීමට විශේෂ ප්රධාන සේවාදායකයක් භාවිතා කරයි.
දත්ත හුවමාරුව සඳහා WebRTC භාවිතයට ස්තූතියි, අපට ක්රීඩාවේ සේවාදායක සංරචකය සේවාදායකයා සිටින බ්රව්සරයට මාරු කළ හැකිය. මෙය අපට කදිම අවස්ථාවක් ලබා දෙයි...
සේවාදායකයන් ඉවත් කරන්න
සේවාදායක තර්කනය නොමැතිකම හොඳ වාසියක් ඇත: අපට සම්පූර්ණ යෙදුම Github පිටු මත හෝ Cloudflare පිටුපස ඇති අපගේම දෘඪාංග මත ස්ථිතික අන්තර්ගතයක් ලෙස යෙදවිය හැක, එමඟින් වේගවත් බාගත කිරීම් සහ නොමිලේ වැඩි වේලාවක් සහතික කරයි. ඇත්ත වශයෙන්ම, අපට ඔවුන් ගැන අමතක කළ හැකි අතර, අපි වාසනාවන්ත නම් සහ ක්රීඩාව ජනප්රිය වුවහොත්, යටිතල පහසුකම් නවීකරණය කිරීමට සිදු නොවේ.
කෙසේ වෙතත්, පද්ධතිය ක්රියාත්මක වීමට නම්, අපට තවමත් බාහිර ගෘහ නිර්මාණ ශිල්පයක් භාවිතා කිරීමට සිදුවේ:
- STUN සේවාදායකයන් එකක් හෝ කිහිපයක්: අපට තෝරා ගැනීමට නොමිලේ විකල්ප කිහිපයක් තිබේ.
- අඩුම තරමින් එක් TURN සේවාදායකයක්: මෙහි නොමිලේ විකල්ප නොමැත, එබැවින් අපට අපගේම ලෙස පිහිටුවීමට හෝ සේවාව සඳහා ගෙවීමට හැකිය. වාසනාවකට මෙන්, බොහෝ විට සම්බන්ධතාවය STUN සේවාදායකයන් හරහා ස්ථාපිත කළ හැකිය (සහ සත්ය p2p ලබා දෙයි), නමුත් ආපසු හැරීමේ විකල්පයක් ලෙස TURN අවශ්ය වේ.
- සංඥා සේවාදායකය: අනෙක් අංශ දෙක මෙන් නොව, සංඥා කිරීම සම්මත නොවේ. සංඥා සේවාදායකය ඇත්ත වශයෙන්ම වගකිව යුත්තේ කුමක් සඳහාද යන්න යෙදුම මත රඳා පවතී. අපගේ නඩුවේදී, සම්බන්ධතාවයක් ස්ථාපනය කිරීමට පෙර, කුඩා දත්ත ප්රමාණයක් හුවමාරු කිරීම අවශ්ය වේ.
- Teeworlds Master Server: එය වෙනත් සේවාදායකයන් විසින් ඔවුන්ගේ පැවැත්ම ප්රචාරණය කිරීමට සහ සේවාලාභීන් විසින් පොදු සේවාදායකයන් සොයා ගැනීමට භාවිතා කරයි. එය අවශ්ය නොවන අතර (සේවාදායකයින්ට සෑම විටම තමන් දන්නා සේවාදායකයකට අතින් සම්බන්ධ විය හැක), ක්රීඩකයින්ට අහඹු පුද්ගලයින් සමඟ ක්රීඩා වලට සහභාගී විය හැකි පරිදි තිබීම සතුටක්.
අපි Google හි නොමිලේ STUN සේවාදායක භාවිතා කිරීමට තීරණය කළ අතර, එක් TURN සේවාදායකයක් අප විසින්ම යොදවා ගත්තෙමු.
අපි භාවිතා කළ අවසාන කරුණු දෙක සඳහා
- Teeworlds ප්රධාන සේවාදායකය ඉතා සරලව ක්රියාත්මක වේ: එක් එක් ක්රියාකාරී සේවාදායකයේ තොරතුරු (නම, IP, සිතියම, මාදිලිය, ...) අඩංගු වස්තූන් ලැයිස්තුවක් ලෙස. සේවාදායකයන් ඔවුන්ගේම වස්තුව ප්රකාශයට පත් කර යාවත්කාලීන කරයි, සහ සේවාලාභීන් සම්පූර්ණ ලැයිස්තුව ගෙන එය ක්රීඩකයාට ප්රදර්ශනය කරයි. අපි මුල් පිටුවේ ලැයිස්තුව HTML ලෙස ප්රදර්ශනය කරන්නෙමු, එවිට ක්රීඩකයින්ට සේවාදායකය මත ක්ලික් කර කෙලින්ම ක්රීඩාවට ගෙන යා හැකිය.
- සංඥා කිරීම අපගේ සොකට් ක්රියාවට නැංවීමට සමීපව සම්බන්ධ වේ, ඊළඟ කොටසේ විස්තර කර ඇත.
ක්රීඩාව තුළ සහ මුල් පිටුවෙහි ඇති සේවාදායකයන් ලැයිස්තුව
සොකට් ක්රියාත්මක කිරීම
අවශ්ය වෙනස්කම් ප්රමාණය අවම කිරීම සඳහා හැකි තරම් Posix UDP Sockets වලට ආසන්න API එකක් සෑදීමට අපට අවශ්යය.
ජාලය හරහා සරලම දත්ත හුවමාරුව සඳහා අවශ්ය අවමය ක්රියාත්මක කිරීමට ද අපට අවශ්යය.
උදාහරණයක් ලෙස, අපට සැබෑ මාර්ගගත කිරීම් අවශ්ය නොවේ: සියලුම සම වයසේ මිතුරන් නිශ්චිත Firebase දත්ත සමුදා අවස්ථාවක් හා සම්බන්ධ එකම "අථත්ය LAN" මත සිටී.
එබැවින්, අපට අනන්ය 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 ට සමාන වේ, නමුත් වැදගත් වෙනස්කම් කිහිපයක් ඇත: ආපසු ඇමතුම් ලොග් කිරීම, දේශීය IPs පැවරීම සහ කම්මැලි සම්බන්ධතා.
ආපසු ඇමතුම් ලියාපදිංචි කිරීම
මුල් වැඩසටහන අවහිර නොවන 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 පැවරුම
අපගේ "ජාලයේ" ඇති නෝඩ් හැඳුනුම්පත් 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 පිරිනැමීම ලියන විට සේවාදායකයාගේ සම්බන්ධතාවය ගැන සේවාදායකයට දන්වනු ලබන අතර සේවාදායකය එහි ප්රතිචාරය සමඟ ප්රතිචාර දක්වයි.
පහත රූප සටහන සොකට් යෝජනා ක්රමයක් සඳහා පණිවිඩ ප්රවාහය සහ සේවාලාභියාගෙන් සේවාදායකයට පළමු පණිවිඩය සම්ප්රේෂණය කිරීමේ උදාහරණයක් පෙන්වයි:
සේවාලාභියා සහ සේවාදායකය අතර සම්බන්ධතා අවධියේ සම්පූර්ණ රූප සටහන
නිගමනය
ඔබ මෙතරම් දුරක් කියවා ඇත්නම්, න්යාය ක්රියාත්මක වන ආකාරය දැකීමට ඔබ උනන්දු වනු ඇත. ක්රීඩාව ක්රීඩා කළ හැකිය
සගයන් අතර සුහද ගැලපීම
ජාල පුස්තකාල කේතය නොමිලේ ලබා ගත හැක
මූලාශ්රය: www.habr.com