Socket

返回 计算机网络

Socket(套接字)是操作系统提供的网络通信抽象接口,应用程序通过 Socket API 发送和接收数据,底层可基于 TCP 或 UDP。


Socket 与协议的关系

应用程序
    │  Socket API(send / recv)
    ▼
操作系统内核
    ├── TCP Socket  →  TCP  →  IP  →  网卡
    └── UDP Socket  →  UDP  →  IP  →  网卡

Socket 是应用层与传输层之间的编程接口,屏蔽底层协议细节。


TCP Socket 通信流程

服务端                          客户端
socket()                        socket()
bind(ip, port)
listen()
accept() ◄─────────────────── connect(server_ip, port)
    │           三次握手完成         │
recv() ◄──────── send(data) ─────────┤
send(data) ──────────────────► recv()
close()                         close()

Java 示例

TCP 服务端

ServerSocket serverSocket = new ServerSocket(8080);
Socket socket = serverSocket.accept();  // 阻塞等待客户端
 
BufferedReader in = new BufferedReader(
    new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
 
String line;
while ((line = in.readLine()) != null) {
    out.println("Echo: " + line);
}
socket.close();

TCP 客户端

Socket socket = new Socket("127.0.0.1", 8080);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
    new InputStreamReader(socket.getInputStream()));
 
out.println("Hello Server");
String response = in.readLine();
socket.close();

Python 示例

TCP 服务端

import socket
 
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('0.0.0.0', 8080))
server.listen(5)
 
conn, addr = server.accept()
data = conn.recv(1024)
conn.send(b'Echo: ' + data)
conn.close()

UDP

# 发送方
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(b'Hello', ('127.0.0.1', 9090))
 
# 接收方
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('0.0.0.0', 9090))
data, addr = sock.recvfrom(1024)

常用 Socket 选项

选项说明
SO_REUSEADDR允许重用 TIME_WAIT 端口,服务重启时常用
SO_KEEPALIVE开启 TCP 保活探测,检测连接是否存活
TCP_NODELAY禁用 Nagle 算法,减少小数据包延迟
SO_TIMEOUT设置 recv 超时时间
SO_SNDBUF / SO_RCVBUF调整发送/接收缓冲区大小

BIO / NIO / AIO

模型说明Java 对应
BIO(阻塞 IO)每个连接一个线程,连接多时线程开销大Socket / ServerSocket
NIO(非阻塞 IO)单线程通过 Selector 管理多路连接java.nio、Netty
AIO(异步 IO)操作系统完成 IO 后回调通知AsynchronousSocketChannel

高并发场景推荐 NIO 框架(Netty),避免 BIO 的线程资源浪费。


粘包与拆包

TCP 是字节流协议,无消息边界,可能出现:

  • 粘包:多个小包合并为一次 recv
  • 拆包:一个大包分多次 recv 到达

常用解决方案:

方案说明示例
固定长度每条消息固定 N 字节简单协议
分隔符用特定字符(\n\0)标记消息结尾Redis 协议、HTTP
长度前缀头部 4 字节写入消息体长度,先读头再读体Netty LengthFieldBasedFrameDecoder
自定义协议帧魔数 + 版本 + 消息类型 + 长度 + 体Dubbo、自研 RPC
┌──────────┬──────────┬──────────┬──────────────┐
│  Magic   │ Version  │   Type   │   Length  Body│
│  2 bytes │ 1 byte   │ 1 byte   │   4 bytes  ... │
└──────────┴──────────┴──────────┴──────────────┘

IO 多路复用

单线程同时监听多个 Socket,避免 BIO 每连接一线程的开销:

机制系统说明上限
select跨平台遍历 fd_set,O(n)1024 fd
pollLinux/Mac链表替代数组,O(n)无硬限制
epollLinux事件驱动,O(1),边缘/水平触发百万级 fd
kqueuemacOS/BSD类似 epoll百万级 fd
IOCPWindows真异步完成端口

epoll 两种触发模式:

  • LT(水平触发,默认):只要缓冲区有数据就持续通知,未读完下次仍通知,易用
  • ET(边缘触发):只在状态变化时通知一次,必须一次性读完,性能更高但编程复杂

TCP 连接状态

LISTEN → SYN_RCVD → ESTABLISHED → FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT → CLOSED
                                 ↑
CLOSED → SYN_SENT → ESTABLISHED → CLOSE_WAIT → LAST_ACK → CLOSED

TIME_WAIT:主动关闭方在 2MSL(约 60s)内保持,防止最后 ACK 丢失重传。大量 TIME_WAIT 说明服务端主动关闭连接,可开启 SO_REUSEADDRnet.ipv4.tcp_tw_reuse

CLOSE_WAIT:被动关闭方收到 FIN 后未调用 close(),通常是代码 bug(连接泄漏)。


TCP 半关闭

close() 关闭读写两个方向;shutdown() 可单独关闭一个方向:

socket.shutdownOutput();  // 发送 FIN,告知对端我不再发数据,但仍可接收

用于:客户端写完数据后半关闭,等待服务端处理完毕再返回结果,再全关闭。


Unix Domain Socket

同一台机器的进程间通信,使用文件路径而非 IP:Port,性能优于 TCP(无需 TCP/IP 协议栈):

# 服务端
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
server.bind('/tmp/myapp.sock')
server.listen(5)
 
# 客户端
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
client.connect('/tmp/myapp.sock')

常见场景:Nginx 与 PHP-FPM、Docker daemon、MySQL 本地连接。


心跳机制

长连接空闲时,中间路由/防火墙可能断开连接而两端不知晓:

方案说明
TCP KeepAlive操作系统级,默认 2 小时探测,可调整(SO_KEEPALIVE + TCP_KEEPIDLE
应用层心跳每隔 N 秒发送 Ping 包,对端回 Pong;超时未回视为断线重连

应用层心跳更可靠(能检测到应用假死),推荐在 Netty 等框架中实现。


相关

  • TCP — TCP socket
  • UDP — UDP socket
  • IP与路由 — Socket 通信需要 IP 地址与端口
  • WebSocket — 应用层协议,与 TCP Socket 不同层次
  • 网络通信 — Socket 是网络通信的底层编程接口
  • 网络安全 — Socket 层面的安全加固(TLS、访问控制)