本节学习目标
- 理解多线程与网络编程之间的关联;
- 掌握网络程序开发的主要架构模式;
- 了解基于TCP协议的网络通信基本实现方式。
1. 网络编程的基本概念
在Java语言中,网络编程的核心目的在于实现不同主机之间的数据交换。为了简化这一过程,Java将其抽象为JVM(Java虚拟机)进程间的通信形式。即使在同一台物理设备上,也可以运行多个独立的JVM进程,这些进程在网络通信的语境下可被视为不同的主机节点。
图1 远程访问——JVM进程之间的交互示意图
每个JVM拥有独立的内存空间和系统资源,既可以在同一台机器上运行,也能分布于不同的物理设备中。借助Java提供的网络API,开发者能够在这些JVM之间建立连接,并进行数据的发送与接收操作。
采用JVM作为网络通信的基本单元具有显著优势。首先,它提供了更高层次的抽象模型,使网络通信的开发更加便捷高效。其次,当多个JVM运行在同一台主机时,它们之间的通信延迟更低,且可以共享部分本地资源,从而提升整体性能。
此外,在此前介绍的多线程编程内容中所涉及的并发机制,如线程调度、同步锁等技术,也为跨JVM的数据交互提供了安全保障,确保数据传输过程中的完整性与一致性。
不同JVM之间的数据访问本质上属于远程调用范畴。Java中的远程方法调用(RMI)以及企业级应用开发框架,正是基于此类通信机制构建而成。
Remote Method Invocation,RMI
JavaBean
Enterprise JavaBean, EJB
网络编程的本质是实现数据的双向交互。在此类通信过程中,通常会划分出两个角色:服务器端和客户端。根据实现方式的不同,主要存在以下两种典型架构模式:
C/S 架构(Client/Server)
该模式需要分别开发客户端和服务器端两套程序代码。由于两端都需要独立实现逻辑功能,因此在开发与后期维护方面成本较高。但其优点在于使用自定义端口和私有通信协议,安全性较强。
C/S架构下的网络通信依赖于两种基础传输协议:
TCP
TCP(传输控制协议):提供可靠的、面向连接的数据传输服务;
UDP
UDP(用户数据报协议):采用无连接方式,适用于对效率要求较高的场景。
B/S 架构(Browser/Server)
此模式无需单独开发客户端程序,仅需部署服务器端应用,用户通过浏览器即可完成访问。开发工作量较小,只需维护一套服务端代码。但由于使用公开的HTTP协议及默认80端口,安全防护能力相对较弱。
诸如
ASP
PHP
JSP
等技术均属于
B/S
的典型代表,这些技术的运行依赖特定服务器环境的支持。若要实现不同
B/S
组件间的互通,则必须借助
Web Service
相关技术支持。
关于 TCP 与 UDP 协议
TCP(传输控制协议) 提供的是可靠、面向连接的通信机制。在数据传输前,通信双方需通过“三次握手”建立稳定连接,确保链路通畅。TCP通过确认应答、超时重传、流量控制等机制保障数据不丢失、不重复、按序到达。
该协议适用于对数据完整性和顺序性要求较高的应用场景,例如网页请求(HTTP)、文件传输(FTP)、电子邮件等。
UDP(用户数据报协议) 则是一种无连接的通信协议,发送方无需事先建立连接即可直接发送数据包。由于省去了握手与断开连接的过程,UDP具备更低的延迟和更小的网络开销。
然而,UDP不保证数据的可靠性或顺序性,若发生丢包或乱序,需由应用程序自身处理恢复逻辑。因此,它更适合用于实时性优先、允许少量数据损失的场合,如在线音视频流、多人实时游戏、广播通信等。
在实际网络编程中,开发者可根据具体业务需求选择合适的传输层协议:
若强调数据的准确送达与顺序一致,则应选用
TCP
;
若追求低延迟并能接受一定程度的数据不可靠性,则可采用
UDP
。某些复杂系统甚至会同时集成两种协议,以分别处理关键数据与非关键实时数据。
2. Socket 与 ServerSocket 类详解
Java 中的
java.net
包封装了网络通信所需的核心工具类,其中最为关键的是以下两个类:
- ServerSocket:专用于服务器端,封装了TCP协议的监听与连接接收功能,负责等待客户端发起连接请求;
- Socket:表示一个客户端连接实例,同样基于TCP协议,用于与服务器进行数据读写操作。
以下是
ServerSocket
类常用方法说明:
| 方法名称 | 类型 | 描述 |
|---|---|---|
|
构造方法 | 创建一个绑定指定端口号的服务器监听实例,建议使用5000以上的端口以避免冲突 |
|
普通方法 | 阻塞式接收客户端连接请求,返回一个Socket对象用于后续通信 |
|
普通方法 | 关闭服务器监听,释放占用的端口资源 |
以下是
Socket
类的常用方法列表:
| 方法名称 | 类型 | 描述 |
|---|---|---|
|
构造方法 | 创建Socket实例并连接到指定IP地址和端口号的目标服务器 |
|
普通方法 | 获取输出流对象,通常包装为PrintStream以便发送字符串数据 |
|
普通方法 | 获取输入流对象,用于读取来自服务器的数据 |
Hello World”。该服务监听本机的9999端口,通过调用accept()方法开启监听,等待客户端接入。
一旦客户端成功建立TCP连接,服务器便通过PrintStream将数据输出至客户端。数据传输完成后,服务器自动关闭。因此,此版本的服务仅能处理单次客户端请求,不具备持续服务能力。运行过程中程序会出现阻塞现象,直到客户端连接才会继续执行,如下图所示:
图2 客户端与服务器端交互 从客户端角度出发,需使用
Socket类来发起连接请求。创建该类实例时,必须指定目标主机地址(如本地可用localhost或IP)和对应端口号。连接成功后,可通过Scanner获取输入流,从而读取来自服务器的数据响应。
服务器端接收到客户端连接后,会为其分配一个对应的Socket对象,代表当前会话的客户端实体。借助该对象,服务器可分别取得输入流与输出流,实现双向通信。为了提升IO效率,通常会对流进行包装处理,例如结合Scanner与PrintStream完成便捷的数据读写操作。
接下来介绍一个典型的网络编程实践案例——Echo回显程序。Echo模型在网络通信中具有代表性:用户在客户端任意输入内容并发送至服务器,服务器接收后添加前缀“ECHO:”再原路返回。为支持多次交互,服务不会在一次通信后立即关闭,而是持续运行,直至客户端发送特定终止指令(如“byebye”)才结束会话。
为实现上述功能,服务器端在接收到客户端连接后,立即获取其输入流和输出流,并利用while循环不断监听新消息、处理并回传结果。同样地,客户端也采用while循环控制输入行为,每输入一行文本即发送给服务器,除非内容为“byebye”,否则服务将持续响应。
尽管范例实现了基本的双端通信,但存在明显局限:它只能顺序处理一个客户端请求,无法并发响应多个连接。原因在于所有逻辑均运行于主线程中,属于典型的单线程架构。实际应用场景中,服务器往往需要同时服务大量客户端,为此应引入多线程机制。
如图所示,通过为每个新连接启动独立线程,可有效实现并发处理:
图3 多线程优化网络编程 具体实现中,每当有新的客户端接入,服务器便创建一个新线程专门负责该客户端的读写操作。主服务线程继续监听其他连接请求,互不干扰。这一设计显著提升了系统的并发能力与响应效率。
// 范例 5:修改服务器端
package com.xiaoshan.demo;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
class EchoThread implements Runnable {
//建立线程类
private Socket client; //每个线程处理一个客户端
public EchoThread(Socket client){
//创建线程对象时传递 Socket
this.client = client;
}
@Override
public void run(){
try {
//每个线程对象取得各自Socket的输入流与输出流
Scanner scan = new Scanner(client.getInputStream());
PrintStream out = new PrintStream(client.getOutputStream());
boolean flag = true;
while(flag){
//控制多次接收操作
if(scan.hasNext()){
//是否有内容
String str = scan.next().trim(); //得到客户端发送的内容
if (str.equalsIgnoreCase("byebye")){
// 程序结束
out.println("拜拜,再见!");
flag = false; //退出循环
}else{
out.println("ECHO:"+str); //回应信息
}
}
}
scan.close();
out.close();
client.close();
} catch(Exception e){
e.printStackTrace();
}
}
}
public class EchoServer {
public static void main(String[] args) throws Exception{
ServerSocket server = new ServerSocket(9999); //在9999端口上监听
boolean flag = true; //循环标记
while(flag){
//接收多个客户端请求
Socket client = server.accept(); //客户端连接
new Thread(new EchoThread(client)).start(); //创建并启动新线程
}
server.close();
}
}
Java网络编程技术广泛应用于多种现实场景,主要包括以下几个方向:
1. **客户端-服务器通信**
借助Java的网络能力,可构建基于C/S模式的分布式系统。通过建立连接、传输数据及响应请求,实现浏览器与Web服务器间的HTTP交互、即时通讯工具等典型应用。
2. **文件传输与共享**
利用TCP协议建立稳定连接,可在不同节点间安全传输文件。常见应用包括FTP(文件传输协议)和SFTP(基于SSH的安全文件传输协议),适用于跨平台数据同步与资源共享。
3. **远程过程调用(RPC)**
Java支持跨主机的方法调用机制,使分布式计算成为可能。开发者可使用Java RMI、Apache Thrift 或 gRPC 等框架,在不同设备上透明调用远程函数,支撑微服务架构的构建。
4. **Socket编程**
Java提供的Socket API 允许开发者直接操控底层网络连接,支持TCP/UDP协议的自定义通信。此类技术常用于实时对战游戏、聊天系统等对延迟敏感的应用场景。
5. **网络爬虫与数据采集**
凭借强大的网络库支持,Java可用于开发高效的数据抓取工具。通过模拟浏览器行为,发送HTTP请求、解析HTML页面、提取关键信息并存储分析,广泛服务于舆情监控、价格比较、大数据采集等领域。// 范例 3: 实现服务器端
package com.xiaoshan.demo;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class EchoServer {
public static void main(String[] args) throws Exception {
ServerSocket server = new ServerSocket(9999); // 定义连接端口
Socket client = server.accept(); //等待客户端连接
//得到客户端输入数据以及向客户端输出数据的对象,利用扫描流接收,打印流输出
Scanner scan = new Scanner(client.getInputStream());
PrintStream out = new PrintStream(client.getOutputStream());
boolean flag = true; //设置循环标记
while(flag){
if (scan.hasNext()){
//是否有内容输入
String str = scan.next().trim(); //得到客户端发送的内容,并删除空格
if(str.equalslgnoreCase("byebye")){
//程序结束标记
out.println("拜拜,再见!"); //输出结束信息
flag = false; //退出循环
}else{
out.println("ECHO:" + str); //回应输入信息,加“ECHO:” 前缀返回
}
}
}
scan.close();
out.close();
client.close();
server.close();
}
}// 范例 4: 定义客户端
package com.xiaoshan.demo;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class EchoClient {
public static void main(String[] args) throws Exception{
Socket client = new Socket("localhost", 9999); //服务器地址与端口
Scanner input = new Scanner(System.in); //键盘输入数据
//利用Scanner包装客户端输入数据(服务器端输出),PrintStream包装客户端输出数据
Scanner scan = new Scanner(client.getInputStream());
PrintStream out = new PrintStream(client.getOutputStream());
input.useDelimiter("\n"); //设置键盘输入分隔符
scan.useDelimiter("\n"); //设置回应数据分隔符
boolean flag = true; //循环标志
while (flag){
System.out.print("请输入要发送数据:");
if(input.hasNext()){
//键盘是否输入数据
String str = input.next().trim(); //取得键盘输入数据
out.println(str); //发送数据到服务器端
if(str.equalsIgnoreCase("byebye"){
//结束标记
flag = false; //结束循环
}
if (scan.hasNext()){
//服务器端有回应
System.out println(scan.next()); //输出回应数据
}
}
}
input.close();
scan.close();
out.close();
client.close();
}
}getInputStream()getOutputStream()// 范例 1: 定义服务器端——主要使用 ServerSocket
package com.xiaoshan.demo;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class HelloServer {
public static void main(String[] args) throws Exception {
ServerSocket server = new ServerSocket(9999); //所有的服务器必须有端口
System.out.println("等待客户端连接…"); //提示信息
Socket client = server.accept(); //等待客户端连接
//OutputStream并不方便进行内容的输出,所以利用打印流完成输出
PrintStream out = new PrintStream(client.getOutputStream());
out.println("Hello World !"); //输出数据
out.close();
client.close();
server.close();
}
}等待客户端连接…// 范例 2: 编写客户端——Socket
package com.xiaoshan.demo;
import java.net.Socket;
import java.util.Scanner;
public class HelloClient {
public static void main(String[] args) throws Exception {
Socket client = new Socket("localhost", 9999); //连接服务器端
//取得客户端的输入数据流对象,表示接收服务器端的输出信息
Scanner scan = new Scanner(client.getInputStream()); //接收服务器端回应数据
scan.useDelimiter("\n"); //设置分隔符
if (scan.hasNext()){
//是否有数据
System.out.println("【回应数据】"+ scan.next()); //取出数据
}
scan.close();
client.close();
}
}【回应数据】Hello World !C/S网络编程在现代应用开发中具有至关重要的地位。它实现了不同主机之间的数据交换,为分布式系统的构建提供了基础支持。借助网络编程,开发者能够实现客户端与服务器之间的通信、网络爬虫与数据采集、文件的传输与共享,以及远程过程调用等多种功能。
本文围绕网络编程的基本概念及其相关协议展开介绍,重点分析了Java平台在网络通信方面的实现机制。我们深入探讨了Java中两个关键的网络编程类,分别是:
Socket
和
ServerSocket
这些核心API为构建网络连接和数据传输提供了必要的支持。
为了更直观地理解其工作原理,文章通过一个实战案例——Echo程序,演示了如何使用Java进行基本的网络通信。该示例展示了服务端接收客户端消息并原样返回的流程,帮助读者掌握Socket通信的基本结构和执行逻辑。
此外,还介绍了支撑网络通信的重要协议类型:
TCP
和
UDP
总体而言,无论是在Web应用开发还是分布式架构设计中,掌握网络编程技术都是开发人员不可或缺的能力。Java凭借其强大且丰富的网络库和API,支持多种通信协议与模式,为构建高效、稳定的网络应用程序提供了坚实的基础。通过深入理解这些技术原理并加以灵活运用,开发者可以应对多样化的应用场景需求,打造高性能的网络服务系统。


雷达卡


京公网安备 11010802022788号







