☰
第11章 网络编程:让程序聊起来
2026/11/1 1:51:50 网站建设 项目流程

第11章 网络编程:让程序聊起来

本章先在本机让两个独立程序通过 UDP 或 TCP 通信。地址、端口、编码、消息边界和结束方式都约定好,再扩展到多客户端和文件上传。

本章路线

  • 第1节 网络编程:让两台程序聊起来

1. 网络编程:让两台程序聊起来

同一程序中的方法可以直接调用,两个独立程序通信则要约好地址、端口和消息格式。UDP 像寄明信片;TCP 提供可靠字节流,但一条消息从哪里开始、到哪里结束,还得由应用自己规定。

在本机完成一次通信,再对照 UDP 数据报和 TCP 字节流,解释自己的消息边界是怎样设计的。

1. 网络编程概述

网络通信先约定层次和协议。应用写出去的“一个字符串”还要变成字节,接收端才能按同样规则解释。

网络编程:实现计算机之间数据交换的编程技术。

网络模型
  • OSI 七层模型:物理层 → 数据链路层 → 网络层 → 传输层 → 会话层 → 表示层 → 应用层
  • TCP/IP 四层模型:网络接口层 → 网际层 → 传输层 → 应用层

Java 主要关注传输层的 TCP 和 UDP 协议。

2. 网络编程三要素

地址决定找哪台主机,端口决定找哪个服务;传输协议也是约定的一部分。

  1. IP 地址:定位设备(哪个电脑)
  2. 端口号:定位进程(哪个程序),范围 065535,01023 为系统保留
  3. 协议:通信规则(TCP/UDP)
IP 地址
// 获取本机 IPInetAddresslocalHost=InetAddress.getLocalHost();System.out.println(localHost.getHostAddress());// 192.168.x.xSystem.out.println(localHost.getHostName());// 主机名// 根据域名获取 IPInetAddressbaidu=InetAddress.getByName("www.baidu.com");System.out.println(baidu.getHostAddress());// 判断是否能 ping 通System.out.println(baidu.isReachable(3000));
IPv4 vs IPv6
  • IPv4:32 位,4 字节,约 43 亿地址(已耗尽)
  • IPv6:128 位,16 字节,号称"地球上每粒沙子都能分一个 IP"
特殊 IP
  • 127.0.0.1/localhost:本机回环地址
  • 192.168.x.x:局域网私有地址
  • 0.0.0.0:本机所有 IP
端口号
  • 0~65535,同一台机器上端口不能冲突
  • 知名端口:HTTP(80)、HTTPS(443)、MySQL(3306)、Redis(6379)、SSH(22)

3. UDP 通信

UDP 保留数据报边界,但送达、顺序和重复问题要由应用按需处理。先在本机做最小收发,再谈聊天室。

User Datagram Protocol:用户数据报协议

特点
特性说明
面向无连接不需要建立连接,直接发
不可靠可能丢包、乱序
效率高无连接建立开销
数据报大小最多 64KB
UDP 发送端
// 1. 创建 DatagramSocket(无需指定端口,系统分配)DatagramSocketsocket=newDatagramSocket();// 2. 将数据打包byte[]data="你好,世界!".getBytes();InetAddressaddress=InetAddress.getByName("127.0.0.1");intport=8888;DatagramPacketpacket=newDatagramPacket(data,data.length,address,port);// 3. 发送socket.send(packet);// 4. 关闭socket.close();
UDP 接收端
// 1. 创建 DatagramSocket,绑定端口DatagramSocketsocket=newDatagramSocket(8888);// 2. 创建接收包(指定缓冲区大小)byte[]buf=newbyte[1024*64];// 最大 64KBDatagramPacketpacket=newDatagramPacket(buf,buf.length);// 3. 接收(阻塞等待)socket.receive(packet);// 4. 解析数据byte[]data=packet.getData();intlength=packet.getLength();Stringmessage=newString(data,0,length);System.out.println("收到: "+message);System.out.println("来自: "+packet.getAddress()+":"+packet.getPort());// 5. 关闭socket.close();
UDP 通信注意点
  1. 先启动接收端,再启动发送端
  2. 发和收是一对一的(一个端口对应一个接收端)
  3. DatagramPacket的缓冲区(buf.length)不要超过 64KB
UDP 实现聊天室
// 发送线程newThread(()->{DatagramSocketsocket=newDatagramSocket();Scannersc=newScanner(System.in);while(true){Stringmsg=sc.nextLine();if("exit".equals(msg))break;byte[]data=msg.getBytes();DatagramPacketpacket=newDatagramPacket(data,data.length,InetAddress.getByName("255.255.255.255"),8888);// 广播地址socket.send(packet);}socket.close();}).start();// 接收线程newThread(()->{DatagramSocketsocket=newDatagramSocket(8888);while(true){byte[]buf=newbyte[1024];DatagramPacketpacket=newDatagramPacket(buf,buf.length);socket.receive(packet);Stringmsg=newString(packet.getData(),0,packet.getLength());System.out.println(packet.getAddress()+": "+msg);}}).start();

4. TCP 通信

TCP 提供有序可靠的字节流,应用仍需定义长度、分隔符等消息边界。read 返回一次,并不等于收到一条完整业务消息。

Transmission Control Protocol:传输控制协议

特点
特性说明
面向连接需要三次握手建立连接
可靠传输确认重传、按序到达
效率较低有连接建立和维护开销
无数据大小限制以流的形式传输
TCP 客户端
// 1. 创建 Socket,连接服务器(IP + 端口)Socketsocket=newSocket("127.0.0.1",9999);// 2. 获取输出流,发送数据OutputStreamos=socket.getOutputStream();os.write("你好,服务器!".getBytes());// 3. 获取输入流,接收响应InputStreamis=socket.getInputStream();byte[]buf=newbyte[1024];intlen=is.read(buf);System.out.println("服务器响应: "+newString(buf,0,len));// 4. 关闭socket.close();
TCP 服务端
// 1. 创建 ServerSocket,绑定端口ServerSocketserverSocket=newServerSocket(9999);// 2. 等待客户端连接(阻塞)Socketsocket=serverSocket.accept();// 3. 获取输入流,读取数据InputStreamis=socket.getInputStream();byte[]buf=newbyte[1024];intlen=is.read(buf);System.out.println("收到: "+newString(buf,0,len));// 4. 获取输出流,回写响应OutputStreamos=socket.getOutputStream();os.write("收到,谢谢!".getBytes());// 5. 关闭socket.close();serverSocket.close();
TCP 通信注意点
  1. 必须先启动服务端,再启动客户端
  2. accept()是阻塞方法,等待客户端连接
  3. read()也是阻塞的,直到数据到达或流关闭
  4. 需要约定结束标记(如 shutdownOutput / 约定 \n / 发送特殊字符串)
避免 read() 阻塞的问题
// 方案 1:发送方关闭输出流(通知接收方结束)socket.shutdownOutput();// 发送完数据后调用// 方案 2:约定结束标记// 发送方:每行末尾加 \n// 接收方:用 BufferedReader.readLine() 按行读取// 方案 3:先发送数据长度// 发送:先写 4 字节表示数据长度,再写数据体// 接收:先读前 4 字节得到长度,再读指定字节数
TCP 多线程服务端
ServerSocketserverSocket=newServerSocket(9999);while(true){Socketsocket=serverSocket.accept();// 每个客户端开一个线程处理newThread(()->{try{InputStreamis=socket.getInputStream();OutputStreamos=socket.getOutputStream();// 处理通信...socket.close();}catch(IOExceptione){e.printStackTrace();}}).start();}
TCP 实现文件上传
// 客户端Socketsocket=newSocket("127.0.0.1",10000);FileInputStreamfis=newFileInputStream("test.jpg");OutputStreamos=socket.getOutputStream();byte[]buf=newbyte[8192];intlen;while((len=fis.read(buf))!=-1){os.write(buf,0,len);}socket.shutdownOutput();// 告诉服务器文件发完了// 接收响应InputStreamis=socket.getInputStream();intlen2=is.read(buf);System.out.println(newString(buf,0,len2));fis.close();socket.close();// 服务端ServerSocketss=newServerSocket(10000);Socketsocket=ss.accept();BufferedInputStreambis=newBufferedInputStream(socket.getInputStream());BufferedOutputStreambos=newBufferedOutputStream(newFileOutputStream(System.currentTimeMillis()+".jpg"));byte[]buf=newbyte[8192];intlen;while((len=bis.read(buf))!=-1){bos.write(buf,0,len);}OutputStreamos=socket.getOutputStream();os.write("上传成功".getBytes());bos.close();socket.close();ss.close();
三次握手与四次挥手

三次握手(建立连接):

  1. 客户端 → 服务器:SYN(同步位),请求连接
  2. 服务器 → 客户端:SYN + ACK,确认收到,我也发连接
  3. 客户端 → 服务器:ACK,确认收到,连接建立

四次挥手(断开连接):

  1. 客户端 → 服务器:FIN,我要断开了
  2. 服务器 → 客户端:ACK,收到断开请求
  3. 服务器 → 客户端:FIN,我也要断开了
  4. 客户端 → 服务器:ACK,收到,连接断开

5. UDP vs TCP 对比

选择协议时,先确认业务是否允许丢失、是否要求有序、怎样看待传输开销,再比较两者。

对比项UDPTCP
连接无连接面向连接(三次握手)
可靠性不可靠,可能丢包可靠传输
速度快相对慢
数据量64KB 上限无限制(流式)
适用场景视频/语音通话、DNS、游戏文件传输、网页、邮件

6. BS 架构(浏览器-服务器)

浏览器和服务器通信通常还遵循 HTTP 等应用层协议。底层能接收字节,只是搭好了进一步理解 Web 的基础。

本质也是 TCP,浏览器充当客户端。

// 简易 BS 服务器ServerSocketss=newServerSocket(8080);Socketsocket=ss.accept();// 读取 HTTP 请求BufferedReaderbr=newBufferedReader(newInputStreamReader(socket.getInputStream()));Stringline;while((line=br.readLine())!=null&&!line.isEmpty()){System.out.println(line);}// 返回 HTTP 响应PrintWriterpw=newPrintWriter(socket.getOutputStream());pw.println("HTTP/1.1 200 OK");pw.println("Content-Type: text/html;charset=utf-8");pw.println();pw.println("<h1>Hello Browser!</h1>");pw.flush();socket.close();ss.close();

多线程 BS 服务器:每个请求一个线程,返回静态资源(HTML/CSS/JS/图片)。

7. 总结

给发送、读取、结束和超时各写一句约定,才能让两端都正常收工。

知识点核心要点
三要素IP(设备)+ 端口(进程)+ 协议(UDP/TCP)
UDPDatagramSocket + DatagramPacket,无连接,64KB 上限
TCPSocket + ServerSocket,三次握手,流式可靠传输
阻塞问题read() 阻塞,用 shutdownOutput 或约定结束标记
文件上传TCP 流式传输 + 多线程处理
BS 架构TCP + HTTP 协议,浏览器当客户端

看图:TCP 给字节流,应用定句号

图中使用换行分隔只是一种应用协议,另一种常见做法是长度前缀。一次 write 与一次 read 没有一一对应关系;传送文件时还应约定长度、大小限制和结束方式。

小练习:把这一节变成自己的代码

做一个本机 TCP 回声服务,以一行作为消息边界;发送后能收到对应回复并正常结束。

卡住时再打开这条提示

行协议需要换行与刷新;别让双方都在等对方先关闭输出。

这一章怎样算学会了

把本机回声服务关掉重写一次。确认两端的消息边界一致,再测试正常发送、结束和超时的情况,别让程序一直等下去。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询