☕ 第十三章 网络编程
📄 一句话概括

网络编程 = 拿着 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 是两套拳——打电话稳但慢(聊天、传文件),寄快递快但可能丢(直播、游戏)。


二、概念速查

  1. IP:找到哪台电脑;127.0.0.1(localhost)永远是本机
  2. 端口:找到电脑里哪个程序,0~65535,避开 1024 以下;3306 = MySQL、80/443 = 网页
  3. 协议:数据的寄送规矩;丢得起选 UDP,丢不起选 TCP
  4. Socket:IP + 端口组成的"插座",通信两端;new Socket(...) 这一行当场联网
  5. accept():服务端堵在门口等连接,阻塞;一次只接一个,常驻要 while(true) + 多线程
  6. read()/receive() 阻塞:没数据/没包裹就停在那行干等,不是死机
  7. read 返回 -1:对方 shutdownOutput() 或 close(),"说完了"的哨兵信号
  8. shutdownOutput() vs close():只关嘴(还想听)vs 整个挂电话
  9. DatagramPacket 两种构造:发件带地址四件套(内容+长度+IP+端口),收件只给空箱
  10. packet.getLength():实际装了多少(≠箱子大小 cart.length),拆包裹必用
  11. getSocketAddress():查寄件人,回信地址现成贴
  12. 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 章的多线程在这儿上岗,也就是"多客户端聊天室"的全部秘密。


五、收官暗线:网络编程没有新知识

本章的每一样"新"东西,拆开都是老朋友:

一句话:你已经会写信了,这章只是教你寄出去。


六、毕业大题:多人聊天室雏形(预习版)

不用等下一章,现在就能拼一个"多客户端"服务端的骨架(每一步都是学过的):

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 项):

  1. while(true) 包住 accept(),服务端能连续接客
  2. 每条专线一个线程,客户端 A 卡住不影响客户端 B
  3. 收消息循环以 readLine() == null(-1 哨兵)收线
  4. 客户端发完用 shutdownOutput() 而不是 close()(半关实验再跑一遍)
  5. try-with-resources 管 Socket 关闭(5.3 的姿势)
  6. 同时开两个客户端 Run,消息互不干扰地打印——跑通它,你就真的从"学 Java"走进"写后端"了

下一步学什么


相关笔记