Managing Connection Lifecycle
1. Handling Connection Open
| Step | Action |
| 1 | Reset reconnect attempt counter |
| 2 | Send auth (if message-based) |
| 3 | Resume subscriptions / state |
| 4 | Start heartbeat timer |
| 5 | Flush outbound queue |
2. Handling Connection Close
Example: Close inspection
ws.addEventListener("close", (e) => {
console.log({ code: e.code, reason: e.reason, clean: e.wasClean });
stopHeartbeat();
if (shouldReconnect(e.code)) scheduleReconnect();
});
| Code | Reconnect? |
| 1000 Normal | No (intentional) |
| 1001 Going Away | Yes |
| 1006 Abnormal | Yes (with backoff) |
| 4401 Unauthorized (custom) | No (refresh token first) |
3. Understanding Close Codes
| Code | Name | Meaning |
| 1000 | Normal Closure | Successful completion |
| 1001 | Going Away | Server shutdown / page navigation |
| 1002 | Protocol Error | Spec violation |
| 1003 | Unsupported Data | e.g. binary on text-only endpoint |
| 1005 | No Status | Reserved — no code received |
| 1006 | Abnormal | Connection dropped without close frame |
| 1007 | Invalid Payload | Bad UTF-8 in text frame |
| 1008 | Policy Violation | Generic policy reject |
| 1009 | Message Too Big | Payload exceeds limit |
| 1010 | Mandatory Extension | Server didn't accept required ext |
| 1011 | Internal Error | Server fault |
| 1012 | Service Restart | Reconnect later |
| 1013 | Try Again Later | Server overloaded |
| 1015 | TLS Handshake | Reserved |
| 3000-3999 | Registered | Library/framework codes |
| 4000-4999 | Application | Free for your app |
4. Sending Close Frame
Example: Close with code/reason
ws.close(1000, "user-logout");
// 4xxx app codes
ws.close(4001, "auth-expired");
| Constraint | Detail |
| code | 1000 or 3000-4999 from app |
| reason | UTF-8 string ≤ 123 bytes |
| Idempotent | Subsequent calls ignored |
5. Handling Abnormal Closure
Note: Code 1006 means no Close frame was exchanged — typically network loss, proxy timeout, or server crash. Always retry with backoff.
| Cause | Action |
| Network loss | Backoff reconnect |
| Idle timeout | Add heartbeat |
| Server crash | Retry; check health endpoint |
6. Implementing Graceful Shutdown
Graceful Shutdown Sequence
- Stop accepting new outbound messages
- Flush
bufferedAmount to 0
- Stop heartbeat timer
- Call
ws.close(1000, reason)
- Await
close event (with timeout)
- Release references / listeners
| Phase | Detail |
| Drain | Wait for buffer to empty |
| Close | Send Close frame with code 1000 |
| Timeout | Force-discard after ~3s |
7. Cleaning Up Resources
| Resource | Cleanup |
| Event listeners | AbortController.abort() |
| Timers | clearInterval/clearTimeout |
| Pending requests | Reject with abort reason |
| Subscriptions | Clear local registry |
8. Tracking Connection Duration
Example: Uptime tracking
let openedAt = 0;
ws.addEventListener("open", () => { openedAt = Date.now(); });
ws.addEventListener("close", () => {
metrics.observe("ws_duration_ms", Date.now() - openedAt);
});
| Metric | Use |
| Connect time | Handshake latency |
| Uptime | Connection stability |
| Reconnect count | Network quality signal |
9. Implementing Connection Pooling
| Strategy | Detail |
| One per tab | Use BroadcastChannel/SharedWorker |
| Multiplexed | One socket, message channel field |
| Per-feature | Separate sockets for isolation |
10. Handling Server Shutdown
| Server Action | Code | Client Action |
| Rolling deploy | 1001 Going Away | Reconnect |
| Maintenance | 1012/1013 | Backoff longer |
| Crash | 1006 | Backoff + circuit breaker |