杰瑞科技汇

Java Socket编程实例如何实现?

  1. 服务器端:在指定端口上监听客户端的连接请求,接收客户端发送的消息,并将其打印出来,然后向客户端发送一个响应。
  2. 客户端:连接到服务器的指定端口,向服务器发送一条消息,并接收服务器的响应。

我们将使用 Java 8 中引入的 try-with-resources 语句,它能自动关闭资源(如 SocketServerSocket),使代码更简洁、更安全。

Java Socket编程实例如何实现?-图1
(图片来源网络,侵删)

服务器端代码

服务器将执行以下步骤:

  1. 创建一个 ServerSocket 并绑定到一个特定的端口(8888)。
  2. 调用 accept() 方法,等待客户端连接,这个方法是阻塞式的,直到有客户端连接上,它会返回一个 Socket 对象。
  3. 通过 Socket 获取输入流,用来读取客户端发送的数据。
  4. 通过 Socket 获取输出流,用来向客户端发送响应。
  5. 关闭连接和资源。

SimpleServer.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 SimpleServer {
    public static void main(String[] args) {
        // 服务器监听的端口号
        int port = 8888;
        // 使用 try-with-resources 确保 ServerSocket 和 Socket 被正确关闭
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            System.out.println("服务器已启动,等待客户端连接...");
            // accept() 方法会阻塞,直到有客户端连接
            try (Socket clientSocket = serverSocket.accept();
                 // 从客户端获取输入流,用于读取数据
                 BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                 // 向客户端获取输出流,用于发送数据
                 PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) {
                System.out.println("客户端已连接: " + clientSocket.getInetAddress().getHostAddress());
                // 读取客户端发送的消息
                String inputLine;
                while ((inputLine = in.readLine()) != null) {
                    System.out.println("收到客户端消息: " + inputLine);
                    // 如果客户端发送 "exit",则关闭连接
                    if ("exit".equalsIgnoreCase(inputLine)) {
                        System.out.println("客户端请求断开连接。");
                        break;
                    }
                    // 向客户端发送响应
                    String response = "服务器已收到你的消息: " + inputLine;
                    out.println(response);
                }
            }
        } catch (IOException e) {
            System.err.println("服务器异常: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

客户端代码

客户端将执行以下步骤:

  1. 创建一个 Socket 对象,指定服务器的 IP 地址(这里是本地 localhost)和端口号。
  2. 通过 Socket 获取输出流,向服务器发送消息。
  3. 通过 Socket 获取输入流,接收服务器的响应。
  4. 关闭连接和资源。

SimpleClient.java

Java Socket编程实例如何实现?-图2
(图片来源网络,侵删)
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 SimpleClient {
    public static void main(String[] args) {
        // 服务器的地址和端口
        String host = "localhost";
        int port = 8888;
        // 使用 try-with-resources 确保 Socket 和相关流被正确关闭
        try (Socket socket = new Socket(host, port);
             // 向服务器获取输出流,用于发送数据
             PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
             // 从服务器获取输入流,用于读取数据
             BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
            System.out.println("已连接到服务器 " + host + ":" + port);
            System.out.println("请输入要发送的消息 (输入 'exit' 退出):");
            Scanner scanner = new Scanner(System.in);
            String userInput;
            // 循环读取用户输入并发送到服务器
            while (true) {
                System.out.print("> ");
                userInput = scanner.nextLine();
                // 发送消息到服务器
                out.println(userInput);
                // 如果用户输入 "exit",则退出循环
                if ("exit".equalsIgnoreCase(userInput)) {
                    break;
                }
                // 读取并打印服务器的响应
                String serverResponse = in.readLine();
                System.out.println("服务器响应: " + serverResponse);
            }
        } catch (UnknownHostException e) {
            System.err.println("无法找到主机: " + host);
            e.printStackTrace();
        } catch (IOException e) {
            System.err.println("I/O error: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

如何运行这个实例

这是一个典型的客户端-服务器模型,你需要先启动服务器,然后再启动客户端。

步骤 1: 编译代码 打开你的终端或命令提示符,进入两个 .java 文件所在的目录,然后编译它们:

javac SimpleServer.java SimpleClient.java

这会生成 SimpleServer.classSimpleClient.class 文件。

步骤 2: 运行服务器 在同一个终端窗口中,先运行服务器:

Java Socket编程实例如何实现?-图3
(图片来源网络,侵删)
java SimpleServer

你会看到以下输出,并且程序会在这里等待:

服务器已启动,等待客户端连接...

服务器已经启动,正在监听 8888 端口。

步骤 3: 运行客户端 打开一个新的终端窗口(不要关闭第一个),然后运行客户端:

java SimpleClient

你会看到以下输出:

已连接到服务器 localhost:8888
请输入要发送的消息 (输入 'exit' 退出):
>

步骤 4: 进行交互 在客户端的终端中输入一些消息,然后按回车。

  • 在客户端输入:
    > Hello, Server!
  • 在客户端终端会看到服务器的响应:
    服务器响应: 服务器已收到你的消息: Hello, Server!
  • 在服务器端的终端会看到:
    客户端已连接: 127.0.0.1
    收到客户端消息: Hello, Server!

你可以继续输入多条消息进行测试,当你在客户端输入 exit 并回车时:

  • 客户端会收到服务器的响应,然后程序退出。
  • 服务器端会打印 "客户端请求断开连接。",accept() 方法会再次被调用,等待下一个客户端连接(如果有的话)。

代码核心概念解析

  1. ServerSocket vs Socket

    • ServerSocket:服务器端的“门”,它负责在指定的端口上监听,等待客户端的“敲门”(连接请求),当有请求时,它会打开一个新的“门”(即 Socket)与这个客户端进行通信。
    • Socket:客户端和服务器之间通信的“通道”,一旦连接建立,双方都通过这个 Socket 来发送和接收数据。
  2. 输入流/输出流

    • socket.getInputStream():获取一个输入流,用于读取从对方(客户端或服务器)发来的数据。
    • socket.getOutputStream():获取一个输出流,用于向对方写入(发送)数据。
    • 我们用 InputStreamReaderBufferedReader 来包装输入流,这样可以方便地按行读取文本。
    • 我们用 PrintWriter 包装输出流,并设置 autoFlushtrue,这样每次调用 println() 方法后都会自动刷新缓冲区,确保数据被立即发送。
  3. 阻塞

    • serverSocket.accept() 是一个阻塞方法,线程会在此处暂停,直到有新的连接进来,这是实现并发服务器的一个关键点。
    • in.readLine() 也是一个阻塞方法,它会一直等待,直到流中有数据可读或连接被关闭。
  4. 异常处理

    • 网络编程充满了不确定性,比如网络中断、服务器未启动、客户端断开连接等,所有与网络相关的操作(new ServerSocket, new Socket, accept, read, write)都必须放在 try-catch 块中,以处理可能发生的 IOException
  5. try-with-resources

    • SocketServerSocketInputStreamOutputStream 等都是需要手动关闭的资源。try-with-resources 语句确保了无论 try 块是正常结束还是抛出异常,这些资源都会在语句结束时被自动调用 close() 方法,有效避免了资源泄露。

这个例子是 Socket 编程的基础,理解了它,你就可以进一步学习更高级的主题,如多线程服务器(一个客户端一个线程)、使用 NIO(New I/O)实现高性能服务器、以及如何传输对象和文件等。

分享:
扫描分享到社交APP
上一篇
下一篇