一、核心类与职责

用途 关键方法
ServerSocket 服务器端监听端口 accept() 阻塞等待连接
Socket 客户端发起连接 / 服务端与客户端通信 getInputStream() getOutputStream()
DatagramSocket UDP 收发数据报 send() receive()
DatagramPacket UDP 数据报的载体 getData() getAddress() getPort()

二、TCP 通信流程图

服务器端                          客户端
   │                                │
   │ new ServerSocket(8888)         │
   │        ↓                       │
   │   accept() 阻塞等待             │
   │        ↓                       │
   │   收到连接 ←─── 三次握手 ────  new Socket("127.0.0.1", 8888)
   │        ↓                       │        ↓
   │   返回 Socket                  │   获得 Socket
   │        ↓                       │        ↓
   │   getInputStream()  ←─── 数据流 ──→ getOutputStream()
   │   getOutputStream() ──── 数据流 ──→ getInputStream()
   │        ↓                       │        ↓
   │   socket.close()  ←── 四次挥手 ── socket.close()

三、3个必须理解的关键概念

1. 阻塞 I/O(BIO)模型

  • accept() 阻塞 → 没有连接时线程挂起
  • read() 阻塞 → 没有数据时线程挂起
  • 每个连接需要独立的线程 → "一连接一线程"模式
  • 这是 Java 传统网络编程的默认模型

2. TCP 流式传输的"粘包/拆包"问题

TCP 是流协议,不保留消息边界。比如:

  • 发送方发了两次 “hello” 和 “world”
  • 接收方可能一次读到 “helloworld”(粘包)
  • 也可能分三次读到 “hel” “lowor” “ld”(拆包)

解决方案:

  • 定长消息
  • 分隔符(如换行符 \n,回显服务器用的就是这种)
  • 消息头+消息体(头里写长度)

3. UDP 的"数据报边界"保证

UDP 保留消息边界,一个 send() 对应一个 receive(),没有粘包问题。
代价:不可靠、不保证送达、不保证顺序。

四、TCP vs UDP 对比

特性 TCP UDP
连接 面向连接(三次握手) 无连接
可靠性 可靠(确认/重传/排序) 不可靠
顺序 保证有序 不保证
边界 流式(无边界) 数据报(有边界)
开销 头部20字节 + 连接管理 头部仅8字节
适用 HTTP、FTP、数据库 DNS、视频流、游戏

五、常见陷阱

  1. 忘记释放资源 → 用 try-with-resources(Java 7+)自动关闭
  2. readLine() 读到 null → 表示对端关闭了连接,要退出循环
  3. read() 不保证一次读完 → 实际长度用返回值,循环读
  4. 端口被占用 → 重启服务器前用 setReuseAddress(true)
  5. 单线程服务器 → 多个客户端会串行排队,必须多线程/线程池处理
  6. Nagle 算法延迟 → 小包会被合并发送,延迟敏感的用 setTcpNoDelay(true)

六、演进路线

BIO(阻塞 I/O,传统 Socket)
  ↓  每个连接一个线程,线程开销大
NIO(New I/O,Channel + Buffer + Selector)
  ↓  一个线程管理多个连接(多路复用)
AIO(异步 I/O,回调方式)
  ↓
Netty(高性能网络框架,封装 NIO)

更多推荐