我会分步进行,从最基础的 TCP Socket 开始,因为它提供了稳定、可靠的连接,然后再介绍更灵活的 UDP Socket。

第一部分:TCP Socket 编程 (可靠连接)
TCP (Transmission Control Protocol) 是一种面向连接的、可靠的协议,它就像打电话,必须先拨号(建立连接),双方通话(数据传输),最后挂断(关闭连接),数据在传输过程中有保证,不会丢失、重复或乱序。
核心概念
-
服务器端:
ServerSocket: 服务器端使用它来监听某个端口,等待客户端的连接请求。accept(): 这是一个阻塞方法,当有客户端连接时,它会返回一个Socket对象,服务器端通过这个Socket与客户端进行后续的通信。
-
客户端:
Socket: 客户端使用它来发起对服务器的连接请求。- 一旦连接成功,客户端也会得到一个
Socket对象,用于与服务器通信。
-
I/O 流:
(图片来源网络,侵删)- 连接建立后,双方都通过
Socket对象获取输入流和输出流来进行数据的读写。 getInputStream(): 获取输入流,用于读取对方发送过来的数据。getOutputStream(): 获取输出流,用于向对方发送数据。
- 连接建立后,双方都通过
步骤 1:创建 TCP 服务器
服务器的工作流程是:打开一个端口 -> 等待连接 -> 处理通信 -> 关闭连接,这个处理过程通常需要在一个循环中,以便能服务多个客户端。
TCPServer.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) {
int port = 8888; // 服务器监听的端口号
// 使用 try-with-resources 语句,确保 ServerSocket 和 Socket 在使用后能被自动关闭
try (ServerSocket serverSocket = new ServerSocket(port)) {
System.out.println("服务器启动,正在监听端口 " + port + "...");
// accept() 方法会阻塞,直到有客户端连接
Socket clientSocket = serverSocket.accept();
System.out.println("客户端已连接: " + clientSocket.getInetAddress().getHostAddress());
// 获取客户端的输入流,用于读取客户端发送的数据
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
// 获取客户端的输出流,用于向客户端发送数据
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
String inputLine;
// 循环读取客户端发送的数据
while ((inputLine = in.readLine()) != null) {
System.out.println("收到客户端消息: " + inputLine);
// 如果客户端发送 "exit",则结束通信
if ("exit".equalsIgnoreCase(inputLine)) {
System.out.println("客户端请求断开连接。");
break;
}
// 将收到的消息回显给客户端
String responseMessage = "服务器收到: " + inputLine;
out.println(responseMessage);
}
} catch (IOException e) {
System.err.println("服务器异常: " + e.getMessage());
e.printStackTrace();
}
System.out.println("服务器已关闭。");
}
}
步骤 2:创建 TCP 客户端
客户端的工作流程是:创建 Socket 连接服务器 -> 发送数据 -> 接收数据 -> 关闭连接。
TCPClient.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class TCPClient {
public static void main(String[] args) {
String hostname = "localhost"; // 或服务器的 IP 地址
int port = 8888;
// 使用 try-with-resources 语句,确保 Socket, PrintWriter, BufferedReader 在使用后能被自动关闭
try (Socket socket = new Socket(hostname, port);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
Scanner scanner = new Scanner(System.in)) {
System.out.println("已连接到服务器 " + hostname + ":" + port);
System.out.println("请输入消息 (输入 'exit' 退出):");
// 从控制台读取用户输入
while (scanner.hasNextLine()) {
String userInput = scanner.nextLine();
// 发送消息到服务器
out.println(userInput);
// 如果用户输入 "exit",则退出循环
if ("exit".equalsIgnoreCase(userInput)) {
break;
}
// 读取服务器返回的响应
String response = in.readLine();
if (response != null) {
System.out.println("服务器响应: " + response);
}
}
} catch (UnknownHostException e) {
System.err.println("找不到主机: " + hostname);
e.printStackTrace();
} catch (IOException e) {
System.err.println("I/O Error: " + e.getMessage());
e.printStackTrace();
}
System.out.println("客户端已关闭。");
}
}
如何运行
-
先运行服务器:
java TCPServer- 控制台会显示:
服务器启动,正在监听端口 8888...,然后程序会停顿在accept()方法处,等待客户端。
- 控制台会显示:
-
再运行客户端:
java TCPClient- 客户端控制台会显示:
已连接到服务器 localhost:8888。 - 你可以在客户端输入任何文本,"Hello Server!",然后按回车。
- 服务器控制台会显示:
收到客户端消息: Hello Server!,并向客户端回显服务器收到: Hello Server!。 - 客户端控制台会显示:
服务器响应: 服务器收到: Hello Server!。 - 在客户端输入
exit并按回车,双方程序都会正常退出。
- 客户端控制台会显示:
第二部分:UDP Socket 编程 (无连接)
UDP (User Datagram Protocol) 是一种无连接的、不可靠的协议,它就像寄明信片,你不需要和对方建立连接,直接把数据包(明信片)发出去,但不保证对方一定能收到,也不保证顺序。
核心概念
- DatagramSocket: 这是发送或接收数据报包的套接字。
- DatagramPacket: 表示一个数据报包,包含了要发送的数据、目标地址和端口,或者接收到的数据和源地址、端口。
步骤 1:创建 UDP 服务器
服务器只需要一个 DatagramSocket 在指定端口上等待接收数据包。
UDPServer.java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPServer {
public static void main(String[] args) {
int port = 8888;
byte[] buffer = new byte[1024]; // 缓冲区,用于接收数据
try (DatagramSocket socket = new DatagramSocket(port)) {
System.out.println("UDP 服务器启动,监听端口 " + port + "...");
while (true) { // 持续等待接收数据
// 创建一个 DatagramPacket 来接收数据
DatagramPacket receivePacket = new DatagramPacket(buffer, buffer.length);
// receive() 方法会阻塞,直到收到一个数据包
socket.receive(receivePacket);
// 从数据包中提取数据
String receivedMessage = new String(receivePacket.getData(), 0, receivePacket.getLength());
System.out.println("收到来自 " + receivePacket.getAddress().getHostAddress() + " 的消息: " + receivedMessage);
// 如果收到 "exit",则服务器也退出
if ("exit".equalsIgnoreCase(receivedMessage)) {
System.out.println("服务器收到退出指令,准备关闭。");
break;
}
// 准备要发送的响应消息
String responseMessage = "UDP服务器收到: " + receivedMessage;
byte[] sendData = responseMessage.getBytes();
// 创建一个新的 DatagramPacket 用于回复
DatagramPacket sendPacket = new DatagramPacket(
sendData,
sendData.length,
receivePacket.getAddress(), // 回复到客户端的地址
receivePacket.getPort() // 回复到客户端的端口
);
// 发送响应
socket.send(sendPacket);
}
} catch (IOException e) {
System.err.println("UDP 服务器异常: " + e.getMessage());
e.printStackTrace();
}
System.out.println("UDP 服务器已关闭。");
}
}
步骤 2:创建 UDP 客户端
客户端也需要一个 DatagramSocket,但它不需要 accept(),而是直接创建 DatagramPacket 发送,然后创建另一个 DatagramPacket 来接收。
UDPClient.java
import java.io.IOException; import java.net.Datagram
