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 是操作系统提供的标准通信机制,虽然底层实现不同,但核心概念(创建-绑定-监听-连接-收发-关闭)在所有平台都一致。