{"id":475664,"date":"2026-04-13T05:57:44","date_gmt":"2026-04-13T05:57:44","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=475664"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=475664","title":{"rendered":"dc.send(file) \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442: \u0447\u0442\u043e \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u043d\u0443\u0436\u043d\u043e \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0444\u0430\u0439\u043b\u0430 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3f9\/2a7\/a6f\/3f92a7a6f0534360a657a8f9765b7ccf.png\" alt=\"\u0420\u0438\u0441. 1. dc.send(file) \u043d\u0435 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0444\u0430\u0439\u043b \u0443\u0436\u0435 \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d.\" title=\"\u0420\u0438\u0441. 1. dc.send(file) \u043d\u0435 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0444\u0430\u0439\u043b \u0443\u0436\u0435 \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d.\" width=\"1280\" height=\"720\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/3f9\/2a7\/a6f\/3f92a7a6f0534360a657a8f9765b7ccf.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3f9\/2a7\/a6f\/3f92a7a6f0534360a657a8f9765b7ccf.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0420\u0438\u0441. 1. dc.send(file) \u043d\u0435 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0444\u0430\u0439\u043b \u0443\u0436\u0435 \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d.<\/figcaption><\/div>\n<\/figure>\n<p>\u0421\u0430\u043c\u0430\u044f \u043e\u043f\u0430\u0441\u043d\u0430\u044f \u0438\u043b\u043b\u044e\u0437\u0438\u044f \u0432 WebRTC-\u0444\u0430\u0439\u043b\u043e\u043e\u0431\u043c\u0435\u043d\u0435 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u0430\u043a:<\/p>\n<pre><code class=\"javascript\">const dc = pc.createDataChannel('file');for (let offset = 0; offset &lt; file.size; offset += CHUNK) {  dc.send(file.slice(offset, offset + CHUNK));}dc.send(JSON.stringify({ type: 'transfer_done' }));\/\/ \u0413\u043e\u0442\u043e\u0432\u043e! ...\u0438\u043b\u0438 \u043d\u0435\u0442?<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043f\u0440\u0430\u0432\u0434\u043e\u043f\u043e\u0434\u043e\u0431\u043d\u043e. DataChannel \u043e\u0442\u043a\u0440\u044b\u0442, \u0447\u0430\u043d\u043a\u0438 \u043b\u0435\u0442\u044f\u0442, <code>transfer_done<\/code> \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d. \u0412 \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e. \u0412 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435 \u2013 \u043d\u0435\u0442. \u041d\u0430 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u043c relay \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441 \u043f\u043e\u043a\u0430\u0436\u0435\u0442 100%, \u0430 \u0444\u0430\u0439\u043b \u043d\u0430 \u0434\u0438\u0441\u043a\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0440\u0435\u0437\u0430\u043d \u2013 \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e <code>send()<\/code> \u043a\u043b\u0430\u0434\u0451\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 SCTP-\u0431\u0443\u0444\u0435\u0440, \u0430 \u043d\u0435 \u00ab\u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432 \u0441\u0435\u0442\u044c\u00bb. \u041f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044c \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043d\u0435 \u0433\u043e\u0442\u043e\u0432 \u043a \u0437\u0430\u043f\u0438\u0441\u0438. \u041e\u0431\u044a\u0435\u043a\u0442 <code>File<\/code> \u043d\u0435 \u043f\u0435\u0440\u0435\u0436\u0438\u0432\u0451\u0442 refresh. \u0410 signaling-\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u0443\u043c\u0435\u0440\u0435\u0442\u044c \u0440\u0430\u043d\u044c\u0448\u0435 DataChannel \u2013 \u0438 <code>peer_left<\/code> \u043e\u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043d\u0435 \u043e\u0448\u0438\u0431\u043a\u043e\u0439, \u0430 \u0448\u0442\u0430\u0442\u043d\u044b\u043c \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0435\u043c.<\/p>\n<p>\u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u2013 \u0440\u0430\u0437\u0431\u043e\u0440 \u0448\u0435\u0441\u0442\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c (\u0438 \u043e\u0434\u043d\u043e\u0433\u043e Safari-\u0441\u044e\u0440\u043f\u0440\u0438\u0437\u0430), \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u044e\u0442 \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0435 \u0444\u0430\u0439\u043b\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 WebRTC \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435. \u041d\u0438 \u043e\u0434\u043d\u0430 \u0438\u0437 \u043d\u0438\u0445 \u043d\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0430 \u0432 \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0430\u0445.<\/p>\n<p>\u0421\u0442\u0430\u0442\u044c\u044f \u0440\u043e\u0434\u0438\u043b\u0430\u0441\u044c \u043f\u043e \u0438\u0442\u043e\u0433\u0430\u043c \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0430\u0434 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c P2P-\u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0444\u0430\u0439\u043b\u043e\u0432. \u0427\u0442\u043e\u0431\u044b \u0434\u0430\u043b\u044c\u0448\u0435 \u0442\u0435\u0440\u043c\u0438\u043d\u044b \u0431\u044b\u043b\u0438 \u043f\u043e\u043d\u044f\u0442\u043d\u044b \u2013 \u0432\u043e\u0442 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0432 \u0434\u0432\u0443\u0445 \u0441\u043b\u043e\u0432\u0430\u0445.<\/p>\n<p>\u0424\u0430\u0439\u043b\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u044e\u0442\u0441\u044f \u043c\u0435\u0436\u0434\u0443 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430\u043c\u0438 \u0447\u0435\u0440\u0435\u0437 WebRTC DataChannel. \u0412 \u043e\u0431\u044b\u0447\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u2013 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e (P2P), \u043f\u0440\u0438 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u0440\u044f\u043c\u043e\u0433\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u2013 \u0447\u0435\u0440\u0435\u0437 TURN relay. \u0421\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0430\u0439\u043b\u0430 \u043d\u0438 \u0432 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u0440\u0435\u0436\u0438\u043c\u043e\u0432 \u2013 \u043e\u043d \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0438\u0440\u0443\u0435\u0442: REST API \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438, WebSocket \u0434\u043b\u044f signaling, \u043c\u0430\u0448\u0438\u043d\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u0432. \u0414\u0430\u0436\u0435 \u0447\u0435\u0440\u0435\u0437 relay DTLS-\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f end-to-end \u2013 relay \u043f\u0440\u043e\u043a\u0441\u0438\u0440\u0443\u0435\u0442 \u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043f\u043e\u0442\u043e\u043a, \u043d\u0435 \u0438\u043c\u0435\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u043c\u0443.<\/p>\n<p>Relay \u0441\u0442\u043e\u0438\u0442 \u0434\u0435\u043d\u0435\u0433 (\u0442\u0440\u0430\u0444\u0438\u043a, VPS), \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 coturn \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u0436\u0451\u0441\u0442\u043a\u0438\u0435 \u043b\u0438\u043c\u0438\u0442\u044b: \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 \u0444\u0430\u0439\u043b\u0430 \u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0447 \u0432 \u0434\u0435\u043d\u044c. \u042d\u0442\u043e \u0430\u0432\u0430\u0440\u0438\u0439\u043d\u044b\u0439 \u0437\u0430\u043f\u0430\u0441\u043d\u043e\u0439 \u043f\u0443\u0442\u044c, \u0430 \u043d\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0440\u0435\u0436\u0438\u043c. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0441\u0432\u043e\u0439 TURN-\u0441\u0435\u0440\u0432\u0435\u0440 (Metered, Twilio, Xirsys, \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u2013 \u043d\u0435\u0432\u0430\u0436\u043d\u043e; \u0443 Metered, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0439 \u0442\u0430\u0440\u0438\u0444 \u0434\u0430\u0451\u0442 500 GB). \u0423\u0447\u0451\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 custom TURN \u0448\u0438\u0444\u0440\u0443\u044e\u0442\u0441\u044f \u043d\u0435\u044d\u043a\u0441\u0442\u0440\u0430\u0433\u0438\u0440\u0443\u0435\u043c\u044b\u043c \u043a\u043b\u044e\u0447\u043e\u043c \u0432 IndexedDB \u0438 \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u043f\u043e\u043a\u0438\u0434\u0430\u044e\u0442 \u0431\u0440\u0430\u0443\u0437\u0435\u0440. \u041d\u0430 custom TURN \u043a\u0432\u043e\u0442 \u0441\u043e \u0441\u0442\u043e\u0440\u043e\u043d\u044b \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043d\u0435\u0442.<\/p>\n<p>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430: P2P \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0441 signaling \u0438 relay fallback<\/p>\n<p>\u0412 \u0432\u0430\u0448\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043c\u043e\u0436\u0435\u0442 \u043d\u0435 \u0431\u044b\u0442\u044c custom TURN \u0438\u043b\u0438 relay-\u043a\u0432\u043e\u0442. \u041d\u043e \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u2013 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0431\u0443\u0444\u0435\u0440\u043e\u043c, \u0433\u043e\u0442\u043e\u0432\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f, \u043f\u0435\u0440\u0435\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435, \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u043d\u044b\u0439 <code>File<\/code> \u2013 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0443\u0442 \u0432 \u043b\u044e\u0431\u043e\u043c WebRTC-\u0444\u0430\u0439\u043b\u043e\u043e\u0431\u043c\u0435\u043d\u043d\u0438\u043a\u0435. \u0414\u0430\u043b\u044c\u0448\u0435 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u043c \u0432\u0441\u0435 \u0448\u0435\u0441\u0442\u044c, \u0441 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u0438\u043a\u043e\u0439 \u0438\u0437 \u043d\u0430\u0448\u0435\u0433\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u044f.<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/58c\/e39\/aba\/58ce39aba9e773f836c83902147119fe.png\" alt=\"\u0420\u0438\u0441. 2. \u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438. \u041f\u0440\u044f\u043c\u043e\u0435 P2P-\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u2013 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0443\u0442\u044c, TURN relay (\u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 coturn \u0438\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439) \u2013 fallback \u043f\u0440\u0438 NAT, firewall \u0438\u043b\u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0439 \u0441\u0435\u0442\u0438. \u0421\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0430\u0439\u043b\u0430 \u0438\u0434\u0451\u0442 \u043c\u0438\u043c\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0434\u0430\u0436\u0435 \u043f\u0440\u0438 relay: DTLS-\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f end-to-end.\" title=\"\u0420\u0438\u0441. 2. \u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438. \u041f\u0440\u044f\u043c\u043e\u0435 P2P-\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u2013 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0443\u0442\u044c, TURN relay (\u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 coturn \u0438\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439) \u2013 fallback \u043f\u0440\u0438 NAT, firewall \u0438\u043b\u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0439 \u0441\u0435\u0442\u0438. \u0421\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0430\u0439\u043b\u0430 \u0438\u0434\u0451\u0442 \u043c\u0438\u043c\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0434\u0430\u0436\u0435 \u043f\u0440\u0438 relay: DTLS-\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f end-to-end.\" width=\"4304\" height=\"2572\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/58c\/e39\/aba\/58ce39aba9e773f836c83902147119fe.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/58c\/e39\/aba\/58ce39aba9e773f836c83902147119fe.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0420\u0438\u0441. 2. \u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438. \u041f\u0440\u044f\u043c\u043e\u0435 P2P-\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u2013 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0443\u0442\u044c, TURN relay (\u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 coturn \u0438\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439) \u2013 fallback \u043f\u0440\u0438 NAT, firewall \u0438\u043b\u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0439 \u0441\u0435\u0442\u0438. \u0421\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0430\u0439\u043b\u0430 \u0438\u0434\u0451\u0442 \u043c\u0438\u043c\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0434\u0430\u0436\u0435 \u043f\u0440\u0438 relay: DTLS-\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f end-to-end.<\/figcaption><\/div>\n<\/figure>\n<h3>\u0421\u0435\u0440\u0432\u0435\u0440 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442, \u043d\u043e \u0431\u0435\u0437 \u043d\u0435\u0433\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442<\/h3>\n<p>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0439: \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0430\u0439\u043b\u0430 \u0438\u0434\u0451\u0442 \u043c\u0438\u043c\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 WebRTC DataChannel, \u0441\u0435\u0440\u0432\u0435\u0440 \u0435\u0433\u043e \u043d\u0435 \u0432\u0438\u0434\u0438\u0442 \u0438 \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442. \u041d\u043e \u00ab\u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442 \u0444\u0430\u0439\u043b\u044b\u00bb \u043d\u0435 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u00ab\u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u043e\u0447\u0442\u0438 \u043d\u0435 \u043d\u0443\u0436\u0435\u043d\u00bb.<\/p>\n<p>\u0412\u043e\u043a\u0440\u0443\u0433 \u043a\u0430\u0436\u0434\u043e\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u2013 REST API \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438 \u0440\u0435\u0437\u043e\u043b\u0432\u0430 \u00ab\u0448\u0430\u0440\u044b\u00bb (share), WebSocket \u0434\u043b\u044f signaling, \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u0430\u044f \u043c\u0430\u0448\u0438\u043d\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u0438 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<p>\u0423 \u0448\u0430\u0440\u044b \u0435\u0441\u0442\u044c \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b. \u041a\u043b\u044e\u0447\u0435\u0432\u043e\u0435: \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0448\u0430\u0440\u0430 \u043d\u0435 \u0443\u043c\u0438\u0440\u0430\u0435\u0442, \u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0432\u00a0<code>active<\/code>\u00a0\u2013 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043e\u043d\u043b\u0430\u0439\u043d \u0438 \u0436\u0434\u0451\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f. \u0415\u0441\u043b\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c \u0437\u0430\u043d\u044f\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0435\u0439, \u043d\u043e\u0432\u044b\u0439 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044c \u043f\u043e\u043f\u0430\u0434\u0430\u0435\u0442 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u043c\u0435\u0441\u0442\u043e \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0439.<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f4f\/5f7\/300\/f4f5f7300ddc85b19b371401da672196.png\" alt=\"\u0420\u0438\u0441. 3. \u0416\u0438\u0437\u043d\u0435\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u0448\u0430\u0440\u044b. \u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u0446\u0438\u043a\u043b active \u2192 matched \u2192 transferring \u2192 active \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u2013 \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043e\u043d\u043b\u0430\u0439\u043d \u0434\u043b\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f. \u0422\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u2013 \u0442\u043e\u043b\u044c\u043a\u043e expired \u0438 cancelled; failed \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 reactivate.\" title=\"\u0420\u0438\u0441. 3. \u0416\u0438\u0437\u043d\u0435\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u0448\u0430\u0440\u044b. \u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u0446\u0438\u043a\u043b active \u2192 matched \u2192 transferring \u2192 active \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u2013 \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043e\u043d\u043b\u0430\u0439\u043d \u0434\u043b\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f. \u0422\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u2013 \u0442\u043e\u043b\u044c\u043a\u043e expired \u0438 cancelled; failed \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 reactivate.\" width=\"2360\" height=\"1968\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/f4f\/5f7\/300\/f4f5f7300ddc85b19b371401da672196.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f4f\/5f7\/300\/f4f5f7300ddc85b19b371401da672196.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0420\u0438\u0441. 3. \u0416\u0438\u0437\u043d\u0435\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u0448\u0430\u0440\u044b. \u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u0446\u0438\u043a\u043b <code>active \u2192 matched \u2192 transferring \u2192 active<\/code> \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u2013 \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043e\u043d\u043b\u0430\u0439\u043d \u0434\u043b\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f. \u0422\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u2013 \u0442\u043e\u043b\u044c\u043a\u043e <code>expired<\/code> \u0438 <code>cancelled<\/code>; <code>failed<\/code> \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 reactivate.<\/figcaption><\/div>\n<\/figure>\n<p>\u041e\u0434\u0438\u043d WebSocket \u043d\u0430 \u0448\u0430\u0440\u0443. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u043d\u043e ECDSA. \u0421\u0435\u0440\u0432\u0435\u0440 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0438\u0440\u0443\u0435\u0442: \u043a\u0442\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c, \u043a\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044c, compare-and-swap \u043d\u0430 \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0443 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f, TTL, \u043e\u0447\u0438\u0441\u0442\u043a\u0430 \u0437\u0430\u0432\u0438\u0441\u0448\u0438\u0445 \u0448\u0430\u0440. \u0421\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0430\u0439\u043b\u0430 \u0438\u0434\u0451\u0442 \u043c\u0438\u043c\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u043d\u043e \u0441\u0435\u0440\u0432\u0435\u0440 \u2013 \u0434\u0438\u0440\u0438\u0436\u0451\u0440 \u0432\u0441\u0435\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430.<\/p>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21161: P2P \u043c\u043e\u0436\u0435\u0442 \u043d\u0435 \u0441\u043b\u0443\u0447\u0438\u0442\u044c\u0441\u044f<\/h3>\n<p>\u0412 \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0430\u0445 WebRTC \u2013 \u044d\u0442\u043e \u043c\u0430\u0433\u0438\u044f \u043f\u0440\u044f\u043c\u044b\u0445 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439. \u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 \u0438\u0434\u0451\u0442 \u0447\u0435\u0440\u0435\u0437 TURN relay. Symmetric NAT, \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 firewall, \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u0430\u044f \u0441\u0435\u0442\u044c \u2013 relay \u043d\u0443\u0436\u0435\u043d \u043d\u0435 \u043a\u0430\u043a \u00ab\u0440\u0435\u0434\u043a\u0438\u0439 \u043a\u043e\u0441\u0442\u044b\u043b\u044c\u00bb, \u0430 \u043a\u0430\u043a \u0448\u0442\u0430\u0442\u043d\u044b\u0439 \u0437\u0430\u043f\u0430\u0441\u043d\u043e\u0439 \u043f\u0443\u0442\u044c.<\/p>\n<p>\u0418 relay \u043c\u0435\u043d\u044f\u0435\u0442 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442, \u043d\u043e \u0438 \u0432\u0441\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u043d\u0438\u0436\u0435 \u043f\u043e \u0441\u0442\u0435\u043a\u0443.<\/p>\n<p>\u0422\u0438\u043f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 \u043f\u043e ICE-\u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442\u0430\u043c \u2013 \u043f\u0440\u0438\u0447\u0451\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044e\u0442\u0441\u044f\u00a0<strong>\u043e\u0431\u0430<\/strong>: local \u0438 remote. \u0415\u0441\u043b\u0438 \u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u0438\u043d relay \u2013 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043a\u043b\u0430\u0441\u0441\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043a\u0430\u043a TURN. \u0414\u0430\u043b\u044c\u0448\u0435 TURN \u0434\u0435\u043b\u0438\u0442\u0441\u044f \u043d\u0430\u00a0<code>server_relay<\/code>\u00a0(\u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 coturn, \u0441 \u043a\u0432\u043e\u0442\u0430\u043c\u0438) \u0438\u00a0<code>custom_relay<\/code>\u00a0(\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 TURN, \u0431\u0435\u0437 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439).<\/p>\n<p>\u041e\u0442 \u0442\u0438\u043f\u0430 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u0440\u0430\u0437\u043c\u0435\u0440 \u0447\u0430\u043d\u043a\u0430:<\/p>\n<pre><code class=\"javascript\">function getOptimalChunkSize(classification: string): number {  switch (classification) {    case 'p2p':          return 1024 * 1024;  \/\/ 1 MB \u2013 \u043c\u0435\u043d\u044c\u0448\u0435 async ops    case 'custom_relay': return 512 * 1024;   \/\/ 512 KB    case 'server_relay': return 64 * 1024;    \/\/ 64 KB \u2013 \u0431\u044b\u0441\u0442\u0440\u044b\u0435 retransmit    default:             return 512 * 1024;  }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u0447\u0435\u043c\u0443 64 KB \u043d\u0430 server relay? SCTP retransmit \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0447\u0430\u043d\u043a\u043e\u0432. \u041d\u0430 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0438 \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0447\u0430\u043d\u043a \u043f\u0440\u0438 \u043f\u043e\u0442\u0435\u0440\u0435 \u0440\u0435\u0442\u0440\u0430\u043d\u0441\u043b\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0446\u0435\u043b\u0438\u043a\u043e\u043c \u2013 \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430 \u0440\u0430\u0441\u0442\u0451\u0442 \u044d\u043a\u0441\u043f\u043e\u043d\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u043e. \u041c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u0435 \u0447\u0430\u043d\u043a\u0438 \u0434\u0430\u044e\u0442 \u0431\u044b\u0441\u0442\u0440\u044b\u0439 recovery.<\/p>\n<p>\u0414\u043b\u044f server relay \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0442 \u043a\u0432\u043e\u0442\u044b: \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 \u0444\u0430\u0439\u043b\u0430, \u0434\u043d\u0435\u0432\u043d\u043e\u0439 \u043b\u0438\u043c\u0438\u0442 \u043d\u0430 \u0430\u043a\u043a\u0430\u0443\u043d\u0442, \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0439 \u0434\u043d\u0435\u0432\u043d\u043e\u0439 \u043b\u0438\u043c\u0438\u0442. \u041b\u0438\u043c\u0438\u0442\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044e\u0442\u0441\u044f \u043d\u0430\u00a0<strong>\u043e\u0431\u0435\u0438\u0445<\/strong>\u00a0\u0441\u0442\u043e\u0440\u043e\u043d\u0430\u0445 \u2013 \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e sender \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c custom TURN (\u0431\u0435\u0437 \u043b\u0438\u043c\u0438\u0442\u043e\u0432), \u0430 receiver \u2013 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 coturn (\u0441 \u043b\u0438\u043c\u0438\u0442\u0430\u043c\u0438).<\/p>\n<p>\u041f\u0435\u0440\u0435\u0434 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0435\u0439 \u043c\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0431\u044b\u0441\u0442\u0440\u0443\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438 TURN \u2013 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439\u00a0<code>RTCPeerConnection<\/code>\u00a0\u0441\u00a0<code>iceTransportPolicy: 'relay'<\/code>. \u0417\u0430 5 \u0441\u0435\u043a\u0443\u043d\u0434 \u0437\u043d\u0430\u0435\u043c, \u0436\u0438\u0432 \u043b\u0438 coturn, \u043d\u0435 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0443\u0442\u044c. \u042d\u0442\u043e \u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043c\u0438\u043d\u0443\u0442\u0443 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f \u0441 \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u043e\u043c, \u0435\u0441\u043b\u0438 relay \u043c\u0451\u0440\u0442\u0432.<\/p>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21162:\u00a0dc.send()\u00a0\u043d\u0435 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u00ab\u0444\u0430\u0439\u043b \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u00bb<\/h3>\n<p>\u042d\u0442\u043e \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0441\u0442\u0430\u0442\u044c\u0438. \u041c\u044b \u043f\u043e\u0439\u043c\u0430\u043b\u0438 \u0435\u0451 \u043d\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 relay: \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441-\u0431\u0430\u0440 \u043f\u043e\u043a\u0430\u0437\u0430\u043b 100%, \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c \u043f\u043e\u0441\u043b\u0430\u043b\u00a0<code>transfer_done<\/code>\u00a0\u2013 \u0430 \u043d\u0430 \u0434\u0438\u0441\u043a\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f \u043d\u0435 \u0445\u0432\u0430\u0442\u0430\u043b\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 \u0434\u0432\u0443\u0445 \u043c\u0435\u0433\u0430\u0431\u0430\u0439\u0442. \u0424\u0430\u0439\u043b \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b \u043f\u043e\u043b\u043d\u044b\u043c, \u043d\u043e \u0431\u044b\u043b \u043e\u0431\u0440\u0435\u0437\u0430\u043d. \u041f\u0440\u0438\u0447\u0438\u043d\u0430 \u043e\u043a\u0430\u0437\u0430\u043b\u0430\u0441\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0438 \u043d\u0435\u043f\u0440\u0438\u044f\u0442\u043d\u043e\u0439:\u00a0<code>dc.send()<\/code>\u00a0\u2013 \u044d\u0442\u043e\u00a0<code>write()<\/code>\u00a0\u0432 SCTP-\u0431\u0443\u0444\u0435\u0440 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430, \u0430 \u043d\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0432 \u0441\u0435\u0442\u044c. \u041c\u0435\u0436\u0434\u0443 \u00ab\u043f\u043e\u043b\u043e\u0436\u0438\u043b \u0432 \u0431\u0443\u0444\u0435\u0440\u00bb \u0438 \u00abreceiver \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u0432\u0441\u0435 \u0431\u0430\u0439\u0442\u044b\u00bb \u2013 \u043f\u0440\u043e\u043f\u0430\u0441\u0442\u044c.<\/p>\n<p>\u041d\u0430 \u0431\u044b\u0441\u0442\u0440\u043e\u043c P2P-\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0438 \u0431\u0443\u0444\u0435\u0440 \u043f\u0443\u0441\u0442 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0441\u0435\u0433\u0434\u0430. \u041d\u0430 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u043c relay (\u2248100 KB\/s) \u0431\u0443\u0444\u0435\u0440 \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0441\u0435\u043a\u0443\u043d\u0434\u044b \u043d\u0435\u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445. \u0415\u0441\u043b\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c\u00a0<code>transfer_done<\/code>\u00a0\u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e \u0447\u0430\u043d\u043a\u0430 \u2013 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u0434\u0451\u0442 receiver&#8217;\u0443 \u0440\u0430\u043d\u044c\u0448\u0435, \u0447\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435.<\/p>\n<p>\u0412\u043e\u0442 \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0439 send loop (\u0443\u043f\u0440\u043e\u0449\u0451\u043d\u043d\u043e):<\/p>\n<pre><code class=\"javascript\">const BUFFER_THRESHOLD = 8 * 1024 * 1024; \/\/ 8 MBconst BUFFER_LOW       = 6 * 1024 * 1024; \/\/ 6 MBdc.bufferedAmountLowThreshold = BUFFER_LOW;let nextBuffer = await file.slice(0, chunkSize).arrayBuffer();while (offset &lt; file.size) {  const buffer = nextBuffer;  \/\/ Overlapped I\/O: \u0447\u0438\u0442\u0430\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0447\u0430\u043d\u043a, \u043f\u043e\u043a\u0430 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0443\u0445\u043e\u0434\u0438\u0442 \u0432 \u0441\u0435\u0442\u044c  const readPromise = offset + buffer.byteLength &lt; file.size    ? file.slice(offset + buffer.byteLength, nextEnd).arrayBuffer()    : null;  \/\/ Backpressure: \u0436\u0434\u0451\u043c, \u043f\u043e\u043a\u0430 \u0431\u0443\u0444\u0435\u0440 \u043f\u043e\u0434\u043e\u043f\u0443\u0441\u0442\u0435\u0435\u0442  if (dc.bufferedAmount &gt;= BUFFER_THRESHOLD) {    await waitForBufferDrain(dc, computeDrainTimeout(dc.bufferedAmount, speed));  }  dc.send(buffer);  offset += buffer.byteLength;  \/\/ \u0420\u0435\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441 \u2013 \u043d\u0435 \u043d\u0430\u0438\u0432\u043d\u044b\u0439 offset  const effectiveSent = Math.max(0, offset - dc.bufferedAmount);  const pct = Math.min((effectiveSent \/ file.size) * 100, isLast ? 100 : 99);  nextBuffer = readPromise ? await readPromise : null;}\/\/ \u0416\u0434\u0451\u043c drain \u2192 ACK \u043e\u0442 receiver \u2192 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0442\u043e\u043c transfer_doneif (dc.bufferedAmount &gt; 0) await waitForBufferDrain(dc, ...);await waitForAck(dc, 10_000).catch(() =&gt; { \/* \u0441\u0442\u0430\u0440\u044b\u0439 receiver *\/ });dc.send(JSON.stringify({ type: 'transfer_done' }));<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u043a\u043e\u0434 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u043e\u0436\u043d\u0435\u0435, \u0447\u0435\u043c \u00ab\u043d\u0430\u0440\u0435\u0437\u0430\u0442\u044c \u0444\u0430\u0439\u043b \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c\u00bb, \u0438 \u043a\u0430\u0436\u0434\u043e\u0435 \u0443\u0441\u043b\u043e\u0436\u043d\u0435\u043d\u0438\u0435 \u2013 \u0441\u043b\u0435\u0434 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u0431\u0430\u0433\u0430.<\/p>\n<p>\u041d\u0430\u0447\u043d\u0451\u043c \u0441 \u0434\u0432\u043e\u0439\u043d\u043e\u0433\u043e \u043f\u043e\u0440\u043e\u0433\u0430:\u00a0<code>BUFFER_THRESHOLD<\/code>\u00a0(8 MB) \u2013 \u0432\u0435\u0440\u0445\u043d\u044f\u044f \u043e\u0442\u043c\u0435\u0442\u043a\u0430, \u043f\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u043f\u0440\u0438\u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u0438\u00a0<code>BUFFER_LOW<\/code>\u00a0(6 MB) \u2013 \u043d\u0438\u0436\u043d\u044f\u044f, \u043f\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0432\u043e\u0437\u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f. \u0417\u0430\u0437\u043e\u0440 \u0432 2 MB \u0434\u0435\u0440\u0436\u0438\u0442 \u043a\u0430\u043d\u0430\u043b \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u043c, \u043d\u043e \u043d\u0435 \u0434\u0430\u0451\u0442 \u0431\u0443\u0444\u0435\u0440\u0443 \u043f\u0435\u0440\u0435\u043f\u043e\u043b\u043d\u0438\u0442\u044c\u0441\u044f. \u042d\u0442\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043f\u0430\u0442\u0442\u0435\u0440\u043d high\/low water mark.<\/p>\n<p>\u0414\u0430\u043b\u044c\u0448\u0435 \u2013 overlapped I\/O. \u041d\u0430\u0438\u0432\u043d\u044b\u0439 send loop \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439: \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0447\u0430\u043d\u043a \u0441 \u0434\u0438\u0441\u043a\u0430, \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c, \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439. \u041f\u043e\u043a\u0430 \u0438\u0434\u0451\u0442\u00a0<code>file.slice().arrayBuffer()<\/code>, DataChannel \u043f\u0440\u043e\u0441\u0442\u0430\u0438\u0432\u0430\u0435\u0442. Overlapped \u0432\u0435\u0440\u0441\u0438\u044f \u0447\u0438\u0442\u0430\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0447\u0430\u043d\u043a \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u0441 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u2013 \u044d\u0442\u043e \u0443\u0431\u0438\u0440\u0430\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0438 \u0437\u0430\u043c\u0435\u0442\u043d\u043e \u0443\u0441\u043a\u043e\u0440\u044f\u0435\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0443 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u0444\u0430\u0439\u043b\u043e\u0432.<\/p>\n<p>\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u2013 \u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u043d\u0430 drain. \u0424\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442: 5 \u0441\u0435\u043a\u0443\u043d\u0434 \u043d\u0430 \u0431\u044b\u0441\u0442\u0440\u043e\u043c P2P \u2013 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e (\u043c\u0451\u0440\u0442\u0432\u043e\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0431\u044b\u0441\u0442\u0440\u043e), \u043d\u043e 5 \u0441\u0435\u043a\u0443\u043d\u0434 \u043d\u0430 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u043c relay \u2013 \u043b\u043e\u0436\u043d\u0430\u044f \u0442\u0440\u0435\u0432\u043e\u0433\u0430 (\u0431\u0443\u0444\u0435\u0440 \u043f\u0440\u043e\u0441\u0442\u043e \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e \u0434\u0440\u0435\u043d\u0438\u0440\u0443\u0435\u0442\u0441\u044f). \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442\u0441\u044f \u0438\u0437 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u043d\u043e\u0439 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438:<\/p>\n<pre><code class=\"javascript\">function computeDrainTimeout(bufferedAmount, measuredSpeed) {  const estimatedDrainMs = (bufferedAmount \/ measuredSpeed) * 1000;  return Math.max(5_000, Math.min(60_000, estimatedDrainMs * 3));}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0411\u044b\u0441\u0442\u0440\u044b\u0439 P2P (50 MB\/s): \u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u22485 \u0441\u0435\u043a. \u041c\u0435\u0434\u043b\u0435\u043d\u043d\u044b\u0439 relay (100 KB\/s): \u0434\u043e 60 \u0441\u0435\u043a. \u041e\u0434\u043d\u0430 \u0444\u043e\u0440\u043c\u0443\u043b\u0430, \u0434\u0432\u0430 \u044d\u043a\u0441\u0442\u0440\u0435\u043c\u0443\u043c\u0430.<\/p>\n<p>\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f \u2013 \u0447\u0435\u0441\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441. \u041d\u0430\u0438\u0432\u043d\u044b\u0439\u00a0<code>offset<\/code>\u00a0\u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 100%, \u043a\u043e\u0433\u0434\u0430 \u043c\u0435\u0433\u0430\u0431\u0430\u0439\u0442\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0435\u0449\u0451 \u0441\u0438\u0434\u044f\u0442 \u0432 SCTP-\u0431\u0443\u0444\u0435\u0440\u0435. \u0420\u0435\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441 \u2013 \u044d\u0442\u043e\u00a0<code>offset \u2212 dc.bufferedAmount<\/code>, \u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435\u043c \u043d\u0430 99% \u0434\u043e \u043f\u043e\u043b\u043d\u043e\u0433\u043e drain. \u0418\u043d\u0430\u0447\u0435 UI \u0432\u0440\u0451\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e, \u0438 \u0438\u043c\u0435\u043d\u043d\u043e \u044d\u0442\u043e \u043c\u044b \u0443\u0432\u0438\u0434\u0435\u043b\u0438 \u0432 \u0442\u043e\u043c \u043f\u0435\u0440\u0432\u043e\u043c \u0431\u0430\u0433\u0435.<\/p>\n<p>\u0418 \u043d\u0430\u043a\u043e\u043d\u0435\u0446 \u2013 \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f. \u041f\u043e\u0441\u043b\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0432\u0441\u0435\u0445 \u0447\u0430\u043d\u043a\u043e\u0432 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c \u0436\u0434\u0451\u0442 \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u043e\u043f\u0443\u0441\u0442\u043e\u0448\u0435\u043d\u0438\u044f \u0431\u0443\u0444\u0435\u0440\u0430, \u0437\u0430\u0442\u0435\u043c\u00a0<code>transfer_ack<\/code>\u00a0\u043e\u0442 receiver \u2013 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0447\u0442\u043e \u0432\u0441\u0435 \u0431\u0430\u0439\u0442\u044b \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u0438 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u044b. \u0418 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u2013\u00a0<code>transfer_done<\/code>. \u042d\u0442\u043e \u043a\u0430\u043a\u00a0<code>fwrite()<\/code>\u00a0+\u00a0<code>fsync()<\/code>: \u0431\u0435\u0437 ACK \u0432\u044b \u043d\u0430\u0434\u0435\u0435\u0442\u0435\u0441\u044c, \u043d\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442\u0435.<\/p>\n<p>\u041d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f \u2013 \u0441\u0432\u043e\u044f \u043c\u0435\u0445\u0430\u043d\u0438\u043a\u0430.\u00a0<code>dc.onmessage<\/code>\u00a0\u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u00a0<code>ArrayBuffer<\/code>, \u043d\u043e DataChannel \u043c\u043e\u0436\u0435\u0442 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439 \u0431\u0443\u0444\u0435\u0440 \u043c\u0435\u0436\u0434\u0443 \u0432\u044b\u0437\u043e\u0432\u0430\u043c\u0438. \u0411\u0435\u0437 \u044f\u0432\u043d\u043e\u0433\u043e \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u2013 \u043f\u043e\u0432\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445:<\/p>\n<pre><code class=\"javascript\">\/\/ Receiver: streaming \u0437\u0430\u043f\u0438\u0441\u044c \u043d\u0430 \u0434\u0438\u0441\u043aconst chunk = event.data.slice(0);  \/\/ \u043a\u043e\u043f\u0438\u044f \u2013 DC \u043c\u043e\u0436\u0435\u0442 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c bufferwriteChain = writeChain.then(() =&gt; writer.write(chunk));  \/\/ disk backpressureif (bytesReceived === fileInfo.size) {  dc.send(JSON.stringify({ type: 'transfer_ack' }));  \/\/ sender \u0436\u0434\u0451\u0442 \u044d\u0442\u043e\u0433\u043e}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><code>writeChain<\/code>\u00a0\u2013 \u0446\u0435\u043f\u043e\u0447\u043a\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u043e\u043c\u0438\u0441\u043e\u0432 \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u043d\u0430 \u0434\u0438\u0441\u043a. \u0415\u0441\u043b\u0438 \u0434\u0438\u0441\u043a \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u044b\u0439 (USB), \u0447\u0430\u043d\u043a\u0438 \u0441\u0442\u0430\u0432\u044f\u0442\u0441\u044f \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u043d\u043e \u043d\u0435 \u043d\u0430\u043a\u0430\u043f\u043b\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0432 RAM \u0431\u0435\u0441\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u043e. \u0414\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u043e \u0432\u0441\u0435\u0439 \u0446\u0435\u043f\u043e\u0447\u043a\u0435: \u0434\u0438\u0441\u043a \u043d\u0435 \u0443\u0441\u043f\u0435\u0432\u0430\u0435\u0442 \u2192 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044c \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0435\u0435 \u0437\u0430\u0431\u0438\u0440\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u043a\u0430\u043d\u0430\u043b\u0430 \u2192 SCTP flow control \u0442\u043e\u0440\u043c\u043e\u0437\u0438\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044f \u2192\u00a0<code>bufferedAmount<\/code>\u00a0\u0440\u0430\u0441\u0442\u0451\u0442 \u2192 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c \u0436\u0434\u0451\u0442 drain.<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/565\/ac7\/558\/565ac75580cb7a249eb443365e2d0a99.png\" alt=\"\u0420\u0438\u0441. 4. \u041d\u0430\u0438\u0432\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u043f\u0440\u043e\u0442\u0438\u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0443\u0442\u0438. \u0414\u0430\u043d\u043d\u044b\u0435 \u0442\u0435\u043a\u0443\u0442 \u0441\u043b\u0435\u0432\u0430 \u043d\u0430\u043f\u0440\u0430\u0432\u043e, \u0430 backpressure \u0438 transfer_ack \u2013 \u0441\u043f\u0440\u0430\u0432\u0430 \u043d\u0430\u043b\u0435\u0432\u043e. \u041f\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u2013 \u044d\u0442\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0441 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0439 \u0441\u0432\u044f\u0437\u044c\u044e, \u0430 \u043d\u0435 \u043e\u0434\u043d\u043e\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u044f\u044f \u0442\u0440\u0443\u0431\u0430.*\" title=\"\u0420\u0438\u0441. 4. \u041d\u0430\u0438\u0432\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u043f\u0440\u043e\u0442\u0438\u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0443\u0442\u0438. \u0414\u0430\u043d\u043d\u044b\u0435 \u0442\u0435\u043a\u0443\u0442 \u0441\u043b\u0435\u0432\u0430 \u043d\u0430\u043f\u0440\u0430\u0432\u043e, \u0430 backpressure \u0438 transfer_ack \u2013 \u0441\u043f\u0440\u0430\u0432\u0430 \u043d\u0430\u043b\u0435\u0432\u043e. \u041f\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u2013 \u044d\u0442\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0441 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0439 \u0441\u0432\u044f\u0437\u044c\u044e, \u0430 \u043d\u0435 \u043e\u0434\u043d\u043e\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u044f\u044f \u0442\u0440\u0443\u0431\u0430.*\" width=\"3264\" height=\"1380\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/565\/ac7\/558\/565ac75580cb7a249eb443365e2d0a99.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/565\/ac7\/558\/565ac75580cb7a249eb443365e2d0a99.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0420\u0438\u0441. 4. \u041d\u0430\u0438\u0432\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u043f\u0440\u043e\u0442\u0438\u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0443\u0442\u0438. \u0414\u0430\u043d\u043d\u044b\u0435 \u0442\u0435\u043a\u0443\u0442 \u0441\u043b\u0435\u0432\u0430 \u043d\u0430\u043f\u0440\u0430\u0432\u043e, \u0430 backpressure \u0438 <code>transfer_ack<\/code> \u2013 \u0441\u043f\u0440\u0430\u0432\u0430 \u043d\u0430\u043b\u0435\u0432\u043e. \u041f\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u2013 \u044d\u0442\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0441 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0439 \u0441\u0432\u044f\u0437\u044c\u044e, \u0430 \u043d\u0435 \u043e\u0434\u043d\u043e\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u044f\u044f \u0442\u0440\u0443\u0431\u0430.*<\/figcaption><\/div>\n<\/figure>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21163: receiver \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0433\u043e\u0442\u043e\u0432 \u0414\u041e \u0442\u043e\u0433\u043e, \u043a\u0430\u043a sender \u043d\u0430\u0447\u043d\u0451\u0442 \u0441\u043b\u0430\u0442\u044c<\/h3>\n<p>\u0421 \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u043c \u0444\u0430\u0439\u043b\u043e\u043c \u0432\u0441\u0451 \u043f\u0440\u043e\u0441\u0442\u043e: \u043f\u0440\u0438\u043d\u044f\u0442\u044c \u0447\u0430\u043d\u043a\u0438 \u0432 \u043f\u0430\u043c\u044f\u0442\u044c, \u0441\u043e\u0431\u0440\u0430\u0442\u044c\u00a0<code>Blob<\/code>, \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0438\u0442\u044c \u0441\u043a\u0430\u0447\u0430\u0442\u044c. \u0424\u0430\u0439\u043b \u0432 5 MB \u0441\u043f\u043e\u043a\u043e\u0439\u043d\u043e \u043f\u043e\u043c\u0435\u0449\u0430\u0435\u0442\u0441\u044f \u0432 RAM, \u0438 \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u0430 save-\u0434\u0438\u0430\u043b\u043e\u0433 \u043f\u043e\u0441\u043b\u0435 \u043f\u0440\u0438\u0451\u043c\u0430 \u043d\u0438\u043a\u043e\u0433\u043e \u043d\u0435 \u0432\u043e\u043b\u043d\u0443\u0435\u0442.<\/p>\n<p>\u0421 \u0431\u043e\u043b\u044c\u0448\u0438\u043c \u0444\u0430\u0439\u043b\u043e\u043c \u0442\u0430\u043a \u043d\u0435\u043b\u044c\u0437\u044f. \u0424\u0430\u0439\u043b \u0432 2 GB \u043d\u0435 \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u0441\u044f \u0432\u00a0<code>ArrayBuffer[]<\/code>\u00a0\u2013 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0443\u043f\u0430\u0434\u0451\u0442 \u0441 OOM \u0440\u0430\u043d\u044c\u0448\u0435, \u0447\u0435\u043c \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0430. \u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u2013 streaming: \u043f\u0438\u0441\u0430\u0442\u044c \u0447\u0430\u043d\u043a\u0438 \u043d\u0430 \u0434\u0438\u0441\u043a \u043f\u043e \u043c\u0435\u0440\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f, \u0447\u0435\u0440\u0435\u0437 File System Access API (<code>showSaveFilePicker()<\/code>\u00a0\u2192\u00a0<code>FileSystemWritableFileStream<\/code>). \u042d\u0442\u043e\u0442 API \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0432 Chrome \u0438 Edge \u2013 \u0432 Firefox \u0438 Safari \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c in-memory fallback, \u0430 \u0437\u043d\u0430\u0447\u0438\u0442, \u0444\u0430\u0439\u043b\u044b \u0431\u043e\u043b\u044c\u0448\u0435 \u0433\u0438\u0433\u0430\u0431\u0430\u0439\u0442\u0430 \u0442\u0430\u043c \u0440\u0438\u0441\u043a\u0443\u044e\u0442 \u0443\u0440\u043e\u043d\u0438\u0442\u044c \u0431\u0440\u0430\u0443\u0437\u0435\u0440. \u041d\u043e \u0434\u0430\u0436\u0435 \u0432 Chrome \u0434\u043b\u044f streaming \u043d\u0443\u0436\u043d\u043e \u0437\u043d\u0430\u0442\u044c, \u043a\u0443\u0434\u0430 \u043f\u0438\u0441\u0430\u0442\u044c,\u00a0<strong>\u0434\u043e \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0447\u0430\u043d\u043a\u0430<\/strong>. \u0410\u00a0<code>showSaveFilePicker()<\/code>\u00a0\u2013 \u044d\u0442\u043e \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0449\u0438\u0439 \u0434\u0438\u0430\u043b\u043e\u0433, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u0441\u0435\u043a\u0443\u043d\u0434\u044b.<\/p>\n<p>\u0412\u043e\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430: \u0435\u0441\u043b\u0438 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0443 \u0438 \u043f\u043e\u0442\u043e\u043c \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c save-\u0434\u0438\u0430\u043b\u043e\u0433, sender \u0443\u0436\u0435 \u043d\u0430\u0447\u043d\u0451\u0442 \u0441\u043b\u0430\u0442\u044c. \u0427\u0430\u043d\u043a\u0438 \u043f\u0440\u0438\u0434\u0443\u0442 \u0434\u043e \u0442\u043e\u0433\u043e, \u043a\u0430\u043a receiver \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u2013 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u044b \u043d\u0430\u0432\u0441\u0435\u0433\u0434\u0430.<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443\u00a0<code>transfer_confirm<\/code>\u00a0\u2013 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u044c\u043d\u044b\u0439 \u0448\u0430\u0433. \u041e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442 metadata (\u0438\u043c\u044f \u0444\u0430\u0439\u043b\u0430, \u0440\u0430\u0437\u043c\u0435\u0440, \u0442\u0438\u043f) \u0447\u0435\u0440\u0435\u0437 DataChannel (\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0437\u0434\u0430\u043d \u0441\u00a0<code>ordered: true<\/code>\u00a0\u2013 \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u0447\u0430\u043d\u043a\u043e\u0432 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d) \u0438\u00a0<strong>\u0436\u0434\u0451\u0442<\/strong>. \u041f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044c \u0441\u043c\u043e\u0442\u0440\u0438\u0442 \u043d\u0430 \u0440\u0430\u0437\u043c\u0435\u0440: \u0435\u0441\u043b\u0438 \u0431\u043e\u043b\u044c\u0448\u0435 100 MB \u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 FSAA \u2013 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 save-\u0434\u0438\u0430\u043b\u043e\u0433. \u0412\u0430\u0436\u043d\u044b\u0439 \u043d\u044e\u0430\u043d\u0441:\u00a0<code>showSaveFilePicker()<\/code>\u00a0\u0442\u0440\u0435\u0431\u0443\u0435\u0442 user gesture \u2013 \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u0438\u0430\u043b\u043e\u0433 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e \u043a\u043b\u0438\u043a\u0443 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0435 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u0432 UI, \u0430 \u043d\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e. \u0418 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a destination \u0433\u043e\u0442\u043e\u0432, \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044c \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0435\u0442:<\/p>\n<pre><code class=\"javascript\">\/\/ Receiver: \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u044c destination \u041f\u0415\u0420\u0415\u0414 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\u043c.\/\/ Sender must not start sending chunks while user picks save location\/\/ (dialog takes seconds, chunks arriving without handler are lost).const fileHandle = await prepareSaveFile(metadata.file, callbacks);\/\/ \u0422\u0415\u041f\u0415\u0420\u042c \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u2013 sender \u043d\u0430\u0447\u043d\u0451\u0442 \u0441\u043b\u0430\u0442\u044c \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043edc.send(JSON.stringify({ type: 'transfer_confirm', accepted: true }));\/\/ \u041f\u0440\u0438\u043d\u044f\u0442\u044c \u0447\u0430\u043d\u043a\u0438await doReceiveFile(dc, metadata.file, fileHandle, signal, callbacks);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u0444\u0430\u0439\u043b \u043c\u0435\u043d\u044c\u0448\u0435 100 MB \u0438\u043b\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 FSAA (Firefox, Safari) \u2013 fallback \u043d\u0430 in-memory:\u00a0<code>ArrayBuffer[]<\/code>\u00a0\u2192\u00a0<code>Blob<\/code>\u00a0\u2192\u00a0<code>&lt;a download&gt;<\/code>. Confirm \u0443\u0445\u043e\u0434\u0438\u0442 \u0441\u0440\u0430\u0437\u0443, \u0431\u0435\u0437 \u0434\u0438\u0430\u043b\u043e\u0433\u0430.<\/p>\n<p>\u0415\u0449\u0451 \u043e\u0434\u043d\u0430 \u043d\u0435\u043e\u0447\u0435\u0432\u0438\u0434\u043d\u0430\u044f \u0434\u0435\u0442\u0430\u043b\u044c \u0432 in-memory \u043f\u0443\u0442\u0438:\u00a0<code>URL.revokeObjectURL()<\/code>\u00a0\u043d\u0435\u043b\u044c\u0437\u044f \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435\u00a0<a href=\"http:\/\/a.click\" rel=\"noopener noreferrer nofollow\"><code>a.click<\/code><\/a><code>()<\/code>. \u0411\u0440\u0430\u0443\u0437\u0435\u0440 \u043f\u0438\u0448\u0435\u0442 \u0444\u0430\u0439\u043b\u044b \u043d\u0430 \u0434\u0438\u0441\u043a \u0432 \u0444\u043e\u043d\u0435 \u2013\u00a0<code>click()<\/code>\u00a0\u043b\u0438\u0448\u044c \u0438\u043d\u0438\u0446\u0438\u0438\u0440\u0443\u0435\u0442 \u0441\u043a\u0430\u0447\u0438\u0432\u0430\u043d\u0438\u0435. Revoke \u0441\u0440\u0430\u0437\u0443 \u2013 \u0442\u0438\u0445\u0430\u044f \u043f\u043e\u0442\u0435\u0440\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0444\u0430\u0439\u043b\u0430\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435 \u0443\u0441\u043f\u0435\u043b\u0438 \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c\u0441\u044f. \u041c\u044b \u043e\u0442\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u043c revocation \u043d\u0430 60 \u0441\u0435\u043a\u0443\u043d\u0434. Blob \u0434\u0435\u0440\u0436\u0438\u0442\u0441\u044f \u0432 \u043f\u0430\u043c\u044f\u0442\u0438, \u043d\u043e \u044d\u0442\u043e \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u0435\u0435, \u0447\u0435\u043c \u043e\u0431\u0440\u0435\u0437\u0430\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b.<\/p>\n<p>\u0414\u043b\u044f server relay \u0435\u0441\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0448\u0430\u0433 \u043f\u0435\u0440\u0435\u0434 \u0432\u0441\u0435\u043c \u044d\u0442\u0438\u043c: UI-\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u043e\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. Receiver \u0432\u0438\u0434\u0438\u0442 \u0440\u0430\u0437\u043c\u0435\u0440 \u0444\u0430\u0439\u043b\u0430 \u0438 \u043e\u0441\u0442\u0430\u0432\u0448\u0438\u0435\u0441\u044f \u043a\u0432\u043e\u0442\u044b, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043e\u0441\u043e\u0437\u043d\u0430\u043d\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u2013 \u0441\u0442\u043e\u0438\u0442 \u043b\u0438 \u0442\u0440\u0430\u0442\u0438\u0442\u044c relay-\u043b\u0438\u043c\u0438\u0442.<\/p>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21164: \u0441\u0435\u0442\u044c \u043b\u043e\u043c\u0430\u0435\u0442\u0441\u044f \u0430\u0441\u0438\u043c\u043c\u0435\u0442\u0440\u0438\u0447\u043d\u043e<\/h3>\n<p>\u0420\u0435\u0436\u0438\u043c\u044b \u043e\u0442\u043a\u0430\u0437\u0430 \u043d\u0435 \u0431\u0438\u043d\u0430\u0440\u043d\u044b\u0435. Signaling WebSocket \u043c\u043e\u0436\u0435\u0442 \u0443\u043c\u0435\u0440\u0435\u0442\u044c, \u0430 DataChannel \u0435\u0449\u0451 \u0436\u0438\u0442\u044c. ICE \u043c\u043e\u0436\u0435\u0442 \u0443\u0439\u0442\u0438 \u0432\u00a0<code>failed<\/code>\u00a0\u0438 \u0447\u0435\u0440\u0435\u0437 \u0441\u0435\u043a\u0443\u043d\u0434\u0443 \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u0432\u00a0<code>checking<\/code>. \u041c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0439 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440 \u043c\u043e\u0436\u0435\u0442 \u0443\u0431\u0438\u0442\u044c \u0431\u0435\u0437\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 TCP \u0437\u0430 \u0434\u0432\u0435 \u043c\u0438\u043d\u0443\u0442\u044b. \u0410 \u043f\u0435\u0440\u0435\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 WebSocket \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u043d\u0435\u0441\u0442\u0438 \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0438\u0437 \u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u0441\u0435\u0441\u0441\u0438\u0438.<\/p>\n<p><strong>ICE grace period.<\/strong>\u00a0\u041f\u0440\u0438\u00a0<code>iceConnectionState === 'failed'<\/code>\u00a0\u2013 \u043d\u0435 \u043f\u0430\u0434\u0430\u0435\u043c \u0441\u0440\u0430\u0437\u0443. \u041d\u0430 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u0441\u0435\u0442\u044f\u0445 relay-\u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442\u044b \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e. \u0414\u0430\u0451\u043c 20 \u0441\u0435\u043a\u0443\u043d\u0434 grace period. \u0415\u0441\u043b\u0438 ICE \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442 \u0432\u00a0<code>checking<\/code>\u00a0\u0438\u043b\u0438\u00a0<code>connected<\/code>\u00a0\u2013 \u0442\u0430\u0439\u043c\u0435\u0440 \u043e\u0442\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f.<\/p>\n<p>\u041d\u043e \u043e\u0434\u043d\u043e\u0433\u043e grace period \u043c\u0430\u043b\u043e. Safari + flaky TURN \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442 \u0431\u0435\u0441\u043a\u043e\u043d\u0435\u0447\u043d\u044b\u0439 \u0446\u0438\u043a\u043b:\u00a0<code>failed \u2192 checking \u2192 failed \u2192 checking...<\/code>. Grace period \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c recovery \u2013 \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0437\u0430\u0432\u0438\u0441\u0430\u0435\u0442 \u043d\u0430\u0432\u0441\u0435\u0433\u0434\u0430. \u0420\u0435\u0448\u0435\u043d\u0438\u0435 \u2013 cumulative counter:<\/p>\n<pre><code class=\"javascript\">onICEStateChange: (iceState) =&gt; {  if (iceState === 'failed') {    if (this.iceFirstFailedAt === null) this.iceFirstFailedAt = Date.now();    const cumulativeMs = Date.now() - this.iceFirstFailedAt;    if (cumulativeMs &gt; 60_000) reject('ICE failed');   \/\/ cumulative cap    if (!this.iceFailTimer) {      this.iceFailTimer = setTimeout(reject, 20_000);  \/\/ grace period    }  } else if (['checking', 'connected', 'completed'].includes(iceState)) {    clearTimeout(this.iceFailTimer);                    \/\/ recovery    this.iceFirstFailedAt = null;                       \/\/ reset cumulative  }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u0432\u0430 \u0442\u0430\u0439\u043c\u0435\u0440\u0430, \u0440\u0430\u0437\u043d\u0430\u044f \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u043a\u0430: grace period \u043b\u043e\u0432\u0438\u0442 \u043e\u0434\u0438\u043d\u043e\u0447\u043d\u044b\u0435 \u0441\u0431\u043e\u0438, cumulative cap \u2013 \u0431\u0435\u0441\u043a\u043e\u043d\u0435\u0447\u043d\u044b\u0435 \u0446\u0438\u043a\u043b\u044b.<\/p>\n<p>\u0415\u0449\u0451 \u043e\u0434\u043d\u0430 \u043b\u043e\u0432\u0443\u0448\u043a\u0430 \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u2013 \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f. WebSocket signaling \u043c\u043e\u0436\u0435\u0442 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0438 \u043f\u0440\u0438\u043d\u0435\u0441\u0442\u0438\u00a0<code>peer_joined<\/code>,\u00a0<code>sdp_offer<\/code>,\u00a0<code>ice_candidate<\/code>\u00a0\u0438\u0437 \u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u0441\u0435\u0441\u0441\u0438\u0438. \u0415\u0441\u043b\u0438 \u0438\u0445 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438 \u2013 \u043d\u043e\u0432\u044b\u0439 transfer flow \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u043c\u0443\u0441\u043e\u0440. \u0420\u0435\u0448\u0435\u043d\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0440\u043d\u043e\u0435, \u043d\u043e \u0437\u0430\u0431\u044b\u0442\u044c \u0435\u0433\u043e \u043b\u0435\u0433\u043a\u043e:<\/p>\n<pre><code class=\"javascript\">private async doReconnect() {  this.eventBuffer = [];  \/\/ stale events \u0438\u0437 \u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u0441\u0435\u0441\u0441\u0438\u0438 \u2013 \u0432 \u043c\u0443\u0441\u043e\u0440  const url = await this.buildURL(this.shareId);  await this.setupWebSocket(url);}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a \u044d\u0442\u043e\u043c\u0443 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0434\u0432\u0435 \u0432\u0435\u0449\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435 \u0438\u043c\u0435\u044e\u0442 \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u044f \u043a WebRTC, \u043d\u043e \u0431\u0435\u0437 \u043d\u0438\u0445 \u0432\u0441\u0451 \u0440\u0430\u0437\u0432\u0430\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445. Keepalive \u2013\u00a0<code>{\"type\":\"ping\"}<\/code>\u00a0\u043a\u0430\u0436\u0434\u044b\u0435 15 \u0441\u0435\u043a\u0443\u043d\u0434 \u2013 \u043d\u0443\u0436\u0435\u043d \u043d\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0443 (\u043e\u043d \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u0435\u0442), \u0430 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u043c \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u0430\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0443\u0431\u0438\u0432\u0430\u044e\u0442 idle TCP \u0437\u0430 2\u20135 \u043c\u0438\u043d\u0443\u0442. \u0410 \u043f\u0440\u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u0435 \u0438\u0437 \u0444\u043e\u043d\u043e\u0432\u043e\u0433\u043e \u0440\u0435\u0436\u0438\u043c\u0430 (Visibility API) \u043d\u0443\u0436\u043d\u043e \u043d\u0435\u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e \u0441\u0431\u0440\u043e\u0441\u0438\u0442\u044c backoff \u0438 \u043f\u0435\u0440\u0435\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f, \u0438\u043d\u0430\u0447\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0436\u0434\u0451\u0442 \u043c\u0438\u043d\u0443\u0442\u044b, \u0445\u043e\u0442\u044f \u0441\u0435\u0442\u044c \u0434\u0430\u0432\u043d\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430.<\/p>\n<p>\u041d\u0430\u043a\u043e\u043d\u0435\u0446,\u00a0<code>peer_left<\/code>\u00a0\u043c\u043e\u0436\u0435\u0442 \u043e\u0437\u043d\u0430\u0447\u0430\u0442\u044c \u0443\u0441\u043f\u0435\u0445. \u041f\u043e\u0441\u043b\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0444\u0430\u0439\u043b\u0430 sender \u0436\u0434\u0451\u0442\u00a0<code>transfer_complete<\/code>\u00a0\u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u043d\u043e receiver \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u0432\u043a\u043b\u0430\u0434\u043a\u0443 \u0440\u0430\u043d\u044c\u0448\u0435. \u0415\u0441\u043b\u0438 \u0444\u0430\u0439\u043b \u0443\u0436\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d (<code>fileSentSuccessfully === true<\/code>), \u044d\u0442\u043e \u043d\u0435 \u043e\u0448\u0438\u0431\u043a\u0430 \u2013 \u0444\u0430\u0439\u043b \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d.\u00a0<code>transfer_complete<\/code>\u00a0\u2013 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 \u0443\u0447\u0451\u0442, \u0430 \u043d\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u0435 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438.<\/p>\n<p>\u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 signaling \u0438 data plane \u0436\u0438\u0432\u0443\u0442 \u0440\u0430\u0437\u043d\u043e\u0439 \u0436\u0438\u0437\u043d\u044c\u044e \u0438 \u043b\u043e\u043c\u0430\u044e\u0442\u0441\u044f \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e. \u041f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u043e \u0441 \u0443\u0447\u0451\u0442\u043e\u043c \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u043d\u0438\u0445 \u043c\u043e\u0436\u0435\u0442 \u0443\u043c\u0435\u0440\u0435\u0442\u044c \u0432 \u043d\u0435\u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0443\u0435\u043c\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442.<\/p>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21165: refresh \u0443\u0431\u0438\u0432\u0430\u0435\u0442\u00a0File, \u043d\u043e \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0443\u0431\u0438\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0443<\/h3>\n<p><code>File<\/code>\u00a0\u043d\u0435 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0443\u0435\u0442\u0441\u044f. \u041d\u0438 \u0432 IndexedDB, \u043d\u0438 \u0432 sessionStorage, \u043d\u0438 \u043a\u0443\u0434\u0430-\u0442\u043e \u0435\u0449\u0451. \u042d\u0442\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u043e\u0439 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b:\u00a0<code>File<\/code>\u00a0\u2013 \u044d\u0442\u043e handle \u043d\u0430 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430. Refresh = handle \u043f\u043e\u0442\u0435\u0440\u044f\u043d. \u0422\u043e\u0447\u043a\u0430.<\/p>\n<p>\u0422\u0435\u043a\u0441\u0442, URL \u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0448\u0430\u0440\u044b \u043c\u043e\u0436\u043d\u043e \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0438\u0437 IndexedDB. \u041d\u043e \u0444\u0430\u0439\u043b \u2013 \u043d\u0435\u0442. Sender \u043f\u043e\u0441\u043b\u0435 refresh \u0437\u043d\u0430\u0435\u0442\u00a0<em>\u043e<\/em>\u00a0\u0444\u0430\u0439\u043b\u0435 (\u0438\u043c\u044f, \u0440\u0430\u0437\u043c\u0435\u0440, \u0442\u0438\u043f), \u043d\u043e \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0441\u0430\u043c\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430.<\/p>\n<p>\u041d\u0430\u0448 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442 \u044d\u0442\u043e. Sender \u0448\u043b\u0451\u0442 metadata \u0441\u00a0<code>available: false<\/code>\u00a0\u0438\u00a0<code>fileHint<\/code>\u00a0\u2013 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u043e \u0444\u0430\u0439\u043b\u0435 \u0431\u0435\u0437 \u0441\u0430\u043c\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430. Receiver \u0432\u0438\u0434\u0438\u0442: \u00ab\u0444\u0430\u0439\u043b \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438 \u0435\u0441\u0442\u044c, \u043d\u043e \u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d\u00bb. \u042d\u0442\u043e \u043d\u0435 \u0437\u0430\u0433\u0430\u0434\u043e\u0447\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430, \u0430 \u0448\u0442\u0430\u0442\u043d\u044b\u0439 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0441 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432\u044b\u043c \u0442\u0435\u0440\u043c\u0438\u043d\u043e\u043c \u2013\u00a0<strong>unhealthy share<\/strong>.<\/p>\n<p>\u0412 UI \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442\u0441\u044f\u00a0<code>UnhealthySharePrompt<\/code>\u00a0\u2013 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u0442\u0430\u0449\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u0437\u0430\u043d\u043e\u0432\u043e. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0435\u0440\u0435\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0444\u0430\u0439\u043b \u2192\u00a0<code>updateFile()<\/code>. \u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044c \u0443\u0436\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d \u0438 \u0436\u0434\u0451\u0442 \u2013 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f\u00a0<code>reannounceFile()<\/code>: \u0447\u0435\u0440\u0435\u0437 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 DataChannel \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0451\u043d\u043d\u044b\u0439 metadata \u0441\u00a0<code>available: true<\/code>, \u0430 \u0437\u0430 \u043d\u0438\u043c \u2013 \u0447\u0430\u043d\u043a\u0438 \u0444\u0430\u0439\u043b\u0430. \u041f\u0435\u0440\u0435\u0441\u0431\u043e\u0440\u043a\u0430 WebRTC-\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043d\u0435 \u043d\u0443\u0436\u043d\u0430.<\/p>\n<p>\u0412\u0430\u0436\u043d\u0430\u044f \u0434\u0435\u0442\u0430\u043b\u044c: active-\u0448\u0430\u0440\u044b\u00a0<strong>\u043d\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044f\u0442\u0441\u044f \u0432 failed<\/strong>\u00a0\u043f\u0440\u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044f.\u00a0<code>FailShare()<\/code>\u00a0\u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f\u00a0<code>matched<\/code>\u00a0\u0438\u00a0<code>transferring<\/code>. Active-\u0448\u0430\u0440\u0430 \u043f\u0435\u0440\u0435\u0436\u0438\u0432\u0430\u0435\u0442 refresh \u2013 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c \u043f\u0435\u0440\u0435\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f, \u0446\u0438\u043a\u043b \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u0442\u0441\u044f.<\/p>\n<p>\u0418\u0437 \u043e\u0434\u043d\u043e\u0433\u043e \u043d\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0443\u0435\u043c\u043e\u0433\u043e\u00a0<code>File<\/code>\u00a0\u0432\u043e\u0437\u043d\u0438\u043a \u0446\u0435\u043b\u044b\u0439 \u0441\u043b\u043e\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430:\u00a0<code>fileHint<\/code>,\u00a0<code>available: false<\/code>, unhealthy share,\u00a0<code>reannounceFile()<\/code>, \u0437\u0430\u0449\u0438\u0442\u0430 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 active-\u0448\u0430\u0440 \u043e\u0442 \u043f\u0440\u0435\u0436\u0434\u0435\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0433\u043e \u0441\u0431\u043e\u044f. \u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u043e\u0439 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0434\u0438\u043a\u0442\u0443\u044e\u0442 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443.<\/p>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21166: \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430 \u2013 \u043d\u043e \u044d\u0442\u043e \u0435\u0449\u0451 \u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u0430\u044f state machine<\/h3>\n<p>\u0421\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0430\u0439\u043b\u0430 \u0438\u0434\u0451\u0442 \u043c\u0438\u043c\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043d\u043e \u0432\u043e\u043a\u0440\u0443\u0433 \u043d\u0435\u0433\u043e \u2013 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u0430\u044f \u043c\u0430\u0448\u0438\u043d\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u0441 \u0433\u0440\u0430\u043d\u0438\u0446\u0430\u043c\u0438 \u0434\u043e\u0432\u0435\u0440\u0438\u044f. \u041f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043a\u043b\u0438\u0435\u043d\u0442 \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0432\u0440\u0430\u0442\u044c.<\/p>\n<p>\u041d\u0430\u0447\u043d\u0451\u043c \u0441 \u0443\u0447\u0451\u0442\u0430 relay-\u0442\u0440\u0430\u0444\u0438\u043a\u0430. \u041a\u043b\u0438\u0435\u043d\u0442 \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u0442\u00a0<code>connection_type<\/code>\u00a0\u043f\u0440\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u2013 \u043d\u043e \u0441\u0435\u0440\u0432\u0435\u0440 \u0435\u043c\u0443 \u043d\u0435 \u0432\u0435\u0440\u0438\u0442. \u041f\u0440\u0438 \u0432\u044b\u0434\u0430\u0447\u0435 TURN credentials \u0441\u0442\u0430\u0432\u0438\u0442\u0441\u044f Redis-\u0444\u043b\u0430\u0433\u00a0<code>turn_issued:{share_id}<\/code>, \u0438 \u043f\u0440\u0438\u00a0<code>transfer_complete<\/code>\u00a0\u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0438\u043c\u0435\u043d\u043d\u043e \u0435\u0433\u043e:<\/p>\n<pre><code class=\"go\">\/\/ \u0421\u0435\u0440\u0432\u0435\u0440: \u0433\u0440\u0430\u043d\u0438\u0446\u0430 \u0434\u043e\u0432\u0435\u0440\u0438\u044f \u2013 \u0443\u0447\u0451\u0442 relay-\u0442\u0440\u0430\u0444\u0438\u043a\u0430turnIssued, _ := redis.Get(ctx, \"turn_issued:\"+shareID).Bool()if turnIssued {    relayRepo.IncrementUsage(ctx, share.AccountID) \/\/ \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u0430\u044f \u043f\u0440\u0430\u0432\u0434\u0430}\/\/ Reset \u0448\u0430\u0440\u044b \u0434\u043b\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044fshareRepo.ResetForReuse(ctx, shareID)hub.RemoveClient(shareID, \"receiver\")hub.PromoteNextReceiver(shareID) \/\/ DB-first CAS, \u0437\u0430\u0442\u0435\u043c room placement<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u043e\u0442 \u0436\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f \u043d\u0435\u0434\u043e\u0432\u0435\u0440\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0438 \u043d\u0430 \u0434\u0440\u0443\u0433\u0438\u0445 \u0443\u0440\u043e\u0432\u043d\u044f\u0445. TURN credentials \u0432\u044b\u0434\u0430\u044e\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u0435\u0440\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430\u043c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0439 \u0448\u0430\u0440\u044b: \u0434\u043b\u044f active \u2013 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044e, \u0434\u043b\u044f matched\/transferring \u2013 \u043e\u0431\u043e\u0438\u043c. \u041f\u043e\u0434\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c\u00a0<code>share_id<\/code>\u00a0\u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a relay \u043d\u0435\u043b\u044c\u0437\u044f. \u041f\u0435\u0440\u0435\u0445\u043e\u0434\u044b \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f\u043c\u0438 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u044b CAS:\u00a0<code>UpdateStatus(expectedStatus, newStatus)<\/code>\u00a0\u043e\u0442\u0432\u0435\u0440\u0433\u0430\u0435\u0442 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u044b, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u043e\u0442\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0433\u043e\u043d\u043a\u0438 \u043f\u0440\u0438 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u043c signaling \u043e\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432.<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0448\u0430\u0440\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0432\u00a0<code>active<\/code>\u00a0\u0447\u0435\u0440\u0435\u0437\u00a0<code>ResetForReuse<\/code>, receiver \u0443\u0434\u0430\u043b\u044f\u0435\u0442\u0441\u044f \u0438\u0437 \u043a\u043e\u043c\u043d\u0430\u0442\u044b, \u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0438\u0437 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u043f\u0440\u043e\u043c\u043e\u0443\u0442\u0438\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 DB-first CAS: \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0437\u0430\u043f\u0438\u0441\u044c \u0432 \u0431\u0430\u0437\u0443, \u043f\u043e\u0442\u043e\u043c \u0440\u0430\u0437\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0431\u0443\u0444\u0435\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0445 ICE-\u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442\u043e\u0432.<\/p>\n<p>\u0414\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0430\u0439\u043b\u0430 \u0438\u0434\u0451\u0442 \u043c\u0438\u043c\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0432\u043e\u0438\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u0437\u0430\u043f\u0438\u0441\u044f\u043c. \u041a\u043b\u0438\u0435\u043d\u0442 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0442\u0435\u0440\u044f\u0442\u044c \u0441\u0432\u044f\u0437\u044c, \u0431\u044b\u0442\u044c \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d \u0438\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u043e\u0432\u0440\u0430\u0442\u044c. \u0421\u0435\u0440\u0432\u0435\u0440\u043d\u0430\u044f \u043c\u0430\u0448\u0438\u043d\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u2013 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u044f \u0446\u0435\u043b\u043e\u0441\u0442\u043d\u043e\u0441\u0442\u0438.<\/p>\n<h3>\u0411\u043e\u043d\u0443\u0441: Safari \u0438 DataChannel-only connection<\/h3>\n<p>Safari \u0434\u043b\u044f DataChannel-only \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f \u043d\u0435 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442 host ICE candidates. \u0420\u0435\u0448\u0435\u043d\u0438\u0435 \u2013 \u0444\u0435\u0439\u043a\u043e\u0432\u044b\u0439 audio transceiver:<\/p>\n<pre><code class=\"javascript\">if (isSafari &amp;&amp; typeof pc.addTransceiver === 'function') {  pc.addTransceiver('audio', { direction: 'recvonly' });}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u00a0<code>getUserMedia<\/code>\u00a0\u0438\u043b\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0443 \u2013 \u0447\u0438\u0441\u0442\u043e \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043d\u044b\u0439 \u043e\u0431\u0445\u043e\u0434\u043d\u043e\u0439 \u043f\u0440\u0438\u0451\u043c. \u041a\u0440\u043e\u043c\u0435 \u044d\u0442\u043e\u0433\u043e: Safari \u043c\u043e\u0436\u0435\u0442 \u0443\u043f\u0430\u0441\u0442\u044c \u043d\u0430 \u043f\u0443\u0441\u0442\u044b\u0445 URL \u0432 TURN-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 (\u043d\u0443\u0436\u043d\u0430 \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044f),\u00a0<code>getStats()<\/code>\u00a0\u043d\u0435 \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e (\u043f\u043e\u0432\u0442\u043e\u0440 3 \u00d7 500 ms \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0442\u0438\u043f\u0430 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f), \u0430 SCTP\u00a0<code>maxMessageSize<\/code>\u00a0\u043c\u043e\u0436\u0435\u0442 \u0432\u0435\u0440\u043d\u0443\u0442\u044c 64 KB, \u043b\u043e\u043c\u0430\u044f \u0440\u0430\u0437\u043c\u0435\u0440 \u0447\u0430\u043d\u043a\u0430. Safari \u2013 \u044d\u0442\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u0438\u0440 \u0434\u0430\u0436\u0435 \u0432 2026 \u0433\u043e\u0434\u0443.<\/p>\n<h3>Trade-offs<\/h3>\n<p>\u041e\u0431\u0430 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043e\u043d\u043b\u0430\u0439\u043d \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e. \u041d\u0435\u0442 offline-\u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438, \u043d\u0435\u0442 \u0431\u0443\u0444\u0435\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u2013 \u044d\u0442\u043e \u043e\u0441\u043e\u0437\u043d\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0431\u043e\u0440. \u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0441 \u043d\u0443\u043b\u0435\u0432\u044b\u043c \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435\u043c \u0446\u0435\u043d\u043d\u0435\u0435, \u0447\u0435\u043c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043e\u0444\u0444\u043b\u0430\u0439\u043d-\u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b \u0431\u044b \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0433\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0438 \u043a\u043b\u044e\u0447\u0435\u0439 \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<p>\u041e\u0431\u044a\u0435\u043a\u0442\u00a0<code>File<\/code>\u00a0\u043d\u0435 \u043f\u0435\u0440\u0435\u0436\u0438\u0432\u0430\u0435\u0442 refresh \u2013 \u044d\u0442\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b, \u0432\u043e\u043a\u0440\u0443\u0433 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u044b \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u043b\u0438 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b (unhealthy share, re-attach), \u043d\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u0435\u0440\u0435\u0442\u0430\u0449\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u0437\u0430\u043d\u043e\u0432\u043e. \u0410 streaming download \u0447\u0435\u0440\u0435\u0437 File System Access API \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0432 Chrome \u0438 Edge \u2013 Firefox \u0438 Safari \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442 in-memory fallback, \u0438 \u0444\u0430\u0439\u043b\u044b \u0431\u043e\u043b\u044c\u0448\u0435 \u0433\u0438\u0433\u0430\u0431\u0430\u0439\u0442\u0430 \u043c\u043e\u0433\u0443\u0442 \u0443\u0440\u043e\u043d\u0438\u0442\u044c \u0431\u0440\u0430\u0443\u0437\u0435\u0440.<\/p>\n<p>\u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u043f\u043e\u0443\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f \u043f\u0440\u043e \u0433\u0440\u0430\u043d\u0438\u0446\u0443 \u0434\u043e\u0432\u0435\u0440\u0438\u044f \u043d\u0430 relay-\u043a\u0432\u043e\u0442\u0435. \u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0434\u043d\u0435\u0432\u043d\u0430\u044f \u0431\u0430\u0439\u0442\u043e\u0432\u0430\u044f \u043a\u0432\u043e\u0442\u0430 \u0444\u043e\u0440\u0441\u0438\u043b\u0430\u0441\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c: \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0434\u0430\u0432\u0430\u043b TURN credentials (\u043e\u043d\u0438 \u043d\u0443\u0436\u043d\u044b \u0438 \u0434\u043b\u044f text\/URL-\u0448\u0430\u0440, \u0433\u0434\u0435 \u043a\u0432\u043e\u0442 \u043d\u0435\u0442), \u0430 <code>relay_limits_exceeded<\/code> \u0432 \u043e\u0442\u0432\u0435\u0442\u0435 \u0431\u044b\u043b \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u043e\u0439 \u043a\u043b\u0438\u0435\u043d\u0442\u0443, \u0430 \u043d\u0435 \u0437\u0430\u043f\u0440\u0435\u0442\u043e\u043c. \u041c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442 \u043f\u0440\u043e\u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u043b \u0431\u044b \u0444\u043b\u0430\u0433 \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u0431\u044b \u043a\u0432\u043e\u0442\u0443 \u0434\u043e \u043a\u043e\u043d\u0446\u0430.<\/p>\n<p>\u0418\u0441\u043f\u0440\u0430\u0432\u0438\u043b\u043e\u0441\u044c \u0434\u0432\u0443\u043c\u044f \u043a\u0430\u0441\u0430\u043d\u0438\u044f\u043c\u0438 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435. \u041f\u0435\u0440\u0432\u043e\u0435 \u2014 \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0448\u0430\u0440\u0430 \u043f\u043e\u043c\u0435\u0447\u0430\u0435\u0442\u0441\u044f <code>has_file<\/code>. \u0412\u0442\u043e\u0440\u043e\u0435 \u2014 \u0432 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0435 <code>GET \/api\/ice-servers<\/code> \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430: \u0435\u0441\u043b\u0438 <code>share.has_file &amp;&amp; relay_limits_exceeded<\/code> \u2192 \u0432 \u043e\u0442\u0432\u0435\u0442 \u0438\u0434\u0443\u0442 \u0442\u043e\u043b\u044c\u043a\u043e STUN-\u0441\u0435\u0440\u0432\u0435\u0440\u044b, \u0431\u0435\u0437 TURN. Text\/URL-\u0448\u0430\u0440\u044b (<code>has_file=false<\/code>) TURN \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442 \u043a\u0430\u043a \u0440\u0430\u043d\u044c\u0448\u0435 \u2014 \u0434\u043b\u044f \u043d\u0438\u0445 \u043a\u0432\u043e\u0442 \u0438 \u043d\u0435 \u0431\u044b\u043b\u043e. \u041c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 credentials \u0434\u043b\u044f \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0433\u043e relay \u043f\u0440\u0438 \u0438\u0441\u0447\u0435\u0440\u043f\u0430\u043d\u043d\u043e\u0439 \u043a\u0432\u043e\u0442\u0435. \u0423\u0437\u043a\u043e\u0435 \u043e\u043a\u043d\u043e \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u2014 in-flight allocation, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u043e\u0435 \u0434\u043e \u043f\u0435\u0440\u0435\u0441\u0435\u0447\u0435\u043d\u0438\u044f \u043a\u0432\u043e\u0442\u044b, \u043c\u043e\u0436\u0435\u0442 \u0434\u043e\u0440\u0435\u0436\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0443; \u044d\u0442\u043e \u0437\u0430\u043a\u0440\u043e\u0435\u0442\u0441\u044f reactive kill-switch&#8217;\u043e\u043c \u0447\u0435\u0440\u0435\u0437 coturn admin CLI \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438.<\/p>\n<p>\u041f\u043b\u044e\u0441 hard-\u043b\u0438\u043c\u0438\u0442\u044b \u043d\u0430 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430: coturn \u0434\u0435\u0440\u0436\u0438\u0442 5 Mbps \u043d\u0430 \u0441\u0435\u0441\u0441\u0438\u044e (<code>max-bps<\/code>) \u0438 \u043c\u0430\u043a\u0441\u0438\u043c\u0443\u043c 50 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 allocations (<code>total-quota<\/code>). \u0421\u0435\u0440\u0432\u0438\u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c \u043d\u0435 \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c, \u043a\u0430\u043a \u043d\u0438 \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0439.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0430\u0448\u0435\u0439 \u0437\u0430\u0434\u0430\u0447\u0435 \u043d\u0443\u0436\u043d\u044b \u043e\u0444\u0444\u043b\u0430\u0439\u043d-\u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0430, \u0438\u0441\u0442\u043e\u0440\u0438\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447, \u0444\u043e\u043d\u043e\u0432\u0430\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430, \u0434\u0435\u0434\u0443\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u044f \u0438\u043b\u0438 \u043c\u0443\u043b\u044c\u0442\u0438\u0434\u0435\u0432\u0430\u0439\u0441 \u2013 \u043f\u0440\u043e\u0449\u0435 \u0438 \u0447\u0435\u0441\u0442\u043d\u0435\u0435 \u0432\u0437\u044f\u0442\u044c S3 \u0441 presigned URL, Google Drive API \u0438\u043b\u0438 \u0434\u0430\u0436\u0435 Telegram-\u0431\u043e\u0442\u0430. P2P \u0447\u0435\u0440\u0435\u0437 WebRTC \u043e\u043f\u0440\u0430\u0432\u0434\u0430\u043d, \u043a\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0438\u0430\u043b\u044c\u043d\u043e \u0432\u0430\u0436\u043d\u043e \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0447\u0443\u0436\u0438\u0435 \u0444\u0430\u0439\u043b\u044b \u043d\u0430 \u0441\u0432\u043e\u0451\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0435, \u0430 \u043e\u0431\u0430 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430 \u0433\u043e\u0442\u043e\u0432\u044b \u0431\u044b\u0442\u044c \u043e\u043d\u043b\u0430\u0439\u043d \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e.<\/p>\n<h3>\u0427\u0442\u043e \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u00ab\u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u0447\u0435\u0440\u0435\u0437 WebRTC\u00bb<\/h3>\n<p>\u00ab\u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0444\u0430\u0439\u043b\u0430\u00bb \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435 \u0443\u0441\u0442\u0440\u043e\u0435\u043d\u0430 \u0441\u043b\u043e\u0436\u043d\u0435\u0435, \u0447\u0435\u043c \u043a\u0430\u0436\u0435\u0442\u0441\u044f \u0438\u0437 \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0430. \u0421\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u044f\u0447\u0435\u0442\u0441\u044f \u043d\u0435 \u0432 \u043e\u0434\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435, \u0430 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0430 \u043f\u043e \u0432\u0441\u0435\u043c\u0443 \u0441\u0442\u0435\u043a\u0443.<\/p>\n<p>\u041d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0430 DataChannel \u0434\u0430\u0451\u0442 \u0431\u0443\u0444\u0435\u0440 \u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0435\u00a0<code>onmessage<\/code>, \u043d\u043e \u043d\u0435 \u0434\u0430\u0451\u0442 \u043d\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c, \u043d\u0438 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438, \u043d\u0438 \u0430\u0434\u0430\u043f\u0442\u0430\u0446\u0438\u0438 \u043f\u043e\u0434 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043a\u0430\u043d\u0430\u043b\u0430. \u0412\u0441\u0451 \u044d\u0442\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043e\u0432\u0435\u0440\u0445: drain-\u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433, \u0430\u0434\u0430\u043f\u0442\u0438\u0432\u043d\u044b\u0435 \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u044b,\u00a0<code>transfer_ack<\/code>\u00a0\u043f\u0435\u0440\u0435\u0434\u00a0<code>transfer_done<\/code>.<\/p>\n<p>\u041d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 \u2013 \u0441\u0432\u043e\u0438 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0434\u0438\u043a\u0442\u0443\u044e\u0442 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b:\u00a0<code>File<\/code>\u00a0\u043d\u0435 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0443\u0435\u0442\u0441\u044f,\u00a0<code>showSaveFilePicker()<\/code>\u00a0\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442,\u00a0<code>URL.revokeObjectURL()<\/code>\u00a0\u043d\u0435\u043b\u044c\u0437\u044f \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0441\u0440\u0430\u0437\u0443, \u0430 DataChannel \u043c\u043e\u0436\u0435\u0442 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0443\u0444\u0435\u0440 \u043c\u0435\u0436\u0434\u0443 \u0432\u044b\u0437\u043e\u0432\u0430\u043c\u0438\u00a0<code>onmessage<\/code>. \u041a\u0430\u0436\u0434\u043e\u0435 \u0438\u0437 \u044d\u0442\u0438\u0445 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439 \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u044c\u043d\u044b\u0439 \u0448\u0430\u0433.<\/p>\n<p>\u041d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u2013 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0446\u0438\u044f. \u0421\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0430\u0439\u043b\u0430 \u0438\u0434\u0451\u0442 \u043c\u0438\u043c\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u043d\u043e \u0441\u0435\u0440\u0432\u0435\u0440 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u0441\u0435\u043c \u0432\u043e\u043a\u0440\u0443\u0433: \u043c\u0430\u0448\u0438\u043d\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u0441 CAS-\u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430\u043c\u0438, \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u0435\u0439, \u0443\u0447\u0451\u0442 relay-\u0442\u0440\u0430\u0444\u0438\u043a\u0430 \u0447\u0435\u0440\u0435\u0437 Redis, \u0437\u0430\u0449\u0438\u0442\u0430 TURN credentials. \u041a\u043b\u0438\u0435\u043d\u0442 \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0432\u0440\u0430\u0442\u044c \u2013 \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0435\u043c\u0443 \u0432\u0435\u0440\u0438\u0442\u044c.<\/p>\n<p>WebRTC \u0434\u0430\u0451\u0442 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442. \u041f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u043c\u0443 \u2013 \u0438 \u043e\u043d \u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0441\u043b\u043e\u0436\u043d\u0435\u0435 \u0441\u0430\u043c\u043e\u0433\u043e WebRTC.<\/p>\n<hr\/>\n<p>\u042d\u0442\u043e\u0442 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b \u043e\u0431\u043a\u0430\u0442\u0430\u043d \u043d\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0444\u0430\u0439\u043b\u043e\u0432 \u2013\u00a0<a href=\"http:\/\/komu.online\" rel=\"noopener noreferrer nofollow\">komu.online<\/a>.<\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1022522\/\">https:\/\/habr.com\/ru\/articles\/1022522\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0420\u0438\u0441. 1. dc.send(file) \u043d\u0435 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0444\u0430\u0439\u043b \u0443\u0436\u0435 \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d.\u0421\u0430\u043c\u0430\u044f \u043e\u043f\u0430\u0441\u043d\u0430\u044f \u0438\u043b\u043b\u044e\u0437\u0438\u044f \u0432 WebRTC-\u0444\u0430\u0439\u043b\u043e\u043e\u0431\u043c\u0435\u043d\u0435 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u0430\u043a:const dc = pc.createDataChannel(&#8216;file&#8217;);for (let offset = 0; offset &lt; file.size; offset += CHUNK) {  dc.send(file.slice(offset, offset + CHUNK));}dc.send(JSON.stringify({ type: &#8216;transfer_done&#8217; }));\/\/ \u0413\u043e\u0442\u043e\u0432\u043e! &#8230;\u0438\u043b\u0438 \u043d\u0435\u0442?\u0412\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043f\u0440\u0430\u0432\u0434\u043e\u043f\u043e\u0434\u043e\u0431\u043d\u043e. DataChannel \u043e\u0442\u043a\u0440\u044b\u0442, \u0447\u0430\u043d\u043a\u0438 \u043b\u0435\u0442\u044f\u0442, transfer_done \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d. \u0412 \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e. \u0412 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435 \u2013 \u043d\u0435\u0442. \u041d\u0430 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u043c relay \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441 \u043f\u043e\u043a\u0430\u0436\u0435\u0442 100%, \u0430 \u0444\u0430\u0439\u043b \u043d\u0430 \u0434\u0438\u0441\u043a\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0440\u0435\u0437\u0430\u043d \u2013 \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e send() \u043a\u043b\u0430\u0434\u0451\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 SCTP-\u0431\u0443\u0444\u0435\u0440, \u0430 \u043d\u0435 \u00ab\u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432 \u0441\u0435\u0442\u044c\u00bb. \u041f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044c \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043d\u0435 \u0433\u043e\u0442\u043e\u0432 \u043a \u0437\u0430\u043f\u0438\u0441\u0438. \u041e\u0431\u044a\u0435\u043a\u0442 File \u043d\u0435 \u043f\u0435\u0440\u0435\u0436\u0438\u0432\u0451\u0442 refresh. \u0410 signaling-\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u0443\u043c\u0435\u0440\u0435\u0442\u044c \u0440\u0430\u043d\u044c\u0448\u0435 DataChannel \u2013 \u0438 peer_left \u043e\u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043d\u0435 \u043e\u0448\u0438\u0431\u043a\u043e\u0439, \u0430 \u0448\u0442\u0430\u0442\u043d\u044b\u043c \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0435\u043c.\u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u2013 \u0440\u0430\u0437\u0431\u043e\u0440 \u0448\u0435\u0441\u0442\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c (\u0438 \u043e\u0434\u043d\u043e\u0433\u043e Safari-\u0441\u044e\u0440\u043f\u0440\u0438\u0437\u0430), \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u044e\u0442 \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0435 \u0444\u0430\u0439\u043b\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 WebRTC \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435. \u041d\u0438 \u043e\u0434\u043d\u0430 \u0438\u0437 \u043d\u0438\u0445 \u043d\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0430 \u0432 \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0430\u0445.\u0421\u0442\u0430\u0442\u044c\u044f \u0440\u043e\u0434\u0438\u043b\u0430\u0441\u044c \u043f\u043e \u0438\u0442\u043e\u0433\u0430\u043c \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0430\u0434 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c P2P-\u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0444\u0430\u0439\u043b\u043e\u0432. \u0427\u0442\u043e\u0431\u044b \u0434\u0430\u043b\u044c\u0448\u0435 \u0442\u0435\u0440\u043c\u0438\u043d\u044b \u0431\u044b\u043b\u0438 \u043f\u043e\u043d\u044f\u0442\u043d\u044b \u2013 \u0432\u043e\u0442 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0432 \u0434\u0432\u0443\u0445 \u0441\u043b\u043e\u0432\u0430\u0445.\u0424\u0430\u0439\u043b\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u044e\u0442\u0441\u044f \u043c\u0435\u0436\u0434\u0443 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430\u043c\u0438 \u0447\u0435\u0440\u0435\u0437 WebRTC DataChannel. \u0412 \u043e\u0431\u044b\u0447\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u2013 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e (P2P), \u043f\u0440\u0438 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u0440\u044f\u043c\u043e\u0433\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u2013 \u0447\u0435\u0440\u0435\u0437 TURN relay. \u0421\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0430\u0439\u043b\u0430 \u043d\u0438 \u0432 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u0440\u0435\u0436\u0438\u043c\u043e\u0432 \u2013 \u043e\u043d \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0438\u0440\u0443\u0435\u0442: REST API \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438, WebSocket \u0434\u043b\u044f signaling, \u043c\u0430\u0448\u0438\u043d\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u0432. \u0414\u0430\u0436\u0435 \u0447\u0435\u0440\u0435\u0437 relay DTLS-\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f end-to-end \u2013 relay \u043f\u0440\u043e\u043a\u0441\u0438\u0440\u0443\u0435\u0442 \u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043f\u043e\u0442\u043e\u043a, \u043d\u0435 \u0438\u043c\u0435\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u043c\u0443.Relay \u0441\u0442\u043e\u0438\u0442 \u0434\u0435\u043d\u0435\u0433 (\u0442\u0440\u0430\u0444\u0438\u043a, VPS), \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 coturn \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u0436\u0451\u0441\u0442\u043a\u0438\u0435 \u043b\u0438\u043c\u0438\u0442\u044b: \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 \u0444\u0430\u0439\u043b\u0430 \u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0447 \u0432 \u0434\u0435\u043d\u044c. \u042d\u0442\u043e \u0430\u0432\u0430\u0440\u0438\u0439\u043d\u044b\u0439 \u0437\u0430\u043f\u0430\u0441\u043d\u043e\u0439 \u043f\u0443\u0442\u044c, \u0430 \u043d\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0440\u0435\u0436\u0438\u043c. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0441\u0432\u043e\u0439 TURN-\u0441\u0435\u0440\u0432\u0435\u0440 (Metered, Twilio, Xirsys, \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u2013 \u043d\u0435\u0432\u0430\u0436\u043d\u043e; \u0443 Metered, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0439 \u0442\u0430\u0440\u0438\u0444 \u0434\u0430\u0451\u0442 500 GB). \u0423\u0447\u0451\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 custom TURN \u0448\u0438\u0444\u0440\u0443\u044e\u0442\u0441\u044f \u043d\u0435\u044d\u043a\u0441\u0442\u0440\u0430\u0433\u0438\u0440\u0443\u0435\u043c\u044b\u043c \u043a\u043b\u044e\u0447\u043e\u043c \u0432 IndexedDB \u0438 \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u043f\u043e\u043a\u0438\u0434\u0430\u044e\u0442 \u0431\u0440\u0430\u0443\u0437\u0435\u0440. \u041d\u0430 custom TURN \u043a\u0432\u043e\u0442 \u0441\u043e \u0441\u0442\u043e\u0440\u043e\u043d\u044b \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043d\u0435\u0442.\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430: P2P \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0441 signaling \u0438 relay fallback\u0412 \u0432\u0430\u0448\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043c\u043e\u0436\u0435\u0442 \u043d\u0435 \u0431\u044b\u0442\u044c custom TURN \u0438\u043b\u0438 relay-\u043a\u0432\u043e\u0442. \u041d\u043e \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u2013 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0431\u0443\u0444\u0435\u0440\u043e\u043c, \u0433\u043e\u0442\u043e\u0432\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f, \u043f\u0435\u0440\u0435\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435, \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u043d\u044b\u0439 File \u2013 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0443\u0442 \u0432 \u043b\u044e\u0431\u043e\u043c WebRTC-\u0444\u0430\u0439\u043b\u043e\u043e\u0431\u043c\u0435\u043d\u043d\u0438\u043a\u0435. \u0414\u0430\u043b\u044c\u0448\u0435 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u043c \u0432\u0441\u0435 \u0448\u0435\u0441\u0442\u044c, \u0441 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u0438\u043a\u043e\u0439 \u0438\u0437 \u043d\u0430\u0448\u0435\u0433\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u044f.\u0420\u0438\u0441. 2. \u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438. \u041f\u0440\u044f\u043c\u043e\u0435 P2P-\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u2013 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0443\u0442\u044c, TURN relay (\u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 coturn \u0438\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439) \u2013 fallback \u043f\u0440\u0438 NAT, firewall \u0438\u043b\u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0439 \u0441\u0435\u0442\u0438. \u0421\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0430\u0439\u043b\u0430 \u0438\u0434\u0451\u0442 \u043c\u0438\u043c\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0434\u0430\u0436\u0435 \u043f\u0440\u0438 relay: DTLS-\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f end-to-end.\u0421\u0435\u0440\u0432\u0435\u0440 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442, \u043d\u043e \u0431\u0435\u0437 \u043d\u0435\u0433\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0439: \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0430\u0439\u043b\u0430 \u0438\u0434\u0451\u0442 \u043c\u0438\u043c\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 WebRTC DataChannel, \u0441\u0435\u0440\u0432\u0435\u0440 \u0435\u0433\u043e \u043d\u0435 \u0432\u0438\u0434\u0438\u0442 \u0438 \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442. \u041d\u043e \u00ab\u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442 \u0444\u0430\u0439\u043b\u044b\u00bb \u043d\u0435 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u00ab\u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u043e\u0447\u0442\u0438 \u043d\u0435 \u043d\u0443\u0436\u0435\u043d\u00bb.\u0412\u043e\u043a\u0440\u0443\u0433 \u043a\u0430\u0436\u0434\u043e\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u2013 REST API \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438 \u0440\u0435\u0437\u043e\u043b\u0432\u0430 \u00ab\u0448\u0430\u0440\u044b\u00bb (share), WebSocket \u0434\u043b\u044f signaling, \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u0430\u044f \u043c\u0430\u0448\u0438\u043d\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u0438 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u0435\u0439.\u0423 \u0448\u0430\u0440\u044b \u0435\u0441\u0442\u044c \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b. \u041a\u043b\u044e\u0447\u0435\u0432\u043e\u0435: \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0448\u0430\u0440\u0430 \u043d\u0435 \u0443\u043c\u0438\u0440\u0430\u0435\u0442, \u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0432\u00a0active\u00a0\u2013 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043e\u043d\u043b\u0430\u0439\u043d \u0438 \u0436\u0434\u0451\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f. \u0415\u0441\u043b\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c \u0437\u0430\u043d\u044f\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0435\u0439, \u043d\u043e\u0432\u044b\u0439 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044c \u043f\u043e\u043f\u0430\u0434\u0430\u0435\u0442 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u043c\u0435\u0441\u0442\u043e \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0439.\u0420\u0438\u0441. 3. \u0416\u0438\u0437\u043d\u0435\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u0448\u0430\u0440\u044b. \u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u0446\u0438\u043a\u043b active \u2192 matched \u2192 transferring \u2192 active \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u2013 \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043e\u043d\u043b\u0430\u0439\u043d \u0434\u043b\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f. \u0422\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u2013 \u0442\u043e\u043b\u044c\u043a\u043e expired \u0438 cancelled; failed \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 reactivate.\u041e\u0434\u0438\u043d WebSocket \u043d\u0430 \u0448\u0430\u0440\u0443. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u043d\u043e ECDSA. \u0421\u0435\u0440\u0432\u0435\u0440 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0438\u0440\u0443\u0435\u0442: \u043a\u0442\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c, \u043a\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044c, compare-and-swap \u043d\u0430 \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0443 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f, TTL, \u043e\u0447\u0438\u0441\u0442\u043a\u0430 \u0437\u0430\u0432\u0438\u0441\u0448\u0438\u0445 \u0448\u0430\u0440. \u0421\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0430\u0439\u043b\u0430 \u0438\u0434\u0451\u0442 \u043c\u0438\u043c\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u043d\u043e \u0441\u0435\u0440\u0432\u0435\u0440 \u2013 \u0434\u0438\u0440\u0438\u0436\u0451\u0440 \u0432\u0441\u0435\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430.\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21161: P2P \u043c\u043e\u0436\u0435\u0442 \u043d\u0435 \u0441\u043b\u0443\u0447\u0438\u0442\u044c\u0441\u044f\u0412 \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0430\u0445 WebRTC \u2013 \u044d\u0442\u043e \u043c\u0430\u0433\u0438\u044f \u043f\u0440\u044f\u043c\u044b\u0445 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439. \u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 \u0438\u0434\u0451\u0442 \u0447\u0435\u0440\u0435\u0437 TURN relay. Symmetric NAT, \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 firewall, \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u0430\u044f \u0441\u0435\u0442\u044c \u2013 relay \u043d\u0443\u0436\u0435\u043d \u043d\u0435 \u043a\u0430\u043a \u00ab\u0440\u0435\u0434\u043a\u0438\u0439 \u043a\u043e\u0441\u0442\u044b\u043b\u044c\u00bb, \u0430 \u043a\u0430\u043a \u0448\u0442\u0430\u0442\u043d\u044b\u0439 \u0437\u0430\u043f\u0430\u0441\u043d\u043e\u0439 \u043f\u0443\u0442\u044c.\u0418 relay \u043c\u0435\u043d\u044f\u0435\u0442 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442, \u043d\u043e \u0438 \u0432\u0441\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u043d\u0438\u0436\u0435 \u043f\u043e \u0441\u0442\u0435\u043a\u0443.\u0422\u0438\u043f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 \u043f\u043e ICE-\u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442\u0430\u043c \u2013 \u043f\u0440\u0438\u0447\u0451\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044e\u0442\u0441\u044f\u00a0\u043e\u0431\u0430: local \u0438 remote. \u0415\u0441\u043b\u0438 \u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u0438\u043d relay \u2013 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043a\u043b\u0430\u0441\u0441\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043a\u0430\u043a TURN. \u0414\u0430\u043b\u044c\u0448\u0435 TURN \u0434\u0435\u043b\u0438\u0442\u0441\u044f \u043d\u0430\u00a0server_relay\u00a0(\u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 coturn, \u0441 \u043a\u0432\u043e\u0442\u0430\u043c\u0438) \u0438\u00a0custom_relay\u00a0(\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 TURN, \u0431\u0435\u0437 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439).\u041e\u0442 \u0442\u0438\u043f\u0430 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u0440\u0430\u0437\u043c\u0435\u0440 \u0447\u0430\u043d\u043a\u0430:function getOptimalChunkSize(classification: string): number {  switch (classification) {    case &#8216;p2p&#8217;:          return 1024 * 1024;  \/\/ 1 MB \u2013 \u043c\u0435\u043d\u044c\u0448\u0435 async ops    case &#8216;custom_relay&#8217;: return 512 * 1024;   \/\/ 512 KB    case &#8216;server_relay&#8217;: return 64 * 1024;    \/\/ 64 KB \u2013 \u0431\u044b\u0441\u0442\u0440\u044b\u0435 retransmit    default:             return 512 * 1024;  }}\u041f\u043e\u0447\u0435\u043c\u0443 64 KB \u043d\u0430 server relay? SCTP retransmit \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0447\u0430\u043d\u043a\u043e\u0432. \u041d\u0430 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0438 \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0447\u0430\u043d\u043a \u043f\u0440\u0438 \u043f\u043e\u0442\u0435\u0440\u0435 \u0440\u0435\u0442\u0440\u0430\u043d\u0441\u043b\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0446\u0435\u043b\u0438\u043a\u043e\u043c \u2013 \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430 \u0440\u0430\u0441\u0442\u0451\u0442 \u044d\u043a\u0441\u043f\u043e\u043d\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u043e. \u041c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u0435 \u0447\u0430\u043d\u043a\u0438 \u0434\u0430\u044e\u0442 \u0431\u044b\u0441\u0442\u0440\u044b\u0439 recovery.\u0414\u043b\u044f server relay \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0442 \u043a\u0432\u043e\u0442\u044b: \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 \u0444\u0430\u0439\u043b\u0430, \u0434\u043d\u0435\u0432\u043d\u043e\u0439 \u043b\u0438\u043c\u0438\u0442 \u043d\u0430 \u0430\u043a\u043a\u0430\u0443\u043d\u0442, \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0439 \u0434\u043d\u0435\u0432\u043d\u043e\u0439 \u043b\u0438\u043c\u0438\u0442. \u041b\u0438\u043c\u0438\u0442\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044e\u0442\u0441\u044f \u043d\u0430\u00a0\u043e\u0431\u0435\u0438\u0445\u00a0\u0441\u0442\u043e\u0440\u043e\u043d\u0430\u0445 \u2013 \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e sender \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c custom TURN (\u0431\u0435\u0437 \u043b\u0438\u043c\u0438\u0442\u043e\u0432), \u0430 receiver \u2013 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 coturn (\u0441 \u043b\u0438\u043c\u0438\u0442\u0430\u043c\u0438).\u041f\u0435\u0440\u0435\u0434 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0435\u0439 \u043c\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0431\u044b\u0441\u0442\u0440\u0443\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438 TURN \u2013 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439\u00a0RTCPeerConnection\u00a0\u0441\u00a0iceTransportPolicy: &#8216;relay&#8217;. \u0417\u0430 5 \u0441\u0435\u043a\u0443\u043d\u0434 \u0437\u043d\u0430\u0435\u043c, \u0436\u0438\u0432 \u043b\u0438 coturn, \u043d\u0435 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0443\u0442\u044c. \u042d\u0442\u043e \u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043c\u0438\u043d\u0443\u0442\u0443 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f \u0441 \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u043e\u043c, \u0435\u0441\u043b\u0438 relay \u043c\u0451\u0440\u0442\u0432.\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u21162:\u00a0dc.send()\u00a0\u043d\u0435 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u00ab\u0444\u0430\u0439\u043b \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u00bb\u042d\u0442\u043e \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0441\u0442\u0430\u0442\u044c\u0438. \u041c\u044b \u043f\u043e\u0439\u043c\u0430\u043b\u0438 \u0435\u0451 \u043d\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 relay: \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441-\u0431\u0430\u0440 \u043f\u043e\u043a\u0430\u0437\u0430\u043b 100%, \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c \u043f\u043e\u0441\u043b\u0430\u043b\u00a0transfer_done\u00a0\u2013 \u0430 \u043d\u0430 \u0434\u0438\u0441\u043a\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f \u043d\u0435 \u0445\u0432\u0430\u0442\u0430\u043b\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 \u0434\u0432\u0443\u0445 \u043c\u0435\u0433\u0430\u0431\u0430\u0439\u0442. \u0424\u0430\u0439\u043b \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b \u043f\u043e\u043b\u043d\u044b\u043c, \u043d\u043e \u0431\u044b\u043b \u043e\u0431\u0440\u0435\u0437\u0430\u043d. \u041f\u0440\u0438\u0447\u0438\u043d\u0430 \u043e\u043a\u0430\u0437\u0430\u043b\u0430\u0441\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0438 \u043d\u0435\u043f\u0440\u0438\u044f\u0442\u043d\u043e\u0439:\u00a0dc.send()\u00a0\u2013 \u044d\u0442\u043e\u00a0write()\u00a0\u0432 SCTP-\u0431\u0443\u0444\u0435\u0440 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430, \u0430 \u043d\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0432 \u0441\u0435\u0442\u044c. \u041c\u0435\u0436\u0434\u0443 \u00ab\u043f\u043e\u043b\u043e\u0436\u0438\u043b \u0432 \u0431\u0443\u0444\u0435\u0440\u00bb \u0438 \u00abreceiver \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u0432\u0441\u0435 \u0431\u0430\u0439\u0442\u044b\u00bb \u2013 \u043f\u0440\u043e\u043f\u0430\u0441\u0442\u044c.\u041d\u0430 \u0431\u044b\u0441\u0442\u0440\u043e\u043c P2P-\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0438 \u0431\u0443\u0444\u0435\u0440 \u043f\u0443\u0441\u0442 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0441\u0435\u0433\u0434\u0430. \u041d\u0430 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u043c relay (\u2248100 KB\/s) \u0431\u0443\u0444\u0435\u0440 \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0441\u0435\u043a\u0443\u043d\u0434\u044b \u043d\u0435\u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445. \u0415\u0441\u043b\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c\u00a0transfer_done\u00a0\u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e \u0447\u0430\u043d\u043a\u0430 \u2013 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u0434\u0451\u0442 receiver&#8217;\u0443 \u0440\u0430\u043d\u044c\u0448\u0435, \u0447\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435.\u0412\u043e\u0442 \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0439 send loop (\u0443\u043f\u0440\u043e\u0449\u0451\u043d\u043d\u043e):const BUFFER_THRESHOLD = 8 * 1024 * 1024; \/\/ 8 MBconst BUFFER_LOW       = 6 * 1024 * 1024; \/\/ 6 MBdc.bufferedAmountLowThreshold = BUFFER_LOW;let nextBuffer = await file.slice(0, chunkSize).arrayBuffer();while (offset &lt; file.size) {  const buffer = nextBuffer;  \/\/ Overlapped I\/O: \u0447\u0438\u0442\u0430\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0447\u0430\u043d\u043a, \u043f\u043e\u043a\u0430 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0443\u0445\u043e\u0434\u0438\u0442 \u0432 \u0441\u0435\u0442\u044c  const readPromise = offset + buffer.byteLength &lt; file.size    ? file.slice(offset + buffer.byteLength, nextEnd).arrayBuffer()    : null;  \/\/ Backpressure: \u0436\u0434\u0451\u043c, \u043f\u043e\u043a\u0430 \u0431\u0443\u0444\u0435\u0440 \u043f\u043e\u0434\u043e\u043f\u0443\u0441\u0442\u0435\u0435\u0442  if (dc.bufferedAmount &gt;= BUFFER_THRESHOLD) {    await waitForBufferDrain(dc, computeDrainTimeout(dc.bufferedAmount, speed));  }  dc.send(buffer);  offset += buffer.byteLength;  \/\/ \u0420\u0435\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441 \u2013 \u043d\u0435 \u043d\u0430\u0438\u0432\u043d\u044b\u0439 offset  const effectiveSent = Math.max(0, offset &#8212; dc.bufferedAmount);  const pct = Math.min((effectiveSent \/ file.size) * 100, isLast ? 100 : 99);  nextBuffer = readPromise ? await readPromise : null;}\/\/ \u0416\u0434\u0451\u043c drain \u2192 ACK \u043e\u0442 receiver \u2192 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0442\u043e\u043c transfer_doneif (dc.bufferedAmount &gt; 0) await waitForBufferDrain(dc, &#8230;);await waitForAck(dc, 10_000).catch(() =&gt; { \/* \u0441\u0442\u0430\u0440\u044b\u0439 receiver *\/ });dc.send(JSON.stringify({ type: &#8216;transfer_done&#8217; }));\u042d\u0442\u043e\u0442 \u043a\u043e\u0434 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u043e\u0436\u043d\u0435\u0435, \u0447\u0435\u043c \u00ab\u043d\u0430\u0440\u0435\u0437\u0430\u0442\u044c \u0444\u0430\u0439\u043b \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c\u00bb, \u0438 \u043a\u0430\u0436\u0434\u043e\u0435 \u0443\u0441\u043b\u043e\u0436\u043d\u0435\u043d\u0438\u0435 \u2013 \u0441\u043b\u0435\u0434 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u0431\u0430\u0433\u0430.\u041d\u0430\u0447\u043d\u0451\u043c \u0441 \u0434\u0432\u043e\u0439\u043d\u043e\u0433\u043e \u043f\u043e\u0440\u043e\u0433\u0430:\u00a0BUFFER_THRESHOLD\u00a0(8 MB) \u2013 \u0432\u0435\u0440\u0445\u043d\u044f\u044f \u043e\u0442\u043c\u0435\u0442\u043a\u0430, \u043f\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u043f\u0440\u0438\u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u0438\u00a0BUFFER_LOW\u00a0(6 MB) \u2013 \u043d\u0438\u0436\u043d\u044f\u044f, \u043f\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0432\u043e\u0437\u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f. \u0417\u0430\u0437\u043e\u0440 \u0432 2 MB \u0434\u0435\u0440\u0436\u0438\u0442 \u043a\u0430\u043d\u0430\u043b \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u043c, \u043d\u043e \u043d\u0435 \u0434\u0430\u0451\u0442 \u0431\u0443\u0444\u0435\u0440\u0443 \u043f\u0435\u0440\u0435\u043f\u043e\u043b\u043d\u0438\u0442\u044c\u0441\u044f. \u042d\u0442\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043f\u0430\u0442\u0442\u0435\u0440\u043d high\/low water mark.\u0414\u0430\u043b\u044c\u0448\u0435 \u2013 overlapped I\/O. \u041d\u0430\u0438\u0432\u043d\u044b\u0439 send loop \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439: \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0447\u0430\u043d\u043a \u0441 \u0434\u0438\u0441\u043a\u0430, \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c, \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439. \u041f\u043e\u043a\u0430 \u0438\u0434\u0451\u0442\u00a0file.slice().arrayBuffer(), DataChannel \u043f\u0440\u043e\u0441\u0442\u0430\u0438\u0432\u0430\u0435\u0442. Overlapped \u0432\u0435\u0440\u0441\u0438\u044f \u0447\u0438\u0442\u0430\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0447\u0430\u043d\u043a \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u0441 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u2013 \u044d\u0442\u043e \u0443\u0431\u0438\u0440\u0430\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0438 \u0437\u0430\u043c\u0435\u0442\u043d\u043e \u0443\u0441\u043a\u043e\u0440\u044f\u0435\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0443 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u0444\u0430\u0439\u043b\u043e\u0432.\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u2013 \u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u043d\u0430 drain. \u0424\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442: 5 \u0441\u0435\u043a\u0443\u043d\u0434 \u043d\u0430 \u0431\u044b\u0441\u0442\u0440\u043e\u043c P2P \u2013 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e (\u043c\u0451\u0440\u0442\u0432\u043e\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0431\u044b\u0441\u0442\u0440\u043e), \u043d\u043e 5 \u0441\u0435\u043a\u0443\u043d\u0434 \u043d\u0430 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u043c relay \u2013 \u043b\u043e\u0436\u043d\u0430\u044f \u0442\u0440\u0435\u0432\u043e\u0433\u0430 (\u0431\u0443\u0444\u0435\u0440 \u043f\u0440\u043e\u0441\u0442\u043e \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e \u0434\u0440\u0435\u043d\u0438\u0440\u0443\u0435\u0442\u0441\u044f). \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442\u0441\u044f \u0438\u0437 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u043d\u043e\u0439 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438:function computeDrainTimeout(bufferedAmount, measuredSpeed) {  const estimatedDrainMs = (bufferedAmount \/ measuredSpeed) * 1000;  return Math.max(5_000, Math.min(60_000, estimatedDrainMs * 3));}\u0411\u044b\u0441\u0442\u0440\u044b\u0439 P2P (50 MB\/s): \u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u22485 \u0441\u0435\u043a. \u041c\u0435\u0434\u043b\u0435\u043d\u043d\u044b\u0439 relay (100 KB\/s): \u0434\u043e 60 \u0441\u0435\u043a. \u041e\u0434\u043d\u0430 \u0444\u043e\u0440\u043c\u0443\u043b\u0430, \u0434\u0432\u0430 \u044d\u043a\u0441\u0442\u0440\u0435\u043c\u0443\u043c\u0430.\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f \u2013 \u0447\u0435\u0441\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441. \u041d\u0430\u0438\u0432\u043d\u044b\u0439\u00a0offset\u00a0\u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 100%, \u043a\u043e\u0433\u0434\u0430 \u043c\u0435\u0433\u0430\u0431\u0430\u0439\u0442\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0435\u0449\u0451 \u0441\u0438\u0434\u044f\u0442 \u0432 SCTP-\u0431\u0443\u0444\u0435\u0440\u0435. \u0420\u0435\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441 \u2013 \u044d\u0442\u043e\u00a0offset \u2212 dc.bufferedAmount, \u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435\u043c \u043d\u0430 99% \u0434\u043e \u043f\u043e\u043b\u043d\u043e\u0433\u043e drain. \u0418\u043d\u0430\u0447\u0435 UI \u0432\u0440\u0451\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e, \u0438 \u0438\u043c\u0435\u043d\u043d\u043e \u044d\u0442\u043e \u043c\u044b \u0443\u0432\u0438\u0434\u0435\u043b\u0438 \u0432 \u0442\u043e\u043c \u043f\u0435\u0440\u0432\u043e\u043c \u0431\u0430\u0433\u0435.\u0418 \u043d\u0430\u043a\u043e\u043d\u0435\u0446 \u2013 \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f. \u041f\u043e\u0441\u043b\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0432\u0441\u0435\u0445 \u0447\u0430\u043d\u043a\u043e\u0432 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c \u0436\u0434\u0451\u0442 \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u043e\u043f\u0443\u0441\u0442\u043e\u0448\u0435\u043d\u0438\u044f \u0431\u0443\u0444\u0435\u0440\u0430, \u0437\u0430\u0442\u0435\u043c\u00a0transfer_ack\u00a0\u043e\u0442 receiver \u2013 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0447\u0442\u043e \u0432\u0441\u0435 \u0431\u0430\u0439\u0442\u044b \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u0438 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u044b. \u0418 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u2013\u00a0transfer_done. \u042d\u0442\u043e \u043a\u0430\u043a\u00a0fwrite()\u00a0+\u00a0fsync(): \u0431\u0435\u0437 ACK \u0432\u044b \u043d\u0430\u0434\u0435\u0435\u0442\u0435\u0441\u044c, \u043d\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442\u0435.\u041d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f \u2013 \u0441\u0432\u043e\u044f \u043c\u0435\u0445\u0430\u043d\u0438\u043a\u0430.\u00a0dc.onmessage\u00a0\u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u00a0ArrayBuffer, \u043d\u043e DataChannel \u043c\u043e\u0436\u0435\u0442 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439 \u0431\u0443\u0444\u0435\u0440 \u043c\u0435\u0436\u0434\u0443 \u0432\u044b\u0437\u043e\u0432\u0430\u043c\u0438. \u0411\u0435\u0437 \u044f\u0432\u043d\u043e\u0433\u043e \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u2013 \u043f\u043e\u0432\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445:\/\/ Receiver: streaming \u0437\u0430\u043f\u0438\u0441\u044c \u043d\u0430 \u0434\u0438\u0441\u043aconst chunk = event.data.slice(0);  \/\/ \u043a\u043e\u043f\u0438\u044f \u2013 DC \u043c\u043e\u0436\u0435\u0442 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c bufferwriteChain = writeChain.then(() =&gt; writer.write(chunk));  \/\/ disk backpressureif (bytesReceived === fileInfo.size) {  dc.send(JSON.stringify({ type: &#8216;transfer_ack&#8217; }));  \/\/ sender \u0436\u0434\u0451\u0442 \u044d\u0442\u043e\u0433\u043e}writeChain\u00a0\u2013 \u0446\u0435\u043f\u043e\u0447\u043a\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u043e\u043c\u0438\u0441\u043e\u0432 \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u043d\u0430 \u0434\u0438\u0441\u043a&#8230;.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-475664","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/475664","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=475664"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/475664\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=475664"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=475664"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=475664"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}