Standard mobile file sharing solutions frequently route local data through intermediary cloud storage infrastructure. Relaying gigabyte-scale payloads through centralized cloud buckets introduces substantial network latency, incurs redundant bandwidth expenditure, and presents clear data isolation risks. To overcome these constraints, I designed and developed ShareConnect, a high-throughput, serverless, peer-to-peer (P2P) file transfer application built with React Native, raw TCP socket streams, UDP subnet broadcasting, and MMKV. This document details the network handshake protocol, chunked streaming pipeline, backpressure management, and architecture required to achieve 30 to 50+ MB/s direct throughput across local network interfaces.
1. Direct Peer Discovery and Handshake Protocol
Establishing an offline transport between two client runtimes without an intermediary signaling server requires a deterministic three-phase lifecycle:
sequenceDiagram
autonumber
actor Receiver as Receiver Node
actor Sender as Sender Node
Note over Receiver: Phase 1: Subnet Beacon
Receiver->>Receiver: Bind Socket to Local Hotspot / WLAN
Receiver->>Sender: UDP Broadcast on 255.255.255.255:8888
Note over Receiver,Sender: Phase 2: Visual Cryptographic Handshake
Receiver->>Receiver: Render QR Payload (IP: 192.168.43.1, Port: 4000, Nonce)
Sender->>Receiver: Capture QR Payload via Camera Sensor
Sender->>Sender: Extract Transport Coordinates and Session Key
Note over Receiver,Sender: Phase 3: Raw TCP Connection Initialization
Sender->>Receiver: TCP SYN -> Connect to 192.168.43.1:4000
Receiver-->>Sender: TCP SYN-ACK -> Establish Persistent Socket
Sender->>Receiver: Emit Handshake Frame (`client_connected`)
Receiver-->>Sender: Acknowledge Handshake Frame
Note over Receiver,Sender: Full-Duplex Binary Stream Established
The Three Protocol Stages:
-
UDP Subnet Discovery (
react-native-udp): The receiving device issues periodic datagram beacons across the local broadcast domain (255.255.255.255:8888) to advertise availability. - Visual QR Exchange: Eliminates manual IP configuration and local mDNS discovery latency by embedding endpoint coordinates, port configuration, and an ephemeral session nonce into an optically scannable matrix.
-
Persistent TCP Socket (
react-native-tcp-socket): The sender connects directly to the receiver's socket server over the local IP interface, creating an uninterrupted, full-duplex transmission path.
2. Chunked Binary Streaming Engine
Reading large binary payloads into JavaScript heap memory triggers Out-of-Memory (OOM) termination in the Hermes and V8 runtimes. The transmission pipeline segments the input stream into 64 KB binary frames, streaming them sequentially while enforcing write backpressure at the destination node.
flowchart TD
Init[Select Source File: 1.5 GB Binary] --> PreFlight[Extract File Stats: Total Chunks, Size, SHA-256 Checksum]
PreFlight --> HeaderFrame[Transmit Metadata Acknowledgment Frame]
subgraph Transmission Loop
HeaderFrame --> Slice[Read 64 KB Block via react-native-fs]
Slice --> SocketWrite[socket.write: Binary Chunk Frame]
SocketWrite --> WaitAck{Wait for send_chunk_ack?}
WaitAck -- ACK Received --> Incr[Advance Window: Index + 1]
Incr --> BoundaryCheck{All Chunks Dispatched?}
BoundaryCheck -- No --> Slice
end
subgraph Ingestion Loop
SocketRead[Socket Event: data] --> FrameParse[Extract Chunk Index and Payload]
FrameParse --> DiskWrite[Append to File Stream via MMKV / Storage Engine]
DiskWrite --> EmitAck[socket.write: send_chunk_ack]
EmitAck --> WaitAck
end
BoundaryCheck -- Yes --> Finalize[Compute Disk SHA-256 Digest]
Finalize --> IntegrityVerify{Digest Valid?}
IntegrityVerify -- Yes --> TargetFile[Mark Complete in Storage]
IntegrityVerify -- No --> ErrorState[Flag Transmission Corruption]
3. Backpressure Flow Control: Mitigating Buffer Saturation
Over a 5 GHz local Wi-Fi interface, network throughput often surpasses flash memory sequential write speeds. Uncontrolled socket writes lead to rapid expansion of the underlying OS socket buffer, leading to high garbage collection overhead and dropped TCP connections. To prevent this, the client implements explicit frame-level acknowledgment flow control:
// Receiver: TCPUtils.ts
export const receiveFileAck = async (
metadata: FileMetadata,
socket: any,
setReceivedFiles: any,
): Promise<void> => {
const { setChunkStore } = useChunkStore.getState();
// Initialize receiving chunk state machine
setChunkStore({
id: metadata.id,
totalChunks: metadata.totalChunks,
name: metadata.name,
size: metadata.size,
mimeType: metadata.mimeType,
chunkArray: []
});
// Signal sender to begin transmitting chunk index 0
socket.write(JSON.stringify({ event: 'send_chunk_ack', chunkNo: 0 }));
};
The sender waits for send_chunk_ack before slicing and dispatching the subsequent segment from the storage layer:
typescript
// Sender: TCPUtils.ts
export const handleChunkAck = async (
ackPayload: { chunkNo: number },
fileRecord: FileRecord,
socket: any
): Promise<void> => {
const nextChunk = ackPayload.chunkNo;
if (nextChunk < fileRecord.totalChunks) {
const chunkData = await RNFS.read(
fileRecord.uri,
CHUNK_SIZE,
nextChunk * CHUNK_SIZE,
'base64'
);
socket.write(JSON.stringify({ event: 'file_chunk', chunkNo: nextChunk, data: chunkData }

Top comments (1)
Implementing a strict chunked streaming pipeline with frame-level acknowledgment is an exceptional, production-grade approach to local peer-to-peer data transfer. Managing high-throughput socket streaming within memory-constrained environments like mobile JavaScript runtimes requires exactly this type of rigorous, defensive systems engineering.
The protocol layout demonstrates several key architectural practices for mobile P2P architecture: