Socket(套接字) 是操作系统提供给 进程 使用网络协议栈的端点抽象。应用程序通过 socket 绑定本地地址、连接远端、收发数据,而具体的 TCPUDP、缓冲区和协议状态由内核维护。

核心问题

网络协议栈在内核里,应用进程不能直接操作网卡包队列。Socket 解决的是“进程如何用统一的文件/句柄接口访问网络通信端点”的问题。

它不是协议本身,也不是端口本身。Socket 更像一个内核对象加一个应用可持有的句柄:进程通过文件描述符调用 read/write/send/recv,内核根据 socket 的协议类型和状态决定如何收发数据。

核心机制

创建 socket 时通常要指定三个维度:

socket(domain, type, protocol)
参数例子含义
domainAF_INET, AF_INET6, AF_UNIX地址族:IPv4、IPv6、Unix Domain Socket
typeSOCK_STREAM, SOCK_DGRAM通信语义:字节流或数据报
protocolTCP, UDP 或 0具体协议,常让内核按 type 推断

一个网络 socket 常见状态包括:

字段含义
local address本地 IP 地址
local port本地 端口
peer address对端 IP 地址,TCP 连接后通常确定
peer port对端端口
protocolTCP、UDP 或其他协议
buffers内核发送/接收缓冲区
stateTCP 的 LISTEN、ESTABLISHED、TIME_WAIT 等状态,或 UDP 的绑定状态

TCP 服务端典型流程是:

listen_fd = socket(AF_INET, SOCK_STREAM, 0)
bind(listen_fd, local_ip, local_port)
listen(listen_fd)
 
conn_fd = accept(listen_fd)
read(conn_fd)
write(conn_fd)
close(conn_fd)

这里有两个不同 socket:

  • listen_fd 是监听 socket,只负责接收新连接。
  • conn_fdaccept() 返回的已连接 socket,对应某个具体客户端四元组。

TCP 客户端典型流程是:

fd = socket(AF_INET, SOCK_STREAM, 0)
connect(fd, server_ip, server_port)
write(fd)
read(fd)
close(fd)

UDP 服务端没有 listen()accept()

fd = socket(AF_INET, SOCK_DGRAM, 0)
bind(fd, local_ip, local_port)
recvfrom(fd)
sendto(fd, peer_ip, peer_port)

因为 UDP 没有连接握手,内核通常按每个数据报携带的源地址和源端口区分发送方。

工程用途

  • 网络服务端:Web 服务器、数据库、消息队列通过监听 socket 接受连接。
  • 网络客户端:浏览器、CLI、SDK 用 socket 连接服务端并收发字节流或数据报。
  • 本机 IPC:Unix Domain Socket 可用于同一台机器上的进程通信,常见于 Docker、数据库和守护进程。
  • 高并发 I/O:事件循环通过 selectpollepollkqueue 等机制监控大量 socket 是否可读可写。

观察指标包括文件描述符数量、监听 backlog、accept 队列、连接状态分布、读写缓冲区大小、阻塞/非阻塞模式、超时设置和错误码。

边界与常见坑

  • socket 不是连接的同义词:UDP socket 可以没有连接;TCP 监听 socket 也不是某个客户端连接。
  • socket 不是端口:多个 socket 可能与同一端口有关,但端口只是传输层编号,socket 是内核通信对象。
  • 一次写不等于一次读:TCP socket 暴露字节流,应用层要自己定义消息边界。
  • 非阻塞 I/O 要处理部分读写send() 写入的字节数可能小于请求字节数,recv() 也可能只读到部分数据。
  • 关闭不等于对端立刻知道:TCP close、shutdown、FIN、RST 和 TIME_WAIT 都有不同语义。
  • 文件描述符泄漏会拖垮服务:忘记 close、连接空闲不清理、accept 太慢都会耗尽资源。

相关术语