Socket(套接字)是什么?
简单来说,Socket 是网络通信的端点,可以理解为两个应用程序通过网络进行通信的"连接插口"。
生活中的类比
想象你要打电话:
- 你的手机 = 客户端Socket
- 朋友的手机 = 服务器Socket
- 电话号码 = IP地址 + 端口
- 电话线/信号 = 网络连接
- 通话内容 = 传输的数据
技术解释
1. Socket 的本质
Socket 是操作系统提供的一个编程接口(API),允许应用程序通过网络发送和接收数据。
javascript
// 简化的 Socket 通信过程
客户端Socket ←---网络---> 服务器Socket
↓ ↓
你的浏览器 Node.js 服务器
2. Socket 的组成
一个 Socket 由以下信息唯一标识:
- IP地址 :设备在网络中的地址(如
192.168.1.100) - 端口号 :应用程序的"门牌号"(如
80用于 HTTP,443用于 HTTPS) - 协议:通信规则(TCP 或 UDP)
格式:IP地址:端口号
例如:192.168.1.100:8080
3. 在 Node.js/Express 中的 Socket
在你之前的代码中:
javascript
app.get('/api/long-poll', (req, res) => {
// req.socket 就是底层网络连接
req.socket.setTimeout(30000); // 设置这个连接的Socket超时
// req.socket 包含连接信息
console.log(req.socket.remoteAddress); // 客户端IP
console.log(req.socket.remotePort); // 客户端端口
console.log(req.socket.localPort); // 服务器端口
});
4. HTTP 请求与 Socket 的关系
javascript
// HTTP请求建立在Socket连接之上
客户端发出HTTP请求
↓
通过Socket发送请求数据
↓
服务器通过Socket接收数据 → 解析成HTTP请求对象(req)
↓
服务器处理请求,生成响应
↓
通过同一个Socket发送响应数据
↓
客户端通过Socket接收响应
5. Socket 的生命周期(TCP 连接)
javascript
// 1. 建立连接(三次握手)
客户端: SYN → 服务器
客户端: ← SYN-ACK 服务器
客户端: ACK → 服务器
// 连接建立!
// 2. 数据传输
客户端通过Socket发送: "GET /api/data HTTP/1.1\r\n..."
服务器通过Socket回复: "HTTP/1.1 200 OK\r\n..."
// 3. 关闭连接(四次挥手)
客户端: FIN → 服务器
服务器: ACK → 客户端
服务器: FIN → 客户端
客户端: ACK → 服务器
// 连接关闭
6. 为什么长轮询需要关注 Socket?
javascript
// 长轮询中的Socket管理
app.get('/long-poll', (req, res) => {
// 问题:HTTP请求可能结束,但Socket连接还在!
// HTTP层:Express响应已发送
res.json({ data: 'done' });
// Socket层:底层TCP连接可能保持打开(Keep-Alive)
// 需要确保连接被正确清理
});
7. 实际代码示例:查看 Socket 信息
javascript
app.get('/connection-info', (req, res) => {
const socket = req.socket;
const info = {
// 连接信息
clientAddress: socket.remoteAddress, // 客户端IP
clientPort: socket.remotePort, // 客户端端口
serverPort: socket.localPort, // 服务器端口
// Socket状态
bytesRead: socket.bytesRead, // 已读取字节数
bytesWritten: socket.bytesWritten, // 已写入字节数
connecting: socket.connecting, // 是否正在连接
destroyed: socket.destroyed, // 是否已销毁
// 超时设置
timeout: socket.timeout, // 当前超时设置
// TLS/SSL信息(如果是HTTPS)
encrypted: socket.encrypted, // 是否加密
authorized: socket.authorized // 证书是否有效
};
res.json(info);
});
// 示例输出:
// {
// "clientAddress": "::ffff:192.168.1.100",
// "clientPort": 54321,
// "serverPort": 3000,
// "bytesRead": 342,
// "bytesWritten": 567,
// "connecting": false,
// "destroyed": false,
// "timeout": 30000
// }
8. Socket 与 HTTP Keep-Alive
javascript
// 现代HTTP默认使用Keep-Alive
app.get('/test', (req, res) => {
console.log('同一个Socket可能被多个HTTP请求重用');
res.json({ message: 'Hello' });
});
// 1. 客户端首次请求:创建新Socket
// 2. 请求完成:Socket不关闭,保持连接
// 3. 客户端再次请求:重用同一个Socket
// 4. 减少连接建立开销,提高性能
9. 不同类型的 Socket
javascript
// 1. TCP Socket(流式,可靠)
// 你的Express服务器使用的就是TCP Socket
const net = require('net');
const server = net.createServer((socket) => {
socket.write('Hello from TCP server!');
socket.end();
});
// 2. UDP Socket(数据报,快速但不可靠)
const dgram = require('dgram');
const udpServer = dgram.createSocket('udp4');
udpServer.on('message', (msg, rinfo) => {
console.log(`UDP message from ${rinfo.address}:${rinfo.port}`);
});
// 3. Unix Domain Socket(本地进程通信)
const http = require('http');
const server = http.createServer();
server.listen('/tmp/server.sock'); // 文件路径而非端口
总结:Socket 的核心概念
| 概念 | 说明 | 类比 |
|---|---|---|
| 通信端点 | 两个程序之间的连接点 | 电话线的两端 |
| IP + 端口 | 唯一标识一个连接 | 电话号码 |
| 双向通道 | 可以同时发送和接收数据 | 双向通话 |
| 协议载体 | 承载 HTTP、WebSocket 等协议 | 邮车运送信件 |
| 资源句柄 | 操作系统分配的资源标识符 | 文件描述符 |
关键理解:
- HTTP 是应用层协议,Socket 是传输层接口
- 一个 TCP 连接 = 一个 Socket 对(客户端Socket + 服务器Socket)
- 长轮询需要管理 Socket,避免连接泄漏
req.socket让你可以直接操作底层网络连接