Socket(套接字)是操作系统提供的网络通信编程接口,是应用程序通过网络发送和接收数据的"插座"。它是网络通信的端点抽象,让程序可以像操作文件一样读写网络数据。
核心概念
| 特性 |
说明 |
| 本质 |
操作系统内核中的通信端点数据结构 |
| 作用 |
应用程序 ↔ 网络 之间的桥梁 |
| 标识方式 |
IP地址 + 端口号(网络套接字)或 文件路径(UNIX套接字) |
| 操作方式 |
类似文件:创建 → 绑定/连接 → 读写 → 关闭 |
主要类型对比
| 类型 |
通信范围 |
地址格式 |
典型应用 |
| TCP Socket |
跨网络 |
IP:Port(如 192.168.1.1:80) |
HTTP/HTTPS、SSH、数据库连接 |
| UDP Socket |
跨网络 |
IP:Port |
DNS、视频流、游戏实时通信 |
| UNIX Domain Socket |
单机内 |
文件路径(如 /tmp/app.sock) |
Nginx↔PHP-FPM、MySQL本地连接、Docker |
Socket 通信流程
客户端 服务端
│ │
│ 1. socket() 创建套接字 │ 1. socket() 创建套接字
│ 2. connect() 连接 ─────────────→ │ 2. bind() 绑定地址
│ │ 3. listen() 监听
│ ←────────────────────────────── │ 4. accept() 接受连接
│ 5. send() / recv() 数据交换 │ 5. send() / recv()
│ 6. close() 关闭 ─────────────→ │ 6. close() 关闭
在你的 netstat 中看到的内容
# 网络套接字(跨机器通信)
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 501647/nginx
# ↑ ↑ ↑ ↑ ↑ ↑
# 接收队列 发送队列 本地地址 远程地址 状态 进程PID
# UNIX套接字(本机进程间通信)
unix 2 [ACC] STREAM LISTENING 776170 144612/PM2 /root/.pm2/pub.sock
# ↑ ↑ ↑ ↑ ↑ ↑ ↑
# 引用计数 标志 类型 状态 Inode 进程 文件路径
实际类比
| 类比 |
解释 |
| 电话插座 |
Socket 就像墙上的电话插孔,插上电话线就能通话 |
| 文件描述符 |
程序通过 Socket 获得的 fd(文件描述符),像操作文件一样 read()/write() |
| 港口码头 |
IP地址是国家,端口号是具体港口,Socket 是码头的装卸货位 |
常用命令查看 Sockets
# 查看所有网络连接
netstat -anp --inet -4 -6 # 传统方式
ss -anp # 现代替代(更快)
# 只看 TCP
ss -tlnp # t=TCP, l=LISTEN, n=数字, p=进程
# 只看 UNIX sockets
ss -xlp # x=UNIX
# 查看某个端口的连接
ss -anp | grep :443
一句话总结
Socket 是应用程序的网络通信端点 — 它封装了 IP、端口、协议等信息,让程序不必关心底层网络细节,只需像读写文件一样收发数据。
UNIX Domain Sockets
UNIX Domain Sockets(UNIX 域套接字)是同一台机器上进程间通信(IPC)的一种机制,与网络套接字(TCP/IP)不同,它不经过网络协议栈,效率更高。
核心特点
| 特性 |
说明 |
| 通信范围 |
仅限同一主机内的进程之间 |
| 传输方式 |
通过文件系统路径标识(如 /tmp/mysql.sock) |
| 速度 |
比 TCP/IP 快 2-3 倍(无需经过内核网络栈) |
| 安全性 |
依赖文件权限控制,天然隔离外部网络 |
在netstat 输出中
unix 2 [ ACC ] STREAM LISTENING 776170 144612/PM2 v6.0.14: /root/.pm2/pub.sock
unix 2 [ ACC ] STREAM LISTENING 776171 144612/PM2 v6.0.14: /root/.pm2/rpc.sock
| 字段 |
含义 |
unix |
套接字类型为 UNIX Domain Socket |
[ ACC ] |
正在监听(Accepting connections) |
STREAM |
面向连接的流式套接字(类似 TCP) |
/root/.pm2/pub.sock |
套接字文件路径 |
/root/.pm2/rpc.sock |
PM2 内部 RPC 通信通道 |
这是 PM2 进程管理器的内部通信机制:
pub.sock → 发布/订阅消息总线
rpc.sock → 远程过程调用接口
- PM2 用它们管理 Node.js 工作进程
常见应用场景
| 服务 |
UNIX Socket 路径示例 |
用途 |
| MySQL/MariaDB |
/var/run/mysqld/mysqld.sock |
本地客户端连接 |
| PHP-FPM |
/run/php/php8.1-fpm.sock |
Nginx 与 PHP 通信 |
| Docker |
/var/run/docker.sock |
CLI 与守护进程通信 |
| Systemd |
/run/systemd/private |
系统服务管理 |
| PM2 |
~/.pm2/*.sock |
进程管理内部通信 |
UNIX Socket vs TCP/IP 对比
| 对比项 |
UNIX Domain Socket |
TCP/IP Socket |
| 地址格式 |
文件路径(如 /tmp/app.sock) |
IP:Port(如 127.0.0.1:8080) |
| 跨主机 |
❌ 不支持 |
✅ 支持 |
| 性能 |
⚡ 更高(数据复制次数少) |
较低 |
| 安全 |
文件权限即可控制 |
需防火墙、TLS 等 |
| 配置 |
需确保路径可访问 |
需端口未被占用 |
实际使用示例
# MySQL 优先使用 UNIX Socket 连接(更快)
mysql -S /var/run/mysqld/mysqld.sock -u root -p
# 对比 TCP 方式
mysql -h 127.0.0.1 -P 3306 -u root -p
# Nginx 配置中常见用法
upstream php-fpm {
server unix:/run/php/php8.1-fpm.sock; # 推荐
# server 127.0.0.1:9000; # 备选
}
总结
UNIX Domain Sockets 是单机版的高效进程通信管道,通过文件系统路径寻址,避免了 TCP/IP 的网络开销。你的 PM2 用它做内部指挥调度,MySQL/Nginx 等也常用它做本地加速通道。
Windows 和 Linux 都支持 Socket,但实现方式和API有所不同。
跨平台支持对比
| 特性 |
Linux/Unix |
Windows |
| Socket API |
BSD Socket(原生) |
Winsock(Windows Sockets) |
| 头文件 |
<sys/socket.h> |
<winsock2.h> |
| 初始化 |
直接使用 |
需先 WSAStartup() |
| 关闭方式 |
close() |
closesocket() |
| 错误处理 |
errno |
WSAGetLastError() |
| UNIX Domain Socket |
✅ 原生支持 |
❌ 不支持(Windows 10 1803+ 有限支持) |
代码对比示例
Linux 版本
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
int main() {
int sock = socket(AF_INET, SOCK_STREAM, 0); // 创建TCP套接字
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
bind(sock, (struct sockaddr*)&addr, sizeof(addr));
listen(sock, 5);
int client = accept(sock, NULL, NULL);
// ... 读写数据 ...
close(sock); // 关闭
return 0;
}
Windows 版本
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib") // 链接库
int main() {
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData); // 必须初始化
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
bind(sock, (struct sockaddr*)&addr, sizeof(addr));
listen(sock, 5);
SOCKET client = accept(sock, NULL, NULL);
// ... 读写数据 ...
closesocket(sock); // Windows专用关闭
WSACleanup(); // 清理Winsock
return 0;
}
关键差异详解
1. 初始化要求
| 平台 |
要求 |
| Linux |
直接调用 socket() 即可 |
| Windows |
必须先调用 WSAStartup() 初始化 Winsock 库 |
2. 数据类型
| Linux |
Windows |
说明 |
int |
SOCKET |
Windows 专用类型(实际也是整数) |
ssize_t |
int |
返回值类型 |
3. 关闭与清理
// Linux
close(sock);
// Windows
closesocket(sock); // 专用函数
WSACleanup(); // 清理资源
现代解决方案:跨平台库
实际开发中很少直接写原生 Socket,常用跨平台封装:
| 库/框架 |
语言 |
特点 |
| Boost.Asio |
C++ |
工业标准,异步高性能 |
| libuv |
C |
Node.js 底层,事件驱动 |
| Qt Network |
C++ |
集成 Qt 生态,信号槽机制 |
| Python socket |
Python |
标准库,自动屏蔽平台差异 |
| Go net |
Go |
原生跨平台,goroutine 友好 |
| Java NIO |
Java |
统一 API,JVM 屏蔽底层 |
Python 示例(完全跨平台)
import socket
# 同一套代码,Linux/Windows/Mac 都能运行
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('0.0.0.0', 8080))
sock.listen(5)
client, addr = sock.accept()
data = client.recv(1024)
client.send(b'Hello')
sock.close()
关于 UNIX Domain Socket
| 平台 |
支持情况 |
| Linux/macOS/Unix |
✅ 完整原生支持 |
| Windows 10 (1803+) |
⚠️ 有限支持(需 WSL 或特定 API) |
| 传统 Windows |
❌ 不支持 |
Windows 传统上使用 命名管道(Named Pipes) 实现类似功能。
总结
| 问题 |
答案 |
| Socket 概念跨平台吗? |
✅ 是,概念完全一致 |
| API 完全相同吗? |
❌ 否,Windows 需 Winsock 封装 |
| 现代开发怎么做? |
使用高级语言或跨平台库,避免直接操作原生 API |
Socket 是操作系统提供的标准通信机制,虽然底层实现不同,但核心概念(创建-绑定-监听-连接-收发-关闭)在所有平台都一致。