Socket 详解:零基础入门教程
1. 什么是 Socket?
Socket(套接字)是网络通信的抽象接口,用于在不同设备之间传输数据。类比于两个城市之间的电话线,一端拨号(客户端),另一端接听(服务器),双方通过线路交换数据。Socket 的主要作用是实现客户端和服务器之间的双向通信,支持 TCP 和 UDP 协议。
2. Socket 的类型
2.1 基于 TCP 的 Socket
基于 TCP 的 Socket 是面向连接的,提供可靠的数据传输,确保数据不丢失且有序传输。适用于文件传输、网页浏览(HTTP)、邮件(SMTP)等场景。核心类包括 ServerSocket
(服务器端监听连接)和 Socket
(客户端和服务器的通信端点)。
2.2 基于 UDP 的 Socket
基于 UDP 的 Socket 是无连接的,传输速度快,但可能丢包。适用于视频流、实时游戏、广播消息等场景。核心类包括 DatagramSocket
(发送和接收数据包)和 DatagramPacket
(封装数据包)。
3. TCP Socket 编程步骤
3.1 服务器端(Server)
服务器端通过 ServerSocket
监听指定端口,等待客户端连接。连接建立后,通过 Socket
获取输入输出流,与客户端进行数据交互,最后关闭资源。
// 示例:TCP 服务器端
import java.io.*;
import java.net.*;
public class TCPServer {
public static void main(String[] args) throws IOException {
// 1. 创建 ServerSocket,监听 8888 端口
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务器已启动,等待连接...");
// 2. 等待客户端连接(阻塞直到有连接)
Socket clientSocket = serverSocket.accept();
System.out.println("客户端已连接:" + clientSocket.getInetAddress());
// 3. 获取输入输出流
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream())
);
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
// 4. 处理数据:读取客户端消息并回复
String clientMessage = in.readLine();
System.out.println("客户端说:" + clientMessage);
out.println("你好,我是服务器!");
// 5. 关闭资源
in.close();
out.close();
clientSocket.close();
serverSocket.close();
}
}
3.2 客户端(Client)
客户端通过 Socket
连接服务器的指定 IP 和端口,获取输入输出流,向服务器发送请求并接收响应,最后关闭资源。
// 示例:TCP 客户端
import java.io.*;
import java.net.*;
public class TCPClient {
public static void main(String[] args) throws IOException {
// 1. 创建 Socket,连接服务器的 8888 端口
Socket socket = new Socket("localhost", 8888);
System.out.println("已连接到服务器...");
// 2. 获取输入输出流
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
// 3. 发送消息
out.println("你好,我是客户端!");
// 4. 接收服务器响应
String serverResponse = in.readLine();
System.out.println("服务器回复:" + serverResponse);
// 5. 关闭资源
in.close();
out.close();
socket.close();
}
}
4. UDP Socket 编程步骤
4.1 发送方(Sender)
发送方通过 DatagramSocket
发送数据包,无需绑定端口。数据包通过 DatagramPacket
封装,指定目标 IP 和端口,最后关闭资源。
// 示例:UDP 发送方
import java.net.*;
public class UDPSender {
public static void main(String[] args) throws IOException {
// 1. 创建 DatagramSocket
DatagramSocket socket = new DatagramSocket();
// 2. 封装数据包(目标地址为 localhost:9999)
String message = "Hello UDP!";
byte[] data = message.getBytes();
InetAddress address = InetAddress.getByName("localhost");
DatagramPacket packet = new DatagramPacket(data, data.length, address, 9999);
// 3. 发送数据
socket.send(packet);
System.out.println("消息已发送!");
// 4. 关闭资源
socket.close();
}
}
4.2 接收方(Receiver)
接收方通过 DatagramSocket
绑定指定端口,准备数据包接收数据。通过 receive()
方法阻塞等待数据包,解析发送方的信息,最后关闭资源。
// 示例:UDP 接收方
import java.net.*;
public class UDPReceiver {
public static void main(String[] args) throws IOException {
// 1. 创建 DatagramSocket,绑定 9999 端口
DatagramSocket socket = new DatagramSocket(9999);
// 2. 准备数据包
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// 3. 接收数据(阻塞直到有数据)
socket.receive(packet);
String message = new String(packet.getData(), 0, packet.getLength());
System.out.println("收到消息:" + message);
// 4. 关闭资源
socket.close();
}
}
代码详细注释
- TCPServer.java:服务器端代码,监听指定端口,等待客户端连接,处理客户端消息并回复。
- TCPClient.java:客户端代码,连接服务器,发送消息并接收服务器响应。
- UDPSender.java:UDP 发送方代码,发送数据包到指定目标地址。
- UDPReceiver.java:UDP 接收方代码,绑定指定端口,接收并处理数据包。
5. 核心知识点总结
概念 | 说明 |
---|---|
IP 地址与端口 | IP 标识设备,端口标识应用程序(范围:0-65535,0-1023 为系统保留) |
TCP vs UDP | TCP 可靠但慢,UDP 快但不可靠。 |
阻塞与非阻塞 | accept() 和 receive() 是阻塞方法,直到有连接或数据到达。 |
多客户端处理 | 服务器端可通过多线程或线程池处理多个客户端请求(示例见下文补充)。 |
数据格式 | 通常使用字符串、JSON 或二进制数据(如图片、文件)。 |
6. 常见问题与解决方案
- 连接超时:检查目标 IP 和端口是否正确,防火墙是否开放。
- 数据乱码:确保发送和接收的字符编码一致(如 UTF-8)。
- 资源未关闭:使用
try-with-resources
自动关闭资源(Java 7+)。try (Socket socket = new Socket("localhost", 8888)) { // 使用 socket } catch (IOException e) { e.printStackTrace(); }
- 端口占用:通过
netstat -ano
查看端口占用情况,终止占用进程。
7. 扩展:多线程处理多客户端
// 多线程服务器示例
public class MultiThreadServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
Socket clientSocket = serverSocket.accept();
// 为每个客户端创建新线程
new Thread(() -> {
try (BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) {
String request = in.readLine();
out.println("处理后的响应:" + request);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
}
8. 测试你的 Socket 程序
- 本地测试:服务器和客户端都运行在同一台机器,IP 使用
localhost
。 - 跨网络测试:确保设备在同一局域网,使用内网 IP(如
192.168.x.x
)。 - 使用 Telnet:快速测试 TCP 服务器(命令:
telnet IP 端口
)。