源本科技 | 码上会

Java Socket

2026/01/31
43
0

学习目标

  • 理解 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(); // 向套接字写入数据

通常会使用更高级的包装流(如 DataInputStreamDataOutputStream)来处理字符串等结构化数据。

关闭连接

通信结束后,必须显式关闭输入流、输出流和套接字,以释放系统资源。

客户端完整示例

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);
    }
}

程序执行流程说明

  1. 服务器启动后,在 5000 端口监听。

  2. 客户端尝试连接该端口。

  3. 服务器接受连接,开始读取客户端发送的消息。

  4. 双方持续通信,直到客户端发送 "Over"

  5. 收到终止信号后,双方关闭连接。


运行示例

步骤

  1. 先运行服务器(在终端 1):

    $ java Server

    输出:

    服务器已启动
    等待客户端连接...
  2. 再运行客户端(在终端 2):

    $ java Client

    输出:

    已连接到服务器
  3. 在客户端输入消息,服务器将实时打印:

    Hello
    我完成了第一个套接字连接
    Over
  4. 服务器输出:

    Hello
    我完成了第一个套接字连接
    Over
    正在关闭连接...

提示:若使用 IDE(如 IntelliJ IDEA 或 Eclipse),请在两个独立的运行配置中分别启动 Server 和 Client,并确保先启动 Server。


通信流程


重点总结

要点

说明

ServerSocket

用于监听端口,调用 accept() 等待客户端连接(阻塞方法)

Socket

代表一个具体的连接端点,客户端和服务器各持有一个

数据流

使用 getInputStream()getOutputStream() 进行双向通信

编码实践

使用 DataInputStream / DataOutputStream 配合 readUTF() / writeUTF() 处理字符串

资源管理

必须在 finally 块或 try-with-resources 中关闭流和套接字,防止资源泄漏

启动顺序

必须先启动服务器,再启动客户端,否则连接会被拒绝


思考题

  1. 为什么 ServerSocket.accept() 是一个阻塞方法?在实际项目中如何避免主线程被长时间阻塞?

  2. 如果希望服务器能同时处理多个客户端连接,应该如何修改上述代码?(提示:考虑多线程)

  3. writeUTF()readUTF() 的底层协议是什么?它们对传输的数据长度有何限制?