Java Sockets代码修改:实现C1发消息到服务器后转发至C2
Alright, let's tackle this problem. Your current code works for one-way communication between a single client and server, but to get C1's message relayed to C2, we need to adjust the server to handle multiple concurrent connections and manage client sessions, plus tweak the clients to fit the new flow.
核心问题分析
Your original server closes the socket immediately after handling a client, which means it can't maintain an active connection with C2 while processing C1's message. We need:
- A threaded server to handle multiple clients at once
- A way to track connected clients (so the server knows where to send the notification)
- Clients that stay connected to listen for incoming messages (especially C2)
服务器端修改
We'll create a server that uses a thread per client, and maintains a thread-safe list of client output streams. When a client sends a message, we'll relay it to all other connected clients (like C2).
import java.io.*; import java.net.*; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; public class RelayServer { // Thread-safe list to hold connected clients' output streams private static final List<PrintWriter> clientWriters = new CopyOnWriteArrayList<>(); public static void main(String[] args) { System.out.println("Relay Server starting on port 9090..."); try (ServerSocket listener = new ServerSocket(9090)) { while (true) { // Spawn a new thread for each incoming connection new ClientHandler(listener.accept()).start(); } } catch (IOException e) { System.err.println("Server error: " + e.getMessage()); } } // Inner class to handle individual client connections private static class ClientHandler extends Thread { private Socket socket; private PrintWriter out; private BufferedReader in; public ClientHandler(Socket socket) { this.socket = socket; } public void run() { try { in = new BufferedReader(new InputStreamReader(socket.getInputStream())); out = new PrintWriter(socket.getOutputStream(), true); // Add this client's writer to the list clientWriters.add(out); System.out.println("New client connected. Total clients: " + clientWriters.size()); // Read messages from the client String message; while ((message = in.readLine()) != null) { System.out.println("Received from client: " + message); // Relay this message to all other clients relayMessage(message); } } catch (IOException e) { System.err.println("Client handler error: " + e.getMessage()); } finally { // Clean up when client disconnects if (out != null) { clientWriters.remove(out); } try { socket.close(); } catch (IOException e) { System.err.println("Error closing socket: " + e.getMessage()); } System.out.println("Client disconnected. Total clients: " + clientWriters.size()); } } // Relay message to all connected clients except the sender private void relayMessage(String message) { for (PrintWriter writer : clientWriters) { if (writer != out) { // Don't send back to the original sender writer.println("Notification from server: " + message); } } } } }
关键修改点:
CopyOnWriteArrayList: Thread-safe collection to store client output streams, so we can safely add/remove clients while processing messages.- Per-client threads: Each client connection gets its own thread, so the server can handle C1 and C2 at the same time.
- Message relay: When a message is received, it's sent to all other connected clients (perfect for sending C1's message to C2).
客户端修改
We'll create two focused client implementations: one for C1 (sends a message and can disconnect) and one for C2 (stays connected to listen for notifications).
C1客户端(发送消息)
import java.io.*; import java.net.*; public class ClientC1 { public static void main(String[] args) { String serverAddress = "localhost"; int port = 9090; try (Socket socket = new Socket(serverAddress, port); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) { // Send message to server String message = "Hello from C1!"; out.println(message); System.out.println("C1 sent: " + message); // Optional: Wait for server acknowledgment String response = in.readLine(); System.out.println("Server response: " + response); } catch (IOException e) { System.err.println("C1 error: " + e.getMessage()); } } }
C2客户端(监听通知)
This client stays connected to the server and listens for incoming messages indefinitely:
import java.io.*; import java.net.*; public class ClientC2 { public static void main(String[] args) { String serverAddress = "localhost"; int port = 9090; try (Socket socket = new Socket(serverAddress, port); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) { System.out.println("C2 connected to server, waiting for notifications..."); // Listen for incoming messages forever String notification; while ((notification = in.readLine()) != null) { System.out.println("\nC2 received: " + notification); } } catch (IOException e) { System.err.println("C2 error: " + e.getMessage()); } } }
客户端关键修改点:
- C2 keeps connection open: It doesn't shut down the output stream immediately, so it can listen for server notifications.
- Try-with-resources: Uses Java's try-with-resources to auto-close sockets/streams, which is cleaner than manual finally blocks.
运行步骤
- Start the
RelayServerfirst. - Start
ClientC2(it will wait for notifications). - Start
ClientC1(it sends a message to the server). - You'll see the message from C1 appear in C2's console!
额外优化建议
- Add client identification: Let clients send a username (like "C1" or "C2") so the server knows exactly who's sending and who to notify.
- Handle disconnections gracefully: The current code handles basic disconnections, but you could add heartbeats to detect unresponsive clients.
- Add error handling for message parsing: Right now, we assume all messages are plain text; you could add serialization for structured data if needed.
内容的提问来源于stack exchange,提问作者the_real_doc

