Java Socket 编程复习总结
·
一、核心类与职责
| 类 | 用途 | 关键方法 |
|---|---|---|
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、视频流、游戏 |
五、常见陷阱
- 忘记释放资源 → 用 try-with-resources(Java 7+)自动关闭
- readLine() 读到 null → 表示对端关闭了连接,要退出循环
- read() 不保证一次读完 → 实际长度用返回值,循环读
- 端口被占用 → 重启服务器前用
setReuseAddress(true) - 单线程服务器 → 多个客户端会串行排队,必须多线程/线程池处理
- Nagle 算法延迟 → 小包会被合并发送,延迟敏感的用
setTcpNoDelay(true)
六、演进路线
BIO(阻塞 I/O,传统 Socket)
↓ 每个连接一个线程,线程开销大
NIO(New I/O,Channel + Buffer + Selector)
↓ 一个线程管理多个连接(多路复用)
AIO(异步 I/O,回调方式)
↓
Netty(高性能网络框架,封装 NIO)
更多推荐


所有评论(0)