வலைப்பதிவுக்குத் திரும்பு
Architecture12 min read

பியர் & சேனல் அரட்டைக் கட்டமைப்பு

PlainApp-ன் ஆஃப்லைன்-முதல் அரட்டை முழுமையாக எப்படி வேலை செய்கிறது என்பதை இக்கட்டுரை விளக்குகிறது: ஒரு செய்தி UI-இல் ஒரு தட்டலிலிருந்து பியர் போக்குவரத்து வழியாக மற்றொரு சாதனத்திற்கு எப்படிப் பயணிக்கிறது, குழுச் சேனல்கள் பல உறுப்பினர்களுக்குச் செய்திகளை எப்படி fan-out செய்கின்றன, மற்றும் நெட்வொர்க்குகள் மறையும்போது அமைப்பு எப்படிப் பின்னடைவாக இருக்கிறது. பெயரிங் (இரண்டு சாதனங்களை bootstrap செய்யும் நம்பிக்கை மற்றும் விசைப் பரிமாற்றம்) தனி பெயரிங் பாய்வுக் கட்டுரையில் கொடுக்கப்பட்டுள்ளது.

பொருளடக்கம்

உயர்-நிலைக் கட்டமைப்பு {#high-level-architecture}

PlainApp அரட்டை சேவையகமற்றது. ஒவ்வொரு சாதனமும் ஒரு உட்பொதிக்கப்பட்ட Ktor HTTP சேவையகத்தை இயக்குகிறது, மற்றும் சாதனங்கள் உள்ளக நெட்வொர்க், Wi-Fi Aware (NAN), அல்லது Bluetooth Low Energy வழியாக நேரடியாக ஒன்றையொன்று பேசுகின்றன. ரிலே சேவையகம் இல்லை, கிளவுட் இன்பாக்ஸ் இல்லை, தொலைபேசி-எண்-அடிப்படையிலான அடையாளம் இல்லை. சாதனங்கள் ஒரு சுயமாக உருவாக்கப்பட்ட clientId-ஆல் அடையாளம் காணப்படுகின்றன, மற்றும் பெயரிங்-ன்போது செய்யப்பட்ட Ed25519 + ECDH handshake வழியாக அங்கீகரிக்கப்படுகின்றன.

இரண்டு வகையான உரையாடல்கள் உள்ளன:

வகைமாறிலிவிளக்கம்
PEERChatTargetType.PEERஇரண்டு பெயர் செய்த சாதனங்களுக்கிடையே 1-க்கு-1 நேரடி அரட்டை.
CHANNELChatTargetType.CHANNELஒரு சாதனத்திற்குச் சொந்தமான பல-தரப்பு குழு அரட்டை; உறுப்பினர்கள் ஒருவருக்கொருவர் செய்திகளை fan-out செய்கிறார்கள்.

ஒரு சிறப்பு "local" இலக்கு சாதனத்தின் சொந்த scratchpad (தனக்குத்தானே குறிப்புகள்) — அதற்கு அனுப்புவது wire-இல் no-op.

கூறு வரைபடம்

Diagram 1
1

கட்டமைப்பு வேண்டுமென்றே அடுக்கப்பட்டது:

  1. UI / GraphQL நுழைவுப் புள்ளிகள் போக்குவரத்து அல்லது DB-ஐ நேரடியாகத் தொடுவதில்லை.
  2. ChatManager ஒரு façade — ஒவ்வொரு அழைப்பவரும் (UI, GraphQL resolver, பியர் பெறுநர்) இதன் வழியாகச் செல்கிறார்கள்.
  3. ChatSender ஒரு dispatcher, ChatTargetType-ஐ மாற்றி பியர் அல்லது சேனல் அனுப்புநர்களுக்குப் பிரித்துவிடுகிறது.
  4. போக்குவரத்து அடுக்கு சுற்று-உடைப்புடன் ஒரு செருகக்கூடிய உத்திசாலைச் சங்கிலி, எனவே ஒரு நம்பமுடியாத Wi-Fi Aware இணைப்பு BLE வழியாகச் செல்லக்கூடிய ஒரு செய்தியை ஒருபோதும் தடுப்பதில்லை.

தரவு மாதிரி {#data-model}

ChatTarget

ரூட்டிங்கின் மிகச் சிறிய அலகு ஒரு ChatTarget — ஒரு (toId, type) ஜோடி, இங்கு type என்பது PEER அல்லது CHANNEL. இது ஒரு encodedToId-ஐ (peer:<id> அல்லது channel:<id>) வெளிப்படுத்துகிறது, இதை UI ஒரு நிலையான ரூட்டிங் விசையாகப் பயன்படுத்துகிறது (உ.ம். TempData.activeToId, இதனால் பெறுநர் அறிவிப்பை வெளியிட வேண்டுமா என்பதைத் தெரிந்துகொள்கிறார்), ஒரு isLocal() சரிபார்ப்பு (toId == "local"), மற்றும் சேமிக்கப்பட்ட சரத்திலிருந்து இலக்கை மீண்டும் கட்டமைக்கும் ஒரு parseId companion.

தரவுத்தள அட்டவணைகள்

எல்லா நிலைத்தன்மையும் Room-ஐப் பயன்படுத்துகிறது. அரட்டைக்கு மூன்று அட்டவணைகள் முக்கியம்:

அட்டவணைEntityநோக்கம்
chatsDChatஒரு செய்திக்கு ஒரு வரிசை (உரை / படம் / கோப்பு).
chat_channelsDChatChannelஒரு குழு சேனலுக்கு ஒரு வரிசை.
peersDPeerஒரு அறியப்பட்ட சாதனத்திற்கு ஒரு வரிசை (பெயர் செய்த அல்லது சேனல்-மட்டும்).

Diagram 2
2

கவனிக்கத்தக்க சில விஷயங்கள்:

  • அடையாளம் clientId, MAC ஒருபோதும் இல்லை. Android ஒவ்வொரு இணைப்பிலும் BLE MAC-ஐ சீரற்றதாக்குகிறது, எனவே தரவுத்தளம் ஒரு நிலையான 13-எழுத்து சுய-உருவாக்க id-ஐப் பயன்படுத்துகிறது. 8-பைட் SHA-256 முன்னொட்டம் (shortId) மட்டுமே கண்டுபிடிப்புக்காக BLE வழியாக ஒளிபரப்பப்படுகிறது.
  • status="channel" peers இந்தச் சாதனம் ஒருபோதும் நேரடியாகப் பெயர் செய்யாத ஒரு சேனலின் உறுப்பினர்கள். அவர்களின் key வெறுமை — அவர்கள் ஜோடிவாரிப் பகிரப்பட்ட விசைக்குப் பதிலாக சேனல் விசையைப் பயன்படுத்தி அங்கீகரிக்கிறார்கள்.
  • owner="me" ஒரு புதிதாக நிறுவப்பட்ட சாதனம் அதன் clientId நிலையானதாகும் முன்பு உரிமையாளராகச் செயல்பட அனுமதிக்கும் ஒரு sentinel; isOwnedByMe() "me" மற்றும் TempData.clientId இரண்டையும் ஏற்கிறது.

GraphQL API மேற்பரப்பு {#graphql-api-surface}

PlainApp இரண்டு GraphQL schemas-ஐ வெளிப்படுத்துகிறது:

  1. Web GraphQL (addChatChannelSchema + addChatMessageSchema) — உள்ளக Ktor சேவையகத்தால் browser UI மற்றும் apitest/ harness-இக்கு வழங்கப்படுகிறது. ChaCha20-ஆல் மறைகுறியாக்கப்பட்ட token-ஆல் அங்கீகரிக்கப்படுகிறது.
  2. Peer GraphQL (PeerGraphQLService.applyPeerSchema) — மறைகுறியாக்கப்பட்ட பியர் போக்குவரத்து வழியாக மற்ற சாதனங்களுக்கு /peer_graphql-இல் வெளிப்படுத்தப்படுகிறது. Ed25519 கையொப்பம் + ChaCha20 உடல் மறைகுறியாக்கத்தால் அங்கீகரிக்கப்படுகிறது.

இரண்டு schemas-உம் அதே வணிக தர்க்க singletons-ஐ (ChannelManager, ChatMessageReceiver, …) பகிர்ந்துகொள்கின்றன, ஆனால் வெவ்வேறு மேற்பரப்புகளை வெளிப்படுத்துகின்றன, ஏனெனில் நம்பிக்கை மாதிரி வேறு: web GraphQL உள்ளக UI-ஐ நம்புகிறது, அதேசமயம் peer GraphQL மறைகுறியாக்க-ரீதியாக அங்கீகரிக்கப்பட்ட பியர்களை மட்டுமே நம்புகிறது.

Web GraphQL மேற்பரப்பு (அரட்டை)

Queries: chatChannels (எல்லா சேனல்களையும் பட்டியலிடு), chatItems(id) (ஒரு இலக்கிற்கான செய்திகள் — id என்பது "local", peer:<id>, அல்லது channel:<id>), மற்றும் latestChatItems (எல்லா அரட்டைகளிலும் முன்னோட்டம்).

அரட்டை mutations: sendChatItem(toId, content), deleteChatItem(id), deleteChatItems(query), மற்றும் retryChatItem(id).

சேனல் mutations: createChatChannel(name), updateChatChannel(id, name), deleteChatChannel(id), leaveChatChannel(id), addChatChannelMember(id, peerId), removeChatChannelMember(id, peerId), acceptChatChannelInvite(id), மற்றும் declineChatChannelInvite(id).

Peer GraphQL மேற்பரப்பு (போக்குவரத்து)

/peer_graphql-இல் வெளிப்படுத்தப்பட்டு Ed25519 கையொப்பம் + ChaCha20 உடல் மறைகுறியாக்கத்தால் அங்கீகரிக்கப்படுகிறது. மூன்று mutations மட்டுமே போக்குவரத்து எல்லையைக் கடக்கின்றன: createChatItem(content) (உள்வரும் பியர் செய்தி), channelSystemMessage(type, payload) (invite/leave போன்ற சேனல் வாழ்க்கைச்சுழற்சி நிகழ்வுகள்), மற்றும் startAware (ஒரு விரைவான போக்குவரத்து ஏற்றுக்கொள்ள பியர் அதன் Wi-Fi Aware சேவையைத் தொடங்கக் கேட்கும் ஒரு தூண்டல்).

c-id HTTP தலைப்பு அனுப்புநரின் clientId-ஐக் கொண்டு செல்கிறது; c-cid தலைப்பு கோரிக்கை சேனல்-வாய்ப்பாட்டில் இருக்கும்போது ஒரு சேனல் id-ஐக் கொண்டு செல்கிறது (இதனால் பெறுநர் மறைநீக்கத்திற்கு ஜோடிவாரிப் பியர் விசைக்குப் பதிலாக சேனல் விசையை எடுக்கிறார்).

பியர் அரட்டை: ஒரு செய்தியை அனுப்புதல் {#peer-chat-sending-a-message}

பயனர் ஒரு பியர் உரையாடலில் அனுப்பு என்பதைத் தட்டும்போது, அழைப்புச் சங்கிலி:

Diagram 3
3

ஒவ்வொரு பாய்ச்சலிலும் செயல்படுத்தப்படும் முக்கிய மாறாத நிலைகள்:

  1. ChatManager.createChatItem எப்போதும் முதலில் ஒரு வரிசையை செருகுகிறது, பிறகு அனுப்புகிறது. இதன் பொருள் UI உடனடியாக ஒரு "நிலுவையில்" பலகையைக் காண்கிறது, மேலும் வழங்கல் நிகழாவிட்டாலும் கூட செய்தி செயலி செயலிழப்புகளில் உயிர்வாழ்கிறது.
  2. PeerGraphQLClient.buildSignedRequest signature|timestamp|requestJson வடிவில் ஒரு உறையை உருவாக்குகிறது. கையொப்பம் "$timestamp$requestJson"-இல் Ed25519-ஆல் இடப்படுகிறது, நேரமுத்திரையை உடலுடன் பிணைத்து, அது புதிய நேரமுத்திரையுடன் மறுபயன்பாடு செய்ய முடியாது.
  3. PeerTransportRouter.send போக்குவரத்துகளை வரிசையாக Lan → WifiAware → Ble மறுசெய்கிறது. ஒவ்வொரு போக்குவரத்தும் ரூட்டர் அடுத்ததை முயற்சிக்க அனுமதிக்க TransportUnavailable-ஐ வீசலாம்.
  4. பெறும் பக்கத்தில், PeerChatParser.decrypt நேரமுத்திரை ±5 நிமிடம் உள்ளே இருப்பதைச் சரிபார்த்து, GraphQL mutation செயல்படுத்தப்படுவதற்கு முன்பே Ed25519 கையொப்பத்தைச் சரிபார்க்கிறது.
  5. ChatMessageReceiver.receive "$fromPeerId|$signature|$timestamp"-ஆல் திறமுடைய ஒரு seenSignatures தொகுப்பை வைத்திருக்கிறது, நகல்களில் ReplayedMessageException-ஐ வீசுகிறது — போக்குவரத்து அதே சுமையை இரண்டு முறை (LAN + BLE) வழங்கலாம் என்பதால் இது அவசியம்.

PeerChatSender.send null அல்லாத பிழைச் சரத்தைத் திருப்பி அனுப்பினால், ChatSendertriggerPeerRediscovery(peerId)-ஐ அழைக்கிறது, இது பியர் அதன் தற்போதைய IP/port-ஐ மறு-அறிவிக்க ஒரு இயக்கப்பட்ட, மறைகுறியாக்கப்பட்ட DISCOVER ஒளிபரப்பை இயக்குகிறது.

பியர் அரட்டை: ஒரு செய்தியைப் பெறுதல் {#peer-chat-receiving-a-message}

உள்வரும் கோரிக்கைகள் உள்ளக Ktor சேவையகத்தின் /peer_graphql ரூட்டில் வந்திறங்குகின்றன, PeerGraphQLService-ஆல் கையாளப்படுகின்றன:

Diagram 4
4

அறிவிப்புகள்

emitNotificationIfNeeded இறுதி படி. TempData.activeToId == targetId (அதாவது பயனர் தற்போது அந்த உரையாடலைப் பார்த்துக்கொண்டிருக்கிறார்) அல்லது canShowNotifications() false ஆக இருக்கும்போது அறிவிப்பை அடக்குகிறது. சேனல் அறிவிப்புகள் அனுப்புநரின் பெயருடன் முன்னொட்டப்படுகின்றன.

சேனல் அரட்டை: தலைவர் தேர்தல் & Fan-Out {#channel-chat-leader-election--fan-out}

சேனல்கள் பல-தரப்பு ஆனால் சேவையகமற்றவை. ஒவ்வொரு உறுப்பினரும் அதே செய்தியை N முறை fan-out செய்வதைத் தவிர்க்க, அனுப்புநர் பக்கம் ஒரு ஒற்றை தலைவரைத் தேர்ந்தெடுக்கிறது, அவருடைய வேலை சேர்ந்த அனைத்து உறுப்பினர்களுக்கும் ஒளிபரப்புவது.

தலைவர் தேர்தல் அல்காரிதம் (DChatChannel.electLeader)

  1. தற்போது ஆன்லைனில் இருக்கும் சேர்ந்த உறுப்பினர்களுக்கு வடிகட்டு (உள்ளக சாதனம் எப்போதும் ஆன்லைன் எனக் கருதப்படுகிறது).
  2. உரிமையாளர் ஆன்லைனில் உள்ள சேர்ந்த உறுப்பினர்களில் இருந்தால் → உரிமையாளரே தலைவர்.
  3. இல்லையெனில், தலைவர் சிறிய clientId உள்ள ஆன்லைன் சேர்ந்த உறுப்பினர் (நிர்ணய tiebreak, ஒருங்கிணைப்பு தேவையில்லை).
  4. ஆன்லைன் சேர்ந்த உறுப்பினர்கள் இல்லை எனில் null-ஐத் திருப்பி அனுப்புகிறது.

அனுப்பல் பாய்வு

Diagram 5
5

தலைவர் ஏன் தேவை?

ஒரு 5-உறுப்பினர் சேனலை எண்ணுங்கள், அங்கு அனைவரும் மற்ற அனைவருக்கும் ஒளிபரப்புகிறார்கள்: ஒரு செய்தி 20 நெட்வொர்க் ரவுண்ட் ட்ரிப்களையும் ஒவ்வொரு உறுப்பினருக்கும் 4 நகல் பிரதிகளையும் உருவாக்கும். ஒரு தலைவரைத் தேர்ந்தெடுப்பதன் மூலம், அந்தச் சாதனம் மட்டுமே fan-out செய்கிறது — அனுப்புநர் fan-out-ஐத் தானே செய்கிறார் (அவர் தலைவராக இருந்தால்) அல்லது தலைவருக்கு ஒரு நகலை ரிலே செய்கிறார், அது பின்னர் fan-out செய்கிறது.

தலைவர் ஆஃப்லைன் இருந்தால், அனுப்புநர் Result.NoLeader-க்கு fallback செய்கிறார், பியர் மறு-கண்டுபிடிப்பை இயக்குகிறார் (இதனால் தலைவரின் IP கண்டுபிடிக்கப்படலாம்), மற்றும் பயனர் மறுமுயற்சி செய்ய அனுமதிக்க நிலையைத் துடைக்கிறார்.

சேனல் விசை ரூட்டிங்

சேனல் செய்திகள் ஜோடிவாரிப் பியர் விசைக்குப் பதிலாக சேனலின் ChaCha20 விசையுடன் மறைகுறியாக்கப்படுகின்றன. இதுதான் ஒரு உறுப்பினர் மற்ற உறுப்பினர்களை சேனல் வழியாக மட்டுமே சந்தித்தவராக (ஒருபோதும் 1-க்கு-1 பெயர் செய்யாதவராக) செய்திகளைப் பெற அனுமதிக்கிறது — அவர்களின் peers வரிசை status="channel" மற்றும் key="" கொண்டிருக்கும். அனுப்புநர் c-cid HTTP தலைப்பை சேனல் id-ஆக அமைக்கிறார்; பெறுநர் ஜோடிவாரி விசைக்குப் பதிலாக ChannelCacher.getKeyBytes(channelId)-ஐத் தேடுகிறார்.

ஒரு-பெறுநர் மறுமுயற்சி

ஒவ்வொரு sendToMember-உம் ஒரு DMessageDeliveryResult-ஐத் திருப்பி அனுப்புகிறது. திரட்டப்பட்ட DMessageStatusData அரட்டை உருப்படியின் status_data JSON-ஆக நிலைத்திருக்கச் செய்யப்படுகிறது. UI "Alice, Bob-க்கு வழங்கப்பட்டது; Carol-க்கு தோல்வி" எனக் காட்டுகிறது, பயனர் Carol-க்கு குறிப்பாக மறுமுயற்சி என்பதைத் தட்ட அனுமதிக்கிறது — ChatManager.sendToChannelMembers மறுமுயற்சி துணைக்குறியீட்டிற்கு sendToRecipients-ஐ மறு-இயக்கம் செய்து, புதிய முடிவுகளை ஏற்கனவே உள்ளவற்றுடன் இணைக்கிறது, மறுமுயற்சி செய்யப்பட்ட பியர்களை மட்டுமே மாற்றுகிறது.

சேனல் கட்டளை செய்திகள் {#channel-system-messages}

சேனல் கட்டுப்பாட்டு-விமான செய்திகள் (invite, accept, decline, update, kick, leave) பியர் GraphQL channelSystemMessage mutation வழியாகப் பரிமாறப்படுகின்றன. அவை ஒரு type சரத்தால் தட்டச்சு செய்யப்பட்ட JSON சுமைகள்:

வகைதிசைகையொப்பம்?நோக்கம்
channel_inviteஉரிமையாளர் → அழைக்கப்பட்டவர்ஆம்ஒரு பியரை அழை; சேனல் விசை + உறுப்பினர்களைக் கொண்டு செல்.
channel_invite_acceptஅழைக்கப்பட்டவர் → உரிமையாளர்இல்லைஏற்பு; ஏற்பவரின் பொது விசையைக் கொண்டு செல்.
channel_invite_declineஅழைக்கப்பட்டவர் → உரிமையாளர்இல்லைநிராகரி; உரிமையாளர் உறுப்பினரை நீக்குகிறார்.
channel_updateஉரிமையாளர் → எல்லா உறுப்பினர்கள்ஆம்உறுப்பினர்/பெயர் மாற்ற ஒளிபரப்பு.
channel_kickஉரிமையாளர் → நீக்கப்பட்ட பியர்ஆம்இலக்கு நீக்கம்; சேனல் நீக்கத்திலும் ஒளிபரப்பு.
channel_leaveஉறுப்பினர் → உரிமையாளர்இல்லைஉறுப்பினர்-தொடங்கிய விலகல் அறிவிப்பு.

கையொப்பமிடப்பட்ட சுமை வடிவம்

மூன்று கையொப்ப வகைகளும் (invite, update, kick) ஒரு நியம pipe-பிரிக்கப்பட்ட சரத்தைப் பயன்படுத்துகின்றன: "$channelId|$version|$action|$target", இங்கு action என்பது invite, update, kick-இல் ஒன்று, மற்றும் target என்பது அழைக்கப்பட்ட/நீக்கப்பட்ட பியர் id (ஒளிபரப்பு kick-க்கு வெறுமை).

உரிமையாளர் இந்தச் சரத்தை அவரது Ed25519 விசையுடன் கையொப்பமிடுகிறார். பெறுநர்கள் channel.owner != fromId என்ற எந்தச் செய்தியையும் கையொப்பத்தைச் சரிபார்ப்பதற்கு முன்பே நிராகரிக்கிறார்கள், மேலும் version உள்ளக பதிப்பை விட ≤ என்ற ChannelUpdate சுமைகளை நிராகரிக்கிறார்கள் (வரிசை-வெளியே-விநியோகத்திற்கு எதிரான பழைய-பதிப்பு பாதுகாப்பு).

Diagram 6
6

சோம்பல் பியர் hydration

ChannelInvite மற்றும் ChannelUpdate ஒரு memberPeers: List<MemberPeerInfo> பட்டியலைக் கொண்டு செல்கின்றன — ஒவ்வொரு உறுப்பினருக்கும் இலகுரக பியர் தகவல் (id, name, publicKey, deviceType, ip, port). பெறுநரின் ensureChannelPeer முன்பு ஒருபோதும் பார்த்திராத உறுப்பினருக்கு status="channel" உடன் ஒரு DPeer வரிசையை உருவாக்குகிறது. இது முக்கியம், ஏனெனில் fan-out ரூட்டிங்கிற்கு செய்திகளை அனுப்ப ஒவ்வொரு உறுப்பினரின் பியர் பதிவும் தேவை.

சேனல் வாழ்க்கைச்சுழற்சி {#channel-lifecycle}

Diagram 7
7

பியர் போக்குவரத்து அடுக்கு (LAN → Wi-Fi Aware → BLE) {#peer-transport-layer-lan--wi-fi-aware--ble}

PeerTransportRouter ஒரு சுற்று-உடைப்புடன் உத்திசாலைச் சங்கிலி. போக்குவரத்துகளின் வரிசையான பட்டியல்:

  1. LanTransport — முதல் தேர்வு. HTTPS வழியாக ChaCha20 மறைகுறியாக்க இடைமறிப்பானுடன் OkHttp-ஐப் பயன்படுத்துகிறது. peer.ip வெறுமையாக இருக்கும்போது முற்றிலும் தவிர்க்கப்படுகிறது (இன்னும் கண்டுபிடிக்காத குறுக்கு-சப்நெட் பியர்).
  2. WifiAwareTransport (Android 13+ மட்டும்) — Wi-Fi Aware (NAN) தரவுப் பாதைகளைப் பயன்படுத்துகிறது. பியரின் awareRunning கொடி false ஆக இருக்கும்போது விரைவாகத் தவிர்க்கிறது (BLE prewarmer ஸ்கேனால் புதுப்பிக்கப்படுகிறது). பியரின் IPv6 hostname plain-aware-peer-ஐ link-local முகவரிக்கு வரைபடுத்தும் தனிப்பயன் DNS வழியாகத் தீர்மானிக்கப்படுகிறது.
  3. BleTransport — எந்தப் பெயர் செய்த பியருக்கும் உத்தரவாத fallback. GATT வழியாக துண்டாக்கப்பட்ட RPC-ஐ ஸ்ட்ரீம் செய்கிறது. மெதுவானது ஆனால் எந்த IP இணைப்பும் இல்லாமலேயே வேலை செய்கிறது.

Diagram 8
8

ஏன் இந்த வரிசை?

  • LAN வேகமானது (ஒற்றை HTTPS ரவுண்ட் ட்ரிப், ~10 ms நேரமுடிவு).
  • Wi-Fi Aware நடுத்தரம் (தரவு-பாதை அமைப்பு ~5 வி, பின்னர் ~10 ms ரவுண்ட் ட்ரிப்கள்) மற்றும் குறுக்கு-சப்நெட்டில் வேலை செய்கிறது (உ.ம். ஒரு சாதனம் guest Wi-Fi-இல், மற்றொன்று IoT Wi-Fi-இல்). பியரின் Aware சேவை இயங்கவில்லை என்றால் விரைவாகத் தவிர்க்க tune செய்யப்பட்டுள்ளது, 10 வி buildLink நேரமுடிவைத் தவிர்க்கிறது.
  • BLE மெதுவானது ஆனால் எந்த IP இணைப்பும் இல்லாமலேயே வேலை செய்கிறது — Wi-Fi இல்லாவிட்டாலும் கூட, செய்தி இன்னும் வந்துசேரும். பெயர் செய்த பியர்களுக்கு உத்தரவாத fallback-ஆகப் பயன்படுத்தப்படுகிறது.

சுற்று-உடைப்பான் ஒரு நம்பமுடியாத போக்குவரத்தை (குறிப்பாக நெட்வொர்க் கொந்தளிப்பின்போது Wi-Fi Aware) 2 தோல்விகளுக்குப் பிறகு 30 வி-க்குத் தவிர்க்கிறது, எனவே fallback மீண்டும் மீண்டும் 10 வி நேரமுடிவுகளுக்குக் காத்திருப்பதற்குப் பதிலாக விரைவாக நிகழ்கிறது.

Wi-Fi Aware handshake

AwareSession ஒரு தரவுப் பாதையைத் திறப்பதற்கு முன்பு இரண்டு-செய்தி handshake செய்கிறது:

  • MSG_HELLO (subscriber → publisher): "நான் உன்னைப் பார்க்கிறேன், இதோ எனது பியர் handle."
  • MSG_READY (publisher → subscriber): "நான் எனது நெட்வொர்க் specifier-ஐப் பதிவுசெய்தேன், நீ இப்போது requestNetwork செய்யலாம்."

இது Android ஃப்ரேம்வொர்க்கின் ~500 ms சாளரத்திற்குள் இரு தரப்பின் connectivityManager.requestNetwork(...) அழைப்புகளை ஒருங்கிணைக்கிறது. subscriber என்பது சிறிய clientId உள்ள பக்கம் (நிர்ணய பங்குப் பிரிவு — இரு தரப்பும் ஒருங்கிணைப்பு இல்லாமல் ஒப்புக்கொள்கின்றன), மற்றும் இது மறுமுயற்சி வளையத்தை வைத்திருக்கிறது.

பியர் நிலை & Presence {#peer-status--presence}

Presence நீண்ட-கால WebSocket இணைப்புகள் வழியாகக் கண்காணிக்கப்படுகிறது. ஒவ்வொரு ஜோடியின் ஒரு பக்கம் மட்டுமே socket-ஐத் திறக்கிறது — நிர்ணய விதி TempData.clientId < peer.id-ஆல் முடிவு செய்யப்படுகிறது. மறு பக்கம் /peer_status-இல் உள்வரும் இணைப்பை ஏற்கிறது.

Diagram 9
9

PeerCacher.onlineMap presence-க்கான உண்மையான மூலம். இது onlinePeerIds: StateFlow<Set<String>>-ஆக வெளிப்படுத்தப்படுகிறது, இது சேனல் தலைவர் தேர்தலால் (electLeader(onlinePeerIds, myId)) நுகரப்படுகிறது.

தற்காலிக சேமிப்பு அடுக்கு {#caching-layer}

இரண்டு caches தரவுத்தள அட்டவணைகளை நினைவகத்தில் பிரதிபலித்து, Compose நேரடியாகச் சேகரிக்கும் StateFlow-களை வெளிப்படுத்துகின்றன:

Diagram 10
10

ஏன் copy-on-write?

Kotlin-ன் MutableStateFlow.distinctUntilChanged கட்டமைப்பு சமத்துவத்தைப் பயன்படுத்துகிறது. DPeer-ஐ இடத்தில் மாற்றியிருந்தால், பெறப்பட்ட pairedPeers பட்டியல் முன்பும் பின்பும் அதே DPeer குறிப்பைக் கொண்டிருக்கும், மேலும் distinctUntilChanged வித்தியாசத்தைப் பார்க்காமல் உமிழ்வை அடக்கும். Entity-ஐ முதலில் நகலெடுத்து, நகலை மாற்றி, வரைபட உள்ளீட்டை ஒரு புதிய PeerRuntime/ChannelRuntime-உடன் மாற்றினால், பெறப்பட்ட பட்டியல் புதிய-குறிப்புகளின்-புதிய-பட்டியலைப் பெற்று flow இயங்குகிறது.

கோப்பு பதிவிறக்கங்கள் {#file-downloads}

உள்வரும் கோப்பு/பட செய்திகள் ஒரு வரம்புக்குட்பட்ட வேலையாள் குளத்தால் தானாகப் பதிவிறக்கப்படுகின்றன. ஒவ்வொரு பதிவிறக்கமும் கிடைக்கக்கூடிய எந்தப் போக்குவரத்து வழியாகவும் (PeerTransportRouter.downloadFile) ஸ்ட்ரீம் செய்து தற்காலிகக் கோப்பில் எழுதுகிறது, பின்னர் செயலியின் மீடியா ஸ்டோருக்குள் import செய்து, அரட்டை உருப்படியின் uri புலத்தை patch செய்கிறது.

Diagram 11
11

போக்குவரத்து-அற்ற ஸ்ட்ரீமிங்

DownloadedResponse(status, ByteReadChannel, onClose): AutoCloseable சுருக்கம் LAN மற்றும் Wi-Fi Aware நேரடி HTTP உடலை ஸ்ட்ரீம் செய்ய அனுமதிக்கிறது, அதேசமயம் BLE துண்டாக்கப்பட்ட RPC-ஐ (16 KiB துண்டுகள் GET /fs?id=…&offset=…&length=… வழியாக) அதே ByteReadChannel-உடன் ஸ்ட்ரீம் செய்கிறது. onClose கால்பேக் BLE-ஐ நுகர்வோர் பதிலை சீக்கிரமாக மூடும்போது (உ.ம். இடைநிறுத்தத்தில்) அதன் பின்னணி பதிவிறக்க coroutine-ஐ ரத்து செய்ய அனுமதிக்கிறது.

வடிவமைப்பு வடிவங்கள் சுருக்கம் {#design-patterns-recap}

வடிவம்எங்கேஏன்
FaçadeChatManagerஒற்றை நுழைவுப் புள்ளி; அழைப்பவர்கள் ஒருபோதும் DB/போக்குவரத்தை நேரடியாகத் தொட மாட்டார்கள்.
Strategy + Chain of Resp.PeerTransportRouter + LanTransport/WifiAwareTransport/BleTransportTransportUnavailable fall-through சமிக்ஞையாகச் செருகக்கூடிய போக்குவரத்துகள்.
Circuit BreakerPeerCircuitBreaker2 தோல்விகள் / 30 வி ஒரு (peer, transport) காலைத் திறக்கிறது, இதனால் Wi-Fi Aware fallback-ஐத் தடுக்காது.
State MachinePeerStatusManager.PeerState, AwarePeerLink.LinkStatesocket வாழ்க்கைச்சுழற்சி மற்றும் NDP link வாழ்க்கைச்சுழற்சிக்கான வெளிப்படையான மாற்றங்கள்.
Producer/Consumer + PoolDownloadQueue (3 வேலையாளர்கள், Channel.BUFFERED)கோப்பு பதிவிறக்கங்களுக்கு வரம்புக்குட்பட்ட ஒரே நேரம்.
Observer / Reactiveஎல்லா இடங்களிலும் StateFlowCompose நேரடியாகச் சேகரிக்கிறது; கைமுறை புதுப்பிப்பு இல்லை.
Replay ProtectionChatMessageReceiver.seenSignatures, PeerChatParser.MAX_TIMESTAMP_DIFF_MSLAN+BLE இரட்டை வழங்கலிலிருந்து நகல்களைக் கைவிடு; வெளியில்-சாளர நேரமுத்திரைகளை நிராகரி.
Exponential BackoffPeerStatusManager.scheduleReconnectmin(60 வி, 1 வி × 2^min(n-1, 6)) — 64 வி-இல் முடிகிறது.
Copy-on-WritePeerCacher.mutatePeer, ChannelCacher.mutateChannelஒவ்வொரு மாற்றத்திலும் StateFlow.distinctUntilChanged இயங்கக் கட்டாயப்படுத்துகிறது.
Signed EnvelopePeerGraphQLClient.buildSignedRequestsignature|timestamp|body — மறுபயன்பாட்டைத் தடுக்க நேரமுத்திரையை உடலுடன் பிணைக்கிறது.
Deterministic Role SplitTempData.clientId < peer.idWebSocket கிளையன்ட் vs சேவையகம், மற்றும் Wi-Fi Aware subscriber vs publisher முடிவு செய்கிறது.
Lazy Hydrationinvite/update-இல் ensureChannelPeerபார்க்காத சேனல் உறுப்பினர்களுக்கு peers வரிசைகளை உருவாக்குகிறது, இதனால் fan-out ரூட்டிங் வேலை செய்யும்.
Encrypted IdentityLANDiscoverManager.discoverSpecificDeviceஇயக்கப்பட்ட DISCOVER இலக்கு id-ஐ பியர் விசையுடன் மறைகுறியாக்குகிறது — இலக்கு மட்டுமே அதை அடையாளம் காணும்.

மேலும் படிப்பதற்கு

  • பெயரிங் பாய்வு — இரண்டு சாதனங்கள் எப்படி நம்பிக்கையை நிறுவி, இந்தக் கட்டுரையில் உள்ள ஒவ்வொரு போக்குவரத்தாலும் பயன்படுத்தப்படும் பகிரப்பட்ட ChaCha20 விசையைப் பரிமாறுகின்றன.
  • apitest/groups/chat-messages.sh மற்றும் apitest/groups/chat-channels.sh — ஒவ்வொரு GraphQL mutation-ஐயும் முழுமையாகச் சோதிக்கும் இயங்கக்கூடிய சோதனைத் திட்டம்.