📄 一句话概括
网络编程 = 拿着 IO 的内功,按协议规矩把数据寄出去——TCP 靠专线稳、UDP 靠包裹快,两套拳各打各的场子。
第十三章三节正课全部收官。这章最妙的地方在于:几乎没有新知识——IO 是第十章的、阻塞哨兵是 10.2 的、flush 是 10.3 的、线程是第十二章的,网络编程只是给这些老朋友换了块新场地。来串一遍线。
一、三节串线
| 节 | 一句话 | 关键角色 |
|---|---|---|
| 13.1 网络基础 | 寄快递的三要素:IP 楼牌号 + 端口房间号 + 协议快递规则 | InetAddress 探路 |
| 13.2 TCP通信 | 打电话:先三次握手拉专线,再你写我读 | ServerSocket(开门)+ Socket(专线)+ IO 流 |
| 13.3 UDP通信 | 寄快递:不握手,地址贴包裹上,打包就扔 | DatagramSocket(快递员)+ DatagramPacket(包裹) |
主线一句话:13.1 定规矩,13.2/13.3 是两套拳——打电话稳但慢(聊天、传文件),寄快递快但可能丢(直播、游戏)。
二、概念速查
- IP:找到哪台电脑;
127.0.0.1(localhost)永远是本机 - 端口:找到电脑里哪个程序,0~65535,避开 1024 以下;3306 = MySQL、80/443 = 网页
- 协议:数据的寄送规矩;丢得起选 UDP,丢不起选 TCP
- Socket:IP + 端口组成的"插座",通信两端;
new Socket(...)这一行当场联网 - accept():服务端堵在门口等连接,阻塞;一次只接一个,常驻要
while(true)+ 多线程 - read()/receive() 阻塞:没数据/没包裹就停在那行干等,不是死机
- read 返回 -1:对方
shutdownOutput()或close(),"说完了"的哨兵信号 - shutdownOutput() vs close():只关嘴(还想听)vs 整个挂电话
- DatagramPacket 两种构造:发件带地址四件套(内容+长度+IP+端口),收件只给空箱
- packet.getLength():实际装了多少(≠箱子大小
cart.length),拆包裹必用 - getSocketAddress():查寄件人,回信地址现成贴
- Connection refused:TCP 拨号没人接才报错;UDP 丢件一声不吭
三、代码模板(4 段)
① TCP 服务端骨架:
ServerSocket server = new ServerSocket(8888);
Socket socket = server.accept(); // 阻塞等客
BufferedReader br = new BufferedReader(
new InputStreamReader(socket.getInputStream())); // 读
PrintStream ps = new PrintStream(socket.getOutputStream(), true); // 写(自动 flush)
// ... br.readLine() 收,ps.println(...) 发
socket.close(); server.close();
② TCP 客户端骨架:
Socket socket = new Socket("127.0.0.1", 8888); // 拨号 + 三次握手
ps.println("客户端的话"); // 发完别急着 close!
socket.shutdownOutput(); // 只关嘴不关耳朵
String reply = br.readLine(); // 还能听
③ UDP 发送骨架:
DatagramSocket socket = new DatagramSocket();
DatagramPacket packet = new DatagramPacket(data, data.length,
InetAddress.getByName("127.0.0.1"), 8888); // 地址贴包裹上
socket.send(packet);
socket.close();
④ UDP 接收骨架:
DatagramSocket socket = new DatagramSocket(8888); // 占房等件
byte[] cart = new byte[1024];
DatagramPacket packet = new DatagramPacket(cart, cart.length);
socket.receive(packet); // 阻塞等包裹
String msg = new String(cart, 0, packet.getLength()); // 按实际长度拆
四、名坑回顾(本章五大坑)
🚨 坑一:TCP 先跑客户端 → Connection refused
不是代码错,是启动顺序错。先服务端后客户端,两边各点一次 Run。
🚨 坑二:UDP 不报错 = 发到了
UDP 丢件一声不吭。收不到先查:接收方先启动了吗?端口一致吗?——别盯着发送方代码改。
🚨 坑三:客户端发完就 `close()`,收不到服务端回话
close 是整个挂断。想边说边听:发完 shutdownOutput()(只关嘴),close 留到最后。
🚨 坑四:`new String(cart)` 不带长度 → 一串空白尾巴
箱子 1024、包裹 11 字节,必须 new String(cart, 0, packet.getLength())——10.2 的老账,网络流里照犯不误。
🚨 坑五:以为 accept/receive 收一个就常驻
一次只接一个、只收一件。服务端要 while(true){ accept(); 新线程干活 }——12 章的多线程在这儿上岗,也就是"多客户端聊天室"的全部秘密。
五、收官暗线:网络编程没有新知识
本章的每一样"新"东西,拆开都是老朋友:
- 读写网络 = 读写流:
getInputStream/getOutputStream与文件流用法一字不差(10.2) - 阻塞与哨兵:
accept/receive卡住等货、read返回 -1,全是 10.2 的脾气(13.2) - flush 与字符流套餐:
BufferedReader + PrintStream原班人马(10.3) - 受检异常:
UnknownHostException「怪外部环境」判定法第 N 次验证(5.2) - 下一站已铺轨:一次 accept 只接一个 → 要并发 → 多线程(12.2)→ 再往后是线程池、Web 后端
一句话:你已经会写信了,这章只是教你寄出去。
六、毕业大题:多人聊天室雏形(预习版)
不用等下一章,现在就能拼一个"多客户端"服务端的骨架(每一步都是学过的):
ServerSocket server = new ServerSocket(8888);
while (true) { // 常驻接客
Socket socket = server.accept(); // 来一个接一个
new Thread(() -> { // 12.2 的 Lambda 线程上岗
try (socket) {
BufferedReader br = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
String msg;
while ((msg = br.readLine()) != null) { // -1 哨兵收线
System.out.println("[" + socket.getRemoteSocketAddress()
+ "] 说:" + msg);
}
} catch (IOException e) { e.printStackTrace(); }
}).start();
}
自检清单(6 项):
while(true)包住accept(),服务端能连续接客- 每条专线一个线程,客户端 A 卡住不影响客户端 B
- 收消息循环以
readLine() == null(-1 哨兵)收线 - 客户端发完用
shutdownOutput()而不是close()(半关实验再跑一遍) - try-with-resources 管 Socket 关闭(5.3 的姿势)
- 同时开两个客户端 Run,消息互不干扰地打印——跑通它,你就真的从"学 Java"走进"写后端"了
下一步学什么
- ☑ 14.1 Swing概述 ✅ —— 点此复习:黑框框终于有了脸——套娃结构与开窗五步
相关笔记
- 第十三章 网络编程/13.1 网络基础:三要素与 InetAddress——全章地基
- 第十三章 网络编程/13.2 TCP通信:打电话全套——握手、专线、流交叉对位
- 第十三章 网络编程/13.3 UDP通信:寄快递全套——包裹、地址、不握手
- 第十章 I-O/10.2 字节流:全章内功源头——阻塞、哨兵、小推车
- 第十二章 多线程/12.2 线程的创建:聊天室骨架里的
new Thread(() -> ...) - 第十一章 JDBC/11.4 数据库连接池:3306 端口与"连接不能共用"的老朋友