What is TCP?
TCP (Transmission Control Protocol) is a core protocol of the Internet Protocol Suite that provides reliable, ordered, and error-checked data transmission between applications. TCP sits at the Transport Layer (Layer 4) and is used by most internet applications including HTTP, HTTPS, FTP, and email.
TCP vs UDP
Key Differences
interface ProtocolComparison {
tcp: {
type: 'Connection-oriented';
reliability: 'Guaranteed delivery';
ordering: 'Packets arrive in order';
speed: 'Slower (overhead)';
headerSize: '20-60 bytes';
errorChecking: 'Extensive';
useCases: ['HTTP', 'HTTPS', 'FTP', 'Email', 'SSH'];
};
udp: {
type: 'Connectionless';
reliability: 'Best effort (no guarantee)';
ordering: 'Packets may arrive out of order';
speed: 'Faster (minimal overhead)';
headerSize: '8 bytes';
errorChecking: 'Basic checksum';
useCases: ['DNS', 'Streaming', 'Gaming', 'VoIP', 'IoT'];
};
}
TCP Three-Way Handshake
Connection Establishment
Client Server
| |
|-- SYN (seq=100) ------------>| 1. Client initiates
| |
|<- SYN-ACK (seq=300,ack=101)--| 2. Server acknowledges
| |
|-- ACK (seq=101,ack=301) ---->| 3. Client confirms
| |
| Connection Established |
Sequence:
1. SYN: Client sends synchronization packet
2. SYN-ACK: Server acknowledges and sends its own SYN
3. ACK: Client acknowledges server's SYN
State: ESTABLISHED
Connection Termination
Client Server
| |
|-- FIN (seq=500) ------------>| 1. Client wants to close
| |
|<- ACK (ack=501) -------------| 2. Server acknowledges
| |
|<- FIN (seq=700) -------------| 3. Server sends FIN
| |
|-- ACK (ack=701) ------------>| 4. Client acknowledges
| |
| Connection Closed |
Four-way handshake for graceful shutdown
TCP in Node.js
Creating a TCP Server
import net from 'net';
const server = net.createServer((socket) => {
console.log('Client connected:', socket.remoteAddress);
// Handle incoming data
socket.on('data', (data) => {
console.log('Received:', data.toString());
// Echo back to client
socket.write(`Echo: ${data.toString()}`);
});
// Handle client disconnect
socket.on('end', () => {
console.log('Client disconnected');
});
// Handle errors
socket.on('error', (err) => {
console.error('Socket error:', err.message);
});
});
server.listen(3000, () => {
console.log('TCP server listening on port 3000');
});
// Graceful shutdown
process.on('SIGTERM', () => {
server.close(() => {
console.log('Server closed');
process.exit(0);
});
});
Creating a TCP Client
import net from 'net';
const client = net.createConnection(
{
host: 'localhost',
port: 3000
},
() => {
console.log('Connected to server');
// Send data
client.write('Hello, server!');
}
);
// Receive data from server
client.on('data', (data) => {
console.log('Received:', data.toString());
client.end(); // Close connection
});
// Handle connection end
client.on('end', () => {
console.log('Disconnected from server');
});
// Handle errors
client.on('error', (err) => {
console.error('Connection error:', err.message);
});
TCP with Proxies
SOCKS5 TCP Proxy
import net from 'net';
interface SOCKSConfig {
proxyHost: string;
proxyPort: number;
targetHost: string;
targetPort: number;
}
class SOCKS5Client {
private config: SOCKSConfig;
constructor(config: SOCKSConfig) {
this.config = config;
}
async connect(): Promise<net.Socket> {
return new Promise((resolve, reject) => {
// Connect to SOCKS5 proxy
const socket = net.createConnection(
{
host: this.config.proxyHost,
port: this.config.proxyPort
},
async () => {
try {
// SOCKS5 handshake
await this.handshake(socket);
// Connect to target through proxy
await this.connectTarget(socket);
resolve(socket);
} catch (error) {
reject(error);
}
}
);
socket.on('error', reject);
});
}
private async handshake(socket: net.Socket): Promise<void> {
return new Promise((resolve, reject) => {
// Send greeting: VER | NMETHODS | METHODS
const greeting = Buffer.from([0x05, 0x01, 0x00]);
socket.write(greeting);
socket.once('data', (data) => {
if (data[0] === 0x05 && data[1] === 0x00) {
resolve();
} else {
reject(new Error('SOCKS5 handshake failed'));
}
});
});
}
private async connectTarget(socket: net.Socket): Promise<void> {
return new Promise((resolve, reject) => {
// Build connection request
const request = this.buildConnectRequest();
socket.write(request);
socket.once('data', (data) => {
if (data[0] === 0x05 && data[1] === 0x00) {
resolve();
} else {
reject(new Error('SOCKS5 connection failed'));
}
});
});
}
private buildConnectRequest(): Buffer {
const { targetHost, targetPort } = this.config;
// VER | CMD | RSV | ATYP
const header = Buffer.from([0x05, 0x01, 0x00, 0x03]);
// Domain length
const domainLength = Buffer.from([targetHost.length]);
// Domain
const domain = Buffer.from(targetHost);
// Port (big-endian)
const port = Buffer.allocUnsafe(2);
port.writeUInt16BE(targetPort);
return Buffer.concat([header, domainLength, domain, port]);
}
}
// Usage
const socks5 = new SOCKS5Client({
proxyHost: 'proxy.example.com',
proxyPort: 1080,
targetHost: 'api.example.com',
targetPort: 443
});
const socket = await socks5.connect();
// Now use socket to communicate with target
socket.write('GET / HTTP/1.1\r\nHost: api.example.com\r\n\r\n');
TCP Connection Pooling
Reusable Connection Pool
class TCPConnectionPool {
private pool: Map<string, net.Socket[]>;
private maxConnections: number;
constructor(maxConnections: number = 10) {
this.pool = new Map();
this.maxConnections = maxConnections;
}
async getConnection(host: string, port: number): Promise<net.Socket> {
const key = `${host}:${port}`;
const connections = this.pool.get(key) || [];
// Return existing connection if available
if (connections.length > 0) {
const socket = connections.pop()!;
// Verify connection is still alive
if (!socket.destroyed) {
return socket;
}
}
// Create new connection
return this.createConnection(host, port, key);
}
private createConnection(
host: string,
port: number,
key: string
): Promise<net.Socket> {
return new Promise((resolve, reject) => {
const socket = net.createConnection({ host, port }, () => {
// Enable keep-alive
socket.setKeepAlive(true, 60000);
resolve(socket);
});
socket.on('error', reject);
});
}
releaseConnection(socket: net.Socket, host: string, port: number): void {
if (socket.destroyed) return;
const key = `${host}:${port}`;
const connections = this.pool.get(key) || [];
if (connections.length < this.maxConnections) {
connections.push(socket);
this.pool.set(key, connections);
} else {
// Pool is full, close connection
socket.end();
}
}
closeAll(): void {
for (const connections of this.pool.values()) {
connections.forEach(socket => socket.end());
}
this.pool.clear();
}
}
// Usage
const pool = new TCPConnectionPool(20);
// Get connection
const socket = await pool.getConnection('api.example.com', 80);
// Use connection
socket.write('GET / HTTP/1.1\r\n\r\n');
socket.on('data', (data) => {
console.log(data.toString());
// Return to pool
pool.releaseConnection(socket, 'api.example.com', 80);
});
TCP Performance Tuning
Socket Options
function optimizeTCPSocket(socket: net.Socket): void {
// Disable Nagle's algorithm for lower latency
socket.setNoDelay(true);
// Enable keep-alive
socket.setKeepAlive(true, 60000); // 60 seconds
// Set buffer sizes
socket.setReadableHighWaterMark(64 * 1024); // 64KB
socket.setWritableHighWaterMark(64 * 1024);
// Set timeout
socket.setTimeout(30000); // 30 seconds
socket.on('timeout', () => {
console.log('Socket timeout');
socket.end();
});
}
// Usage
const socket = net.createConnection({ host: 'example.com', port: 80 });
optimizeTCPSocket(socket);
Measuring TCP Performance
class TCPPerformanceMonitor {
private startTime: number = 0;
private bytesReceived: number = 0;
private bytesSent: number = 0;
startMonitoring(socket: net.Socket): void {
this.startTime = Date.now();
socket.on('data', (data) => {
this.bytesReceived += data.length;
});
// Track sent data
const originalWrite = socket.write.bind(socket);
socket.write = ((data: any, ...args: any[]) => {
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
this.bytesSent += buffer.length;
return originalWrite(data, ...args);
}) as any;
}
getMetrics() {
const duration = (Date.now() - this.startTime) / 1000; // seconds
return {
duration: `${duration.toFixed(2)}s`,
bytesReceived: this.bytesReceived,
bytesSent: this.bytesSent,
downloadSpeed: `${(this.bytesReceived / duration / 1024).toFixed(2)} KB/s`,
uploadSpeed: `${(this.bytesSent / duration / 1024).toFixed(2)} KB/s`,
totalBandwidth: this.bytesReceived + this.bytesSent
};
}
}
// Usage
const monitor = new TCPPerformanceMonitor();
const socket = net.createConnection({ host: 'example.com', port: 80 });
monitor.startMonitoring(socket);
socket.on('end', () => {
console.log('Performance metrics:', monitor.getMetrics());
});
TCP Error Handling
Robust Error Handling
class RobustTCPClient {
private maxRetries: number = 3;
private retryDelay: number = 1000;
async connectWithRetry(
host: string,
port: number,
attempt: number = 0
): Promise<net.Socket> {
try {
return await this.connect(host, port);
} catch (error) {
if (attempt >= this.maxRetries) {
throw new Error(`Failed after ${this.maxRetries} attempts: ${error}`);
}
console.log(`Connection failed, retrying in ${this.retryDelay}ms...`);
await new Promise(resolve => setTimeout(resolve, this.retryDelay));
return this.connectWithRetry(host, port, attempt + 1);
}
}
private connect(host: string, port: number): Promise<net.Socket> {
return new Promise((resolve, reject) => {
const socket = net.createConnection({ host, port }, () => {
resolve(socket);
});
// Handle common errors
socket.on('error', (err: NodeJS.ErrnoException) => {
switch (err.code) {
case 'ECONNREFUSED':
reject(new Error('Connection refused - server not listening'));
break;
case 'ETIMEDOUT':
reject(new Error('Connection timed out'));
break;
case 'ENOTFOUND':
reject(new Error('Host not found'));
break;
case 'ENETUNREACH':
reject(new Error('Network unreachable'));
break;
default:
reject(err);
}
});
socket.setTimeout(10000); // 10 second timeout
socket.on('timeout', () => {
socket.destroy();
reject(new Error('Connection timeout'));
});
});
}
}
// Usage
const client = new RobustTCPClient();
const socket = await client.connectWithRetry('api.example.com', 443);
TCP Security
TLS/SSL over TCP
import tls from 'tls';
import fs from 'fs';
// Create secure TCP server
const options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem')
};
const secureServer = tls.createServer(options, (socket) => {
console.log('Secure connection from:', socket.remoteAddress);
socket.on('data', (data) => {
console.log('Encrypted data received:', data.toString());
socket.write('Secure response');
});
});
secureServer.listen(443, () => {
console.log('Secure TCP server listening on port 443');
});
// Create secure TCP client
const secureSocket = tls.connect(
{
host: 'secure.example.com',
port: 443,
rejectUnauthorized: true // Verify server certificate
},
() => {
console.log('Secure connection established');
console.log('Cipher:', secureSocket.getCipher());
secureSocket.write('Encrypted message');
}
);
TCP Best Practices
For Developers
- Always implement proper error handling
- Use connection pooling for efficiency
- Set appropriate timeouts
- Enable keep-alive for long connections
- Clean up connections properly
For Performance
- Disable Nagle’s algorithm when needed
- Use appropriate buffer sizes
- Monitor connection metrics
- Implement connection reuse
- Handle backpressure correctly
For Reliability
- Implement retry logic with backoff
- Handle all error codes appropriately
- Monitor connection health
- Use TLS for sensitive data
- Implement graceful shutdown