أنشئ تطبيقات ويب فورية متعددة اللاعبين، دون الحاجة إلى خادم
جرب العرض التوضيحي ؟
يدير trystero شبكة بريد سرية تسمح لمستخدمي التطبيق الخاص بك بالتحدث مباشرة مع بعضهم البعض، بشكل مشفر وبدون وسيط خادم.
الشبكة مليئة بقنوات الاتصال المفتوحة واللامركزية: أجهزة تتبع التورنت، ووسطاء أجهزة إنترنت الأشياء، وبروتوكولات الملفات الصغيرة، والشبكات الاجتماعية المتخصصة.
يمكن استخدام trystero على هذه الشبكات لإنشاء اتصالات P2P آمنة وخاصة تلقائيًا بين مستخدمي تطبيقك دون أي جهد من جانبك.
يمكن للأقران الاتصال عبر؟ بت تورنت ؟ نستر ، ؟ MQTT أو ⚡️ Supabase أو Firebase أو ؟ IPFS - كل ذلك باستخدام نفس واجهة برمجة التطبيقات (API).
إلى جانب جعل مطابقة الأقران تلقائيًا، يقدم trystero بعض التجريدات الرائعة أعلى WebRTC:
يمكنك رؤية ما يبنيه الأشخاص باستخدام trystero هنا.
إذا كنت تريد فقط تجربة trystero ، يمكنك تخطي هذا الشرح والبدء في استخدامه.
لإنشاء اتصال مباشر من نظير إلى نظير مع WebRTC، يلزم توفر قناة إشارات لتبادل معلومات النظراء (SDP). عادةً ما يتضمن ذلك تشغيل خادم التوفيق الخاص بك، لكن trystero يلخص هذا الأمر نيابةً عنك ويقدم استراتيجيات متعددة "بدون خادم" لربط النظراء (حاليًا BitTorrent وNostr وMQTT وSupabase وFirebase وIPFS).
النقطة المهمة التي يجب تذكرها هي:
بعيدًا عن اكتشاف الأقران، لا تمس بيانات تطبيقك مطلقًا وسيط الإستراتيجية ويتم إرسالها مباشرة من نظير إلى نظير ومشفرة من طرف إلى طرف بين المستخدمين.
؟
يمكنك مقارنة الاستراتيجيات هنا.
يمكنك التثبيت باستخدام npm ( npm i trystero
) والاستيراد كما يلي:
import { joinRoom } from ' trystero '
أو ربما تفضل علامة نصية بسيطة؟ قم بتنزيل ملف JS مُعد مسبقًا من أحدث إصدار وقم باستيراده محليًا:
< script type =" module " >
import { joinRoom } from './ trystero -torrent.min.js'
</ script >
بشكل افتراضي، يتم استخدام استراتيجية Nostr. لاستخدام رمز مختلف، ما عليك سوى الاستيراد العميق مثل ذلك (يجب أن يتعامل المجمع الخاص بك مع تضمين التعليمات البرمجية ذات الصلة فقط):
import { joinRoom } from ' trystero /mqtt' // ( trystero -mqtt.min.js with a local file)
// or
import { joinRoom } from ' trystero /torrent' // ( trystero -torrent.min.js)
// or
import { joinRoom } from ' trystero /supabase' // ( trystero -supabase.min.js)
// or
import { joinRoom } from ' trystero /firebase' // ( trystero -firebase.min.js)
// or
import { joinRoom } from ' trystero /ipfs' // ( trystero -ipfs.min.js)
بعد ذلك، قم بضم المستخدم إلى غرفة باستخدام المعرف:
const config = { appId : 'san_narciso_3d' }
const room = joinRoom ( config , 'yoyodyne' )
الوسيطة الأولى هي كائن تكوين يتطلب appId
. يجب أن يكون هذا معرفًا فريدًا تمامًا لتطبيقك¹. الوسيطة الثانية هي معرف الغرفة.
لماذا الغرف؟ لا تستطيع المتصفحات التعامل إلا مع كمية محدودة من اتصالات WebRTC في المرة الواحدة، لذا يوصى بتصميم تطبيقك بحيث يتم تقسيم المستخدمين إلى مجموعات (أو غرف، أو مساحات أسماء، أو قنوات... أي شيء تريد تسميتهم).
¹ عند استخدام Firebase، يجب أن يكون appId
هو databaseURL
، وعند استخدام Supabase، يجب أن يكون عنوان URL لمشروعك.
استمع لأقرانك الذين ينضمون إلى الغرفة:
room . onPeerJoin ( peerId => console . log ( ` ${ peerId } joined` ) )
استمع لأقرانك وهم يغادرون الغرفة:
room . onPeerLeave ( peerId => console . log ( ` ${ peerId } left` ) )
استمع للأقران الذين يرسلون تدفقات الصوت/الفيديو الخاصة بهم:
room . onPeerStream (
( stream , peerId ) => ( peerElements [ peerId ] . video . srcObject = stream )
)
لإلغاء الاشتراك في الأحداث، غادر الغرفة:
room . leave ( )
يمكنك الوصول إلى معرف النظير الخاص بالمستخدم المحلي عن طريق استيراد selfId
كما يلي:
import { selfId } from ' trystero '
console . log ( `my peer ID is ${ selfId } ` )
إرسال أقرانهم دفق الفيديو الخاص بك:
room . addStream (
await navigator . mediaDevices . getUserMedia ( { audio : true , video : true } )
)
إرسال والاشتراك في إجراءات P2P المخصصة:
const [ sendDrink , getDrink ] = room . makeAction ( 'drink' )
// buy drink for a friend
sendDrink ( { drink : 'negroni' , withIce : true } , friendId )
// buy round for the house (second argument omitted)
sendDrink ( { drink : 'mezcal' , withIce : false } )
// listen for drinks sent to you
getDrink ( ( data , peerId ) =>
console . log (
`got a ${ data . drink } with ${ data . withIce ? '' : 'out' } ice from ${ peerId } `
)
)
يمكنك أيضًا استخدام الإجراءات لإرسال البيانات الثنائية، مثل الصور:
const [ sendPic , getPic ] = room . makeAction ( 'pic' )
// blobs are automatically handled, as are any form of TypedArray
canvas . toBlob ( blob => sendPic ( blob ) )
// binary data is received as raw ArrayBuffers so your handling code should
// interpret it in a way that makes sense
getPic (
( data , peerId ) => ( imgs [ peerId ] . src = URL . createObjectURL ( new Blob ( [ data ] ) ) )
)
لنفترض أننا نريد أن يتمكن المستخدمون من تسمية أنفسهم:
const idsToNames = { }
const [ sendName , getName ] = room . makeAction ( 'name' )
// tell other peers currently in the room our name
sendName ( 'Oedipa' )
// tell newcomers
room . onPeerJoin ( peerId => sendName ( 'Oedipa' , peerId ) )
// listen for peers naming themselves
getName ( ( name , peerId ) => ( idsToNames [ peerId ] = name ) )
room . onPeerLeave ( peerId =>
console . log ( ` ${ idsToNames [ peerId ] || 'a weird stranger' } left` )
)
تعتبر الإجراءات ذكية وتتعامل مع التسلسل والتقطيع خلف الكواليس. هذا يعني أنه يمكنك إرسال ملفات كبيرة جدًا وأي بيانات ترسلها سيتم استقبالها على الجانب الآخر بنفس النوع (رقم كرقم، سلسلة كسلسلة، كائن ككائن، ثنائي كثنائي، وما إلى ذلك) .
فيما يلي مثال بسيط لكيفية إنشاء غرفة دردشة صوتية:
// this object can store audio instances for later
const peerAudios = { }
// get a local audio stream from the microphone
const selfStream = await navigator . mediaDevices . getUserMedia ( {
audio : true ,
video : false
} )
// send stream to peers currently in the room
room . addStream ( selfStream )
// send stream to peers who join later
room . onPeerJoin ( peerId => room . addStream ( selfStream , peerId ) )
// handle streams from other peers
room . onPeerStream ( ( stream , peerId ) => {
// create an audio instance and set the incoming stream
const audio = new Audio ( )
audio . srcObject = stream
audio . autoplay = true
// add the audio to peerAudio object if you want to address it for something
// later (volume, etc.)
peerAudios [ peerId ] = audio
} )
القيام بنفس الشيء مع الفيديو مشابه، فقط تأكد من إضافة التدفقات الواردة إلى عناصر الفيديو في DOM:
const peerVideos = { }
const videoContainer = document . getElementById ( 'videos' )
room . onPeerStream ( ( stream , peerId ) => {
let video = peerVideos [ peerId ]
// if this peer hasn't sent a stream before, create a video element
if ( ! video ) {
video = document . createElement ( 'video' )
video . autoplay = true
// add video element to the DOM
videoContainer . appendChild ( video )
}
video . srcObject = stream
peerVideos [ peerId ] = video
} )
لنفترض أن تطبيقك يدعم إرسال أنواع مختلفة من الملفات وتريد إضافة تعليق توضيحي إلى وحدات البايت الأولية التي يتم إرسالها مع البيانات الوصفية حول كيفية تفسيرها. بدلاً من إضافة بايتات البيانات التعريفية يدويًا إلى المخزن المؤقت، يمكنك ببساطة تمرير وسيطة البيانات التعريفية في إجراء المرسل للحمولة الثنائية الخاصة بك:
const [ sendFile , getFile ] = makeAction ( 'file' )
getFile ( ( data , peerId , metadata ) =>
console . log (
`got a file ( ${ metadata . name } ) from ${ peerId } with type ${ metadata . type } ` ,
data
)
)
// to send metadata, pass a third argument
// to broadcast to the whole room, set the second peer ID argument to null
sendFile ( buffer , null , { name : 'The Courierʼs Tragedy' , type : 'application/pdf' } )
تقوم وظائف مرسل الإجراء بإرجاع وعد يتم حله عند الانتهاء من الإرسال. يمكنك اختياريًا استخدام هذا للإشارة إلى المستخدم عند إجراء عملية نقل كبيرة.
await sendFile ( amplePayload )
console . log ( 'done sending to all peers' )
تأخذ وظائف مرسل الإجراء أيضًا وظيفة رد اتصال اختيارية سيتم استدعاؤها بشكل مستمر مع تقدم عملية الإرسال. يمكن استخدام هذا لإظهار شريط التقدم للمرسل لعمليات النقل الكبيرة. يتم استدعاء رد الاتصال بقيمة مئوية بين 0 و1 ومعرف النظير المتلقي:
sendFile (
payload ,
// notice the peer target argument for any action sender can be a single peer
// ID, an array of IDs, or null (meaning send to all peers in the room)
[ peerIdA , peerIdB , peerIdC ] ,
// metadata, which can also be null if you're only interested in the
// progress handler
{ filename : 'paranoids.flac' } ,
// assuming each peer has a loading bar added to the DOM, its value is
// updated here
( percent , peerId ) => ( loadingBars [ peerId ] . value = percent )
)
وبالمثل، يمكنك الاستماع إلى أحداث التقدم كمستقبل مثل هذا:
const [ sendFile , getFile , onFileProgress ] = room . makeAction ( 'file' )
onFileProgress ( ( percent , peerId , metadata ) =>
console . log (
` ${ percent * 100 } % done receiving ${ metadata . filename } from ${ peerId } `
)
)
لاحظ أنه يتم إرسال أي بيانات وصفية مع أحداث التقدم حتى تتمكن من إظهار للمستخدم المتلقي أن هناك عملية نقل جارية ربما مع اسم الملف الوارد.
وبما أن النظير يمكنه إرسال عمليات إرسال متعددة بالتوازي، فيمكنك أيضًا استخدام بيانات التعريف للتمييز بينها، على سبيل المثال عن طريق إرسال معرف فريد.
بمجرد اتصال الأقران ببعضهم البعض، يتم تشفير جميع اتصالاتهم من طرف إلى طرف. أثناء عملية الاتصال/الاكتشاف الأولية، يتم إرسال نقاط SDP الخاصة بالأقران عبر وسيط استراتيجية النظير المختار. افتراضيًا، يتم تشفير SDP باستخدام مفتاح مشتق من معرف التطبيق الخاص بك ومعرف الغرفة لمنع ظهور بيانات جلسة النص العادي في السجلات. يعد هذا أمرًا جيدًا بالنسبة لمعظم حالات الاستخدام، ومع ذلك، يمكن لمشغل إستراتيجية الترحيل إجراء هندسة عكسية للمفتاح باستخدام معرفات الغرفة والتطبيق. الخيار الأكثر أمانًا هو تمرير معلمة password
في كائن تكوين التطبيق والتي سيتم استخدامها لاشتقاق مفتاح التشفير:
joinRoom ( { appId : 'kinneret' , password : 'MuchoMaa$' } , 'w_a_s_t_e__v_i_p' )
هذا سر مشترك يجب معرفته مسبقًا ويجب أن تتطابق كلمة المرور مع جميع أقرانهم في الغرفة حتى يتمكنوا من الاتصال. من أمثلة حالات الاستخدام غرفة محادثة خاصة حيث يتعلم المستخدمون كلمة المرور عبر وسائل خارجية.
دوال trystero غير فعالة، لذا فهي تعمل بالفعل خارج الصندوق كخطافات React.
فيما يلي مثال بسيط للمكون حيث يقوم كل نظير بمزامنة لونه المفضل مع أي شخص آخر:
import { joinRoom } from ' trystero '
import { useState } from 'react'
const trystero Config = { appId : 'thurn-und-taxis' }
export default function App ( { roomId } ) {
const room = joinRoom ( trystero Config , roomId )
const [ sendColor , getColor ] = room . makeAction ( 'color' )
const [ myColor , setMyColor ] = useState ( '#c0ffee' )
const [ peerColors , setPeerColors ] = useState ( { } )
// whenever new peers join the room, send my color to them:
room . onPeerJoin ( peer => sendColor ( myColor , peer ) )
// listen for peers sending their colors and update the state accordingly:
getColor ( ( color , peer ) =>
setPeerColors ( peerColors => ( { ... peerColors , [ peer ] : color } ) )
)
const updateColor = e => {
const { value } = e . target
// when updating my own color, broadcast it to all peers:
sendColor ( value )
setMyColor ( value )
}
return (
< >
< h1 > trystero + React </ h1 >
< h2 > My color: </ h2 >
< input type = "color" value = { myColor } onChange = { updateColor } />
< h2 > Peer colors: </ h2 >
< ul >
{ Object . entries ( peerColors ) . map ( ( [ peerId , color ] ) => (
< li key = { peerId } style = { { backgroundColor : color } } >
{ peerId } : { color }
</ li >
) ) }
</ ul >
</ >
)
}
قد يلاحظ القراء الأذكياء أن المثال أعلاه بسيط ولا يأخذ في الاعتبار ما إذا كنا نريد تغيير معرف غرفة المكون أو إلغاء تثبيته. بالنسبة لهذه السيناريوهات، يمكنك استخدام خطاف useRoom()
البسيط الذي يلغي الاشتراك في أحداث الغرفة وفقًا لذلك:
import { joinRoom } from ' trystero '
import { useEffect , useRef } from 'react'
export const useRoom = ( roomConfig , roomId ) => {
const roomRef = useRef ( joinRoom ( roomConfig , roomId ) )
const lastRoomIdRef = useRef ( roomId )
useEffect ( ( ) => {
if ( roomId !== lastRoomIdRef . current ) {
roomRef . current . leave ( )
roomRef . current = joinRoom ( roomConfig , roomId )
lastRoomIdRef . current = roomId
}
return ( ) => roomRef . current . leave ( )
} , [ roomConfig , roomId ] )
return roomRef . current
}
لاستخدام استراتيجية Supabase:
appId
في تكوين trystero ، وانسخ مفتاح واجهة برمجة التطبيقات anon public
وقم بتعيينه كمفتاح supabaseKey
في تكوين trysteroإذا كنت تريد استخدام إستراتيجية Firebase ولم يكن لديك مشروع حالي:
databaseURL
واستخدمه كمعرف appId
في تكوين trystero الخاص بك{
"rules" : {
".read" : false ,
".write" : false ,
"__ trystero __" : {
".read" : false ,
".write" : false ,
"$room_id" : {
".read" : true ,
".write" : true
}
}
}
}
تضمن هذه القواعد أن يكون حضور نظير الغرفة قابلاً للقراءة فقط إذا كانت مساحة اسم الغرفة معروفة مسبقًا.
joinRoom(config, roomId, [onError])
إضافة مستخدم محلي إلى الغرفة حيث يقوم أقران آخرون في نفس مساحة الاسم بفتح قنوات الاتصال وإرسال الأحداث. سيؤدي استدعاء joinRoom()
عدة مرات بنفس مساحة الاسم إلى إرجاع نفس مثيل الغرفة.
config
- كائن التكوين الذي يحتوي على المفاتيح التالية:
appId
- (مطلوب) سلسلة فريدة تحدد تطبيقك. عند استخدام Supabase، يجب تعيين هذا على عنوان URL لمشروعك (راجع تعليمات إعداد Supabase). إذا كنت تستخدم Firebase، فيجب أن يكون هذا هو databaseURL
من تكوين Firebase (راجع أيضًا firebaseApp
أدناه للحصول على طريقة بديلة لتكوين إستراتيجية Firebase).
password
- (اختياري) سلسلة لتشفير أوصاف الجلسة عبر AES-GCM أثناء تمريرها عبر وسيط النظير. إذا لم يتم التعيين، فسيتم تشفير أوصاف الجلسة باستخدام مفتاح مشتق من معرف التطبيق واسم الغرفة. يجب أن تتطابق كلمة المرور المخصصة بين أي أقران في الغرفة حتى يتمكنوا من الاتصال. انظر التشفير لمزيد من التفاصيل.
rtcConfig
- (اختياري) يحدد RTCConfiguration
مخصص لجميع اتصالات الأقران.
relayUrls
- (اختياري،؟ BitTorrent،؟ Nostr،؟ MQTT فقط) قائمة مخصصة لعناوين URL للإستراتيجية التي سيتم استخدامها لتمهيد اتصالات P2P. سيكون هؤلاء هم متتبعو BitTorrent، ومرحلات Nostr، ووسطاء MQTT، على التوالي. يجب أن يدعموا اتصالات WebSocket الآمنة.
relayRedundancy
- (اختياري، BitTorrent، Nostr، MQTT فقط) عدد صحيح يحدد عدد متتبعات التورنت التي سيتم الاتصال بها في وقت واحد في حالة فشل بعضها. سيؤدي تمرير خيار relayUrls
إلى تجاهل هذا الخيار حيث سيتم استخدام القائمة بأكملها.
supabaseKey
- (مطلوب، ⚡️ Supabase فقط) مفتاح anon public
لمشروع Supabase الخاص بك.
firebaseApp
- (اختياري، Firebase فقط) يمكنك تمرير مثيل تطبيق Firebase الذي تمت تهيئته بالفعل بدلاً من appId
. عادةً ما يقوم trystero بتهيئة تطبيق Firebase استنادًا إلى appId
ولكن هذا سيفشل إذا قمت بالفعل بتهيئته للاستخدام في مكان آخر.
rootPath
- (اختياري، Firebase فقط) سلسلة تحدد المسار حيث يكتب trystero بيانات التوفيق الخاصة به في قاعدة البيانات الخاصة بك ( '__ trystero __'
بشكل افتراضي). يعد تغيير هذا مفيدًا إذا كنت تريد تشغيل تطبيقات متعددة باستخدام نفس قاعدة البيانات ولا تريد القلق بشأن تضارب مساحة الاسم.
libp2pConfig
- (اختياري، IPFS فقط) Libp2pOptions
حيث يمكنك تحديد قائمة من النظراء الثابتين للتمهيد.
roomId
- سلسلة لنظيرات مساحة الاسم والأحداث داخل الغرفة.
onError(details)
- (اختياري) وظيفة رد اتصال سيتم استدعاؤها إذا تعذر الانضمام إلى الغرفة بسبب كلمة مرور غير صحيحة. details
عبارة عن كائن يحتوي على appId
و roomId
و peerId
error
في وصف الخطأ.
إرجاع كائن بالطرق التالية:
leave()
إزالة المستخدم المحلي من الغرفة وإلغاء الاشتراك في أحداث الغرفة.
getPeers()
إرجاع خريطة RTCPeerConnection
s للأقران الموجودين في الغرفة (لا يشمل المستخدم المحلي). مفاتيح هذا الكائن هي معرفات الأقران المعنية.
addStream(stream, [targetPeers], [metadata])
بث دفق الوسائط إلى أقرانهم الآخرين.
stream
- MediaStream
مع الصوت و/أو الفيديو لإرسالها إلى أقرانهم في الغرفة.
targetPeers
- (اختياري) إذا تم تحديده، فسيتم إرسال الدفق فقط إلى معرف النظير الهدف (سلسلة) أو قائمة معرفات النظير (مصفوفة).
metadata
- (اختياري) بيانات تعريف إضافية (أي نوع قابل للتسلسل) ليتم إرسالها مع الدفق. يعد هذا مفيدًا عند إرسال تدفقات متعددة حتى يعرف المستلمون أي منها (على سبيل المثال، كاميرا الويب مقابل لقطة الشاشة). إذا كنت تريد بث دفق إلى جميع النظراء في الغرفة باستخدام وسيطة بيانات التعريف، فقم بتمرير null
كالوسيطة الثانية.
removeStream(stream, [targetPeers])
توقف عن إرسال دفق الوسائط الذي تم إرساله مسبقًا إلى أقرانهم الآخرين.
stream
- MediaStream
تم إرساله مسبقًا لإيقاف الإرسال.
targetPeers
- (اختياري) إذا تم تحديده، تتم إزالة الدفق فقط من معرف النظير الهدف (سلسلة) أو قائمة معرفات النظير (مصفوفة).
addTrack(track, stream, [targetPeers], [metadata])
يضيف مسار وسائط جديد إلى الدفق.
track
- MediaStreamTrack
لإضافته إلى التدفق الموجود.
stream
- MediaStream
الهدف الذي سيتم إرفاق المسار الجديد به.
targetPeers
- (اختياري) إذا تم تحديده، فسيتم إرسال المسار فقط إلى معرف النظير المستهدف (سلسلة) أو قائمة معرفات النظير (مصفوفة).
metadata
- (اختياري) البيانات الوصفية الإضافية (أي نوع قابل للتسلسل) ليتم إرسالها مع المسار. راجع ملاحظات metadata
لـ addStream()
أعلاه لمزيد من التفاصيل.
removeTrack(track, stream, [targetPeers])
إزالة مسار الوسائط من الدفق.
track
- MediaStreamTrack
المراد إزالته.
stream
- MediaStream
المسار المرفق به.
targetPeers
- (اختياري) إذا تم تحديده، تتم إزالة المسار فقط من معرف النظير الهدف (سلسلة) أو قائمة معرفات النظير (مصفوفة).
replaceTrack(oldTrack, newTrack, stream, [targetPeers])
يستبدل مسار الوسائط بآخر جديد.
oldTrack
- MediaStreamTrack
المطلوب إزالته.
newTrack
- MediaStreamTrack
لإرفاقه.
stream
- MediaStream
المتصل به oldTrack
.
targetPeers
- (اختياري) إذا تم تحديده، فسيتم استبدال المسار فقط بمعرف النظير الهدف (سلسلة) أو قائمة معرفات النظير (مصفوفة).
onPeerJoin(callback)
يسجل وظيفة رد الاتصال التي سيتم استدعاؤها عندما ينضم أحد الأقران إلى الغرفة. إذا تم الاتصال أكثر من مرة، فسيتم استدعاء آخر رد اتصال مسجل فقط.
callback(peerId)
- وظيفة يتم تشغيلها عند انضمام أحد الزملاء، ويتم استدعاؤها باستخدام معرف النظير.مثال:
onPeerJoin ( peerId => console . log ( ` ${ peerId } joined` ) )
onPeerLeave(callback)
يسجل وظيفة رد الاتصال التي سيتم استدعاؤها عندما يغادر أحد الزملاء الغرفة. إذا تم الاتصال أكثر من مرة، فسيتم استدعاء آخر رد اتصال مسجل فقط.
callback(peerId)
- وظيفة يتم تشغيلها عند مغادرة أحد الأقران، ويتم الاتصال بها باستخدام معرف النظير.مثال:
onPeerLeave ( peerId => console . log ( ` ${ peerId } left` ) )
onPeerStream(callback)
يسجل وظيفة رد الاتصال التي سيتم استدعاؤها عندما يرسل أحد الأقران دفق وسائط. إذا تم الاتصال أكثر من مرة، فسيتم استدعاء آخر رد اتصال مسجل فقط.
callback(stream, peerId, metadata)
- وظيفة يتم تشغيلها عندما يرسل أحد الزملاء دفق وسائط، يتم استدعاؤه باستخدام دفق النظير ومعرفه وبيانات التعريف الاختيارية (راجع addStream()
أعلاه للحصول على التفاصيل).مثال:
onPeerStream ( ( stream , peerId ) =>
console . log ( `got stream from ${ peerId } ` , stream )
)
onPeerTrack(callback)
يسجل وظيفة رد الاتصال التي سيتم استدعاؤها عندما يرسل أحد الأقران مسار وسائط. إذا تم الاتصال أكثر من مرة، فسيتم استدعاء آخر رد اتصال مسجل فقط.
callback(track, stream, peerId, metadata)
- وظيفة يتم تشغيلها عندما يرسل أحد الزملاء مسار وسائط، يتم استدعاؤه باستخدام مسار النظير، والدفق المرفق، والمعرف، وبيانات التعريف الاختيارية (راجع addTrack()
أعلاه للحصول على التفاصيل).مثال:
onPeerTrack ( ( track , stream , peerId ) =>
console . log ( `got track from ${ peerId } ` , track )
)
makeAction(actionId)
الاستماع إلى إجراءات البيانات المخصصة وإرسالها.
actionId
- سلسلة لتسجيل هذا الإجراء بشكل متسق بين جميع أقرانه.إرجاع مجموعة من ثلاث وظائف:
يرسل البيانات إلى الأقران ويعيد وعدًا يتم حله عندما ينتهي جميع الأقران المستهدفين من تلقي البيانات.
(data, [targetPeers], [metadata], [onProgress])
data
- أي قيمة لإرسالها (بدائية، كائن، ثنائي). تتم معالجة التسلسل والتقطيع تلقائيًا. يتم تلقي البيانات الثنائية (مثل Blob
و TypedArray
) من قبل نظير آخر باعتبارها ArrayBuffer
ملحدة.
targetPeers
- (اختياري) إما معرف نظير (سلسلة)، أو مجموعة من معرفات النظير، أو null
(تشير إلى الإرسال إلى جميع النظراء في الغرفة).
metadata
- (اختياري) إذا كانت البيانات ثنائية، فيمكنك إرسال كائن بيانات تعريف اختياري يصفها (راجع البيانات التعريفية الثنائية).
onProgress
- (اختياري) وظيفة رد اتصال سيتم استدعاؤها عند إرسال كل قطعة لكل نظير. سيتم استدعاء الدالة بقيمة بين 0 و1 ومعرف نظير. راجع تحديثات التقدم للحصول على مثال.
يسجل وظيفة رد الاتصال التي يتم تشغيلها عند تلقي بيانات هذا الإجراء من أقران آخرين.
(data, peerId, metadata)
data
- القيمة التي يرسلها النظير المرسل. تتم معالجة إلغاء التسلسل تلقائيًا، أي سيتم استلام الرقم كرقم، والكائن ككائن، وما إلى ذلك.
peerId
- سلسلة معرف النظير المرسل.
metadata
- (اختياري) كائن بيانات تعريف اختياري يوفره المرسل إذا كانت data
ثنائية، على سبيل المثال اسم ملف.
يسجل وظيفة رد الاتصال التي يتم تشغيلها عند تلقي بيانات جزئية من النظراء. يمكنك استخدام هذا لتتبع عمليات النقل الثنائية الكبيرة. راجع تحديثات التقدم للحصول على مثال.
(percent, peerId, metadata)
percent
- رقم بين 0 و1 يشير إلى النسبة المئوية لاكتمال عملية النقل.
peerId
- سلسلة معرف النظير المرسل.
metadata
- (اختياري) كائن بيانات التعريف الاختياري الذي يوفره المرسل.
مثال:
const [ sendCursor , getCursor ] = room . makeAction ( 'cursormove' )
window . addEventListener ( 'mousemove' , e => sendCursor ( [ e . clientX , e . clientY ] ) )
getCursor ( ( [ x , y ] , peerId ) => {
const peerCursor = cursorMap [ peerId ]
peerCursor . style . left = x + 'px'
peerCursor . style . top = y + 'px'
} )
ping(peerId)
يأخذ معرف النظير ويعيد وعدًا يتم حله بالمللي ثانية التي استغرقتها رحلة الذهاب والإياب إلى ذلك النظير. استخدم هذا لقياس الكمون.
peerId
- سلسلة معرف النظير للنظير المستهدف.مثال:
// log round-trip time every 2 seconds
room . onPeerJoin ( peerId =>
setInterval (
async ( ) => console . log ( `took ${ await room . ping ( peerId ) } ms` ) ,
2000
)
)
selfId
سلسلة معرفات فريدة سيعرفها أقرانهم الآخرون على المستخدم المحلي عالميًا عبر الغرف.
getRelaySockets()
(؟ BitTorrent،؟ Nostr،؟ MQTT فقط) يُرجع كائنًا من مفاتيح URL للترحيل المعينة لاتصالات WebSocket الخاصة بهم. يمكن أن يكون هذا مفيدًا لتحديد حالة اتصال المستخدم بالمرحلات ومعالجة أي فشل في الاتصال.
مثال:
console . log ( trystero . getRelaySockets ( ) )
// => Object {
// "wss://tracker.webtorrent.dev": WebSocket,
// "wss://tracker.openwebtorrent.com": WebSocket
// }
getOccupants(config, roomId)
(Firebase فقط) يُرجع وعدًا يتم حله في قائمة معرفات المستخدمين الموجودة في مساحة الاسم المحددة. يعد هذا مفيدًا للتحقق من عدد المستخدمين الموجودين في الغرفة دون الانضمام إليها.
config
- كائن التكوينroomId
- سلسلة مساحة الاسم التي يمكنك تمريرها إلى joinRoom()
.مثال:
console . log ( ( await trystero . getOccupants ( config , 'the_scope' ) ) . length )
// => 3
الإعداد لمرة واحدة¹ | حجم الحزمة² | الوقت للاتصال³ | |
---|---|---|---|
؟ Nostr | لا أحد ؟ | 54 ك | ⏱️⏱️ |
؟ إم كيو تي تي | لا أحد ؟ | 332 ألف | ⏱️⏱️ |
؟ بت تورنت | لا أحد ؟ | 25 ألف؟ | ⏱️⏱️ |
⚡️ سوبابيس | ~5 دقائق | 150 ألف | ⏱️ ؟ |
Firebase | ~5 دقائق | 177 ألف | ⏱️ ؟ |
؟ إيبفس | لا أحد ؟ | 945 ألف | ⏱️⏱️ |
¹ جميع الاستراتيجيات باستثناء Firebase لا تتطلب أي إعداد. Firebase عبارة عن إستراتيجية مُدارة تتطلب إعداد حساب.
² يتم حسابه من خلال التجميع المجمع + الضغط المختصر.
³ السرعة النسبية لاتصال الأقران ببعضهم البعض عند الانضمام إلى الغرفة. يعد Firebase شبه فوري بينما تكون الاستراتيجيات الأخرى أبطأ قليلاً في تبادل معلومات النظير.
الميزة الفريدة لـ trystero هي أنه لا يتطلب أي إعداد خلفي ويستخدم بنية تحتية لا مركزية في معظم الحالات. وهذا يسمح بإجراء تجارب خالية من الاحتكاك وعدم وجود نقطة فشل واحدة. أحد العوائق المحتملة هو أنه من الصعب ضمان أن البنية التحتية العامة التي تستخدمها ستكون دائمًا متاحة بشكل كبير، حتى مع تقنيات التكرار التي تستخدمها trystero . في حين أن الاستراتيجيات الأخرى لا مركزية، فإن استراتيجيات Supabase وFirebase هي نهج أكثر إدارة مع تحكم أكبر واتفاقية مستوى الخدمة، والتي قد تكون أكثر ملاءمة لتطبيقات "الإنتاج".
يجعل trystero التبديل بين الاستراتيجيات أمراً تافهاً - ما عليك سوى تغيير سطر استيراد واحد والتجربة بسرعة:
import { joinRoom } from ' trystero /[torrent|nostr|mqtt|supabase|firebase|ipfs]'
trystero بواسطة دان موتزنبيكر