How Croc Supports Reconnection and Resuming Interrupted Transfers
Croc automatically detects network disconnections, implements exponential backoff with a 10-attempt limit, negotiates resume protocol versions with peers, and transfers only missing chunks to seamlessly resume interrupted file transfers.
Croc is an open-source command-line file transfer tool designed to survive flaky networks where traditional tools would fail completely. When TCP connections drop mid-transfer, croc does not terminate the process; instead, it enters a sophisticated reconnection routine that negotiates fresh relay rooms and resumes exactly where the transfer left off. This article examines the specific mechanisms in src/croc/croc.go, src/comm/comm.go, and supporting modules that make interruption-tolerant transfers possible.
Detecting Transfer Interruptions
Identifying Disconnects with transferDisconnectError
When a TCP link drops, croc wraps the underlying error in a custom transferDisconnectError type defined in src/croc/croc.go lines 302-306. The helper function isTransferDisconnectError (lines 93-96) recognizes this wrapper and distinguishes between fatal errors (like corrupted files) and recoverable network drops (like WiFi hiccups or mobile handoffs). Upon detection, croc triggers the reconnection routine instead of exiting.
Reconnection Strategy and Backoff Mechanisms
Limiting Retry Attempts
The reconnection logic respects a hard ceiling defined by maxReconnectAttempts in src/croc/croc.go lines 55-56, which defaults to 10 attempts. This prevents infinite retry loops against permanently offline peers while accommodating temporary network fluctuations.
Exponential Backoff Timing
Between attempts, croc implements reconnectBackoff (lines 79-90) to calculate delays using exponential backoff capping at 5 seconds. This reduces relay server load and allows transient network issues to resolve naturally.
Generating Fresh Relay Rooms
To resume a transfer, croc must establish a new communication channel independent of the broken connection. The generateReconnectRoom function (lines 71-77) creates a cryptographically random room name for the resumed session, ensuring the client and sender can reconnect through the relay without interfering with other active transfers or the stale session.
Negotiating Resume Protocol Versions
Before exchanging file data, croc ensures both peers speak the same resume protocol. The client maintains a reconnectVersion (lines 52-54 in src/croc/croc.go) exchanged during the handshake. If the peer reports a higher version (peerReconnectVersion), the client immediately adopts it, guaranteeing backward and forward compatibility during the resume process.
Resuming Interrupted Transfers with Chunk Ranges
Requesting Missing Chunks via RemoteFileRequest
Once the reconnection handshake completes, the receiver transmits a RemoteFileRequest (lines 104-110) containing the specific byte ranges or chunks still needed to complete the file. The sender responds by transmitting only those missing pieces rather than restarting the entire transfer from byte zero.
Handling Large Resume Payloads
For transfers involving multi-gigabyte files, the list of missing chunks can become substantial. To prevent timeouts during this control phase, src/comm/comm.go line 30 raises the messageBodyReadTimeout to 10 minutes, ensuring that extensive chunk lists can traverse slow relays without dropping the connection.
Completing the Transfer
The sender finalizes the resume by executing finishSender (lines 51-69 in src/croc/croc.go), writing only the requested missing chunks to the wire while the receiver writes them to the appropriate offsets in the target file. The underlying TCP wrapper in src/comm/comm.go and connection logic in src/tcp/tcp.go handle the actual byte streaming during this phase.
CLI Options for Automatic Resume
Using the Overwrite Flag
According to src/cli/cli.go line 125, users can invoke the --overwrite flag to automatically attempt resuming interrupted transfers without interactive prompting. Without this flag, croc asks for user confirmation unless --no-prompt is also set.
Resume a transfer automatically with default behavior:
croc send --code abcdef large-file.zip # Start transfer
# ... network drops ...
croc receive --code abcdef # Re-run the same command
# croc detects the previous session, creates a new relay room,
# asks for missing chunks, and continues.
Force resume without prompting:
croc receive --code abcdef --overwrite # Skip the "overwrite?" prompt
Programmatic handling of disconnections:
if err != nil {
if croc.IsTransferDisconnectError(err) {
// Reconnection logic is embedded in croc.Client.Start()
// Simply allow Start() to continue or call it again.
}
}
Summary
- Error Detection: Croc wraps TCP errors in
transferDisconnectError(src/croc/croc.golines 302-306) to identify recoverable network drops. - Retry Limits: The client attempts reconnection up to 10 times with exponential backoff (max 5 seconds) via
maxReconnectAttemptsandreconnectBackoff. - Fresh Channels:
generateReconnectRoomcreates new random room names to avoid session conflicts. - Version Negotiation:
reconnectVersionensures both peers agree on the resume protocol before exchanging data. - Partial Transfer:
RemoteFileRequestrequests only missing chunks, whilemessageBodyReadTimeout(10 minutes insrc/comm/comm.goline 30) accommodates large chunk lists. - CLI Control: The
--overwriteflag insrc/cli/cli.goline 125 enables automatic resume without user intervention.
Frequently Asked Questions
How many times will croc attempt to reconnect before giving up?
Croc defaults to 10 reconnection attempts as defined by maxReconnectAttempts in src/croc/croc.go lines 55-56. After exhausting these attempts, the transfer terminates and returns an error.
Does croc restart the entire file when resuming a transfer?
No. Croc uses RemoteFileRequest (lines 104-110) to communicate exactly which chunks are missing. The sender then transmits only those pieces via finishSender (lines 51-69), appending them to the partially received file without restarting from byte zero.
What happens if the sender and receiver have different versions of croc?
The resume protocol includes version negotiation through reconnectVersion and peerReconnectVersion (lines 52-54). Both sides automatically adopt the higher version number, ensuring backward compatibility and preventing protocol mismatches during reconnection.
How long will croc wait for a large list of missing chunks to transfer?
Croc sets messageBodyReadTimeout to 10 minutes in src/comm/comm.go line 30 specifically for resume control messages. This extended timeout accommodates huge file manifests that might otherwise timeout on slow relays, while normal data chunks use shorter deadlines.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →