理解 Java 中套接字(Socket)的基本概念及其在网络通信中的作用
掌握客户端与服务器端的套接字编程基本流程
能够使用 java.net 包实现简单的双向通信程序
了解输入 / 输出流在套接字通信中的使用方式
掌握连接资源的正确关闭方法,避免资源泄漏
Java 中的套接字编程允许两台设备通过网络进行通信。它基于 java.net 包,通过客户端与服务器之间的数据交换,构建分布式应用,如聊天系统、文件传输工具或典型的客户端 - 服务器架构应用。套接字(Socket)是通信的端点,每台设备通过 IP 地址和 TCP 端口号相互识别,从而建立连接并传输数据。
要连接到服务器,客户端需创建一个 Socket 对象,指定目标服务器的 IP 地址和端口号:
Socket socket = new Socket("127.0.0.1", 5000);第一个参数:服务器的 IP 地址。127.0.0.1 表示本地回环地址(即本机),适用于单机测试。
第二个参数:TCP 端口号(范围 0~65535)。例如 HTTP 服务默认使用 80 端口。
连接建立后,通过输入流(InputStream)读取数据,通过输出流(OutputStream)发送数据:
InputStream input = socket.getInputStream(); // 从套接字读取数据
OutputStream output = socket.getOutputStream(); // 向套接字写入数据通常会使用更高级的包装流(如 DataInputStream 和 DataOutputStream)来处理字符串等结构化数据。
通信结束后,必须显式关闭输入流、输出流和套接字,以释放系统资源。
import java.io.*;
import java.net.*;
public class Client {
private Socket socket = null;
private DataInputStream userInput = null;
private DataOutputStream outToServer = null;
public Client(String address, int port) {
try {
socket = new Socket(address, port);
System.out.println("已连接到服务器");
userInput = new DataInputStream(System.in);
outToServer = new DataOutputStream(socket.getOutputStream());
} catch (UnknownHostException e) {
System.err.println("未知主机: " + e.getMessage());
return;
} catch (IOException e) {
System.err.println("IO 异常: " + e.getMessage());
return;
}
String message = "";
while (!message.equals("Over")) {
try {
message = userInput.readLine();
outToServer.writeUTF(message);
} catch (IOException e) {
System.err.println("发送消息失败: " + e.getMessage());
}
}
// 关闭资源
try {
userInput.close();
outToServer.close();
socket.close();
} catch (IOException e) {
System.err.println("关闭连接时出错: " + e.getMessage());
}
}
public static void main(String[] args) {
new Client("127.0.0.1", 5000);
}
}如果服务器未启动,运行客户端将抛出
Connection refused异常。
服务器需要两个关键对象:
ServerSocket:监听指定端口,等待客户端连接请求。
Socket:一旦有客户端连接,accept() 方法返回一个代表该连接的 Socket 实例,用于与客户端通信。
通过 getInputStream() 获取客户端发送的数据,通过 getOutputStream() 向客户端发送响应(本例中仅接收)。
通信完成后,应关闭客户端套接字及相关的流,释放资源。
import java.net.*;
import java.io.*;
public class Server {
private Socket clientSocket = null;
private ServerSocket serverSocket = null;
private DataInputStream inFromClient = null;
public Server(int port) {
try {
serverSocket = new ServerSocket(port);
System.out.println("服务器已启动");
System.out.println("等待客户端连接...");
clientSocket = serverSocket.accept();
System.out.println("客户端已连接");
inFromClient = new DataInputStream(
new BufferedInputStream(clientSocket.getInputStream())
);
String message = "";
while (!message.equals("Over")) {
try {
message = inFromClient.readUTF();
System.out.println(message);
} catch (IOException e) {
System.err.println("读取消息失败: " + e.getMessage());
}
}
System.out.println("正在关闭连接...");
clientSocket.close();
inFromClient.close();
} catch (IOException e) {
System.err.println("服务器异常: " + e.getMessage());
}
}
public static void main(String[] args) {
new Server(5000);
}
}服务器启动后,在 5000 端口监听。
客户端尝试连接该端口。
服务器接受连接,开始读取客户端发送的消息。
双方持续通信,直到客户端发送 "Over"。
收到终止信号后,双方关闭连接。
先运行服务器(在终端 1):
$ java Server输出:
服务器已启动
等待客户端连接...再运行客户端(在终端 2):
$ java Client输出:
已连接到服务器在客户端输入消息,服务器将实时打印:
Hello
我完成了第一个套接字连接
Over服务器输出:
Hello
我完成了第一个套接字连接
Over
正在关闭连接...提示:若使用 IDE(如 IntelliJ IDEA 或 Eclipse),请在两个独立的运行配置中分别启动 Server 和 Client,并确保先启动 Server。

为什么 ServerSocket.accept() 是一个阻塞方法?在实际项目中如何避免主线程被长时间阻塞?
如果希望服务器能同时处理多个客户端连接,应该如何修改上述代码?(提示:考虑多线程)
writeUTF() 和 readUTF() 的底层协议是什么?它们对传输的数据长度有何限制?