Socket ne reçoit pas ou n’envoie pas de message depuis / vers le serveur

J’implémente un serveur multi-thread Java qui reçoit les messages d’un client et les diffuse aux autres clients, mais cela ne fonctionne pas. Le serveur reçoit les messages du client uniquement lorsque l’application client se ferme (le socket client se ferme). L’application est divisée en deux modules: client et server . Le code est un peu long, je sais que c’est ennuyeux de tout lire, mais aidez-moi à résoudre ce problème.

Voici le lien de l’application GitHub pour faciliter la lecture. S’il vous plaît test twig de test .


Les classes du module server :

GameServer.java

 /* * This file contains the application core server responsible to wait and accept clients connections requests. * */ package server; import com.sun.istack.internal.NotNull; import java.io.IOException; import java.io.PrintWriter; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.HashSet; import java.util.Scanner; import java.util.logging.Level; import java.util.logging.Logger; /** * @author Michael Pacheco [email protected] * @version 1.0 * * This class is responsible to receive clients connections and send messages to them. * */ public class GameServer implements Runnable { /** * The port number used to start the server. * */ private int port; /** * The socket used to accept clients connections. * */ private ServerSocket serverSocket; /** * A {@link Logger} used to print messages for debug purpose. * */ private final static Logger LOGGER = Logger.getLogger(GameServer.class.getName()); /** * A hash set to store the clients sockets * */ private HashSet clientsSockets; private GameServer() { clientsSockets = new HashSet(); } /** * Instantiates a new {@link GameServer} with a given port number. * @param port the port number used to start the server. * */ public GameServer(int port) { this(); this.port = port; } /** * Override method from Runnable. This method is called when an attempt to close the application occur. * */ @Override public void run() { Scanner s = new Scanner(System.in); while (s.hasNext()) s.nextLine(); shutdown(); } /** * Start the server and listen for clients connections requests. * */ public void start () { try { LOGGER.log(Level.INFO, "Trying to start the server...\n"); serverSocket = new ServerSocket(this.port); final String ip = InetAddress.getLocalHost().getHostAddress(); LOGGER.log(Level.INFO, "Server started!\n\tPort: {0}\n\t IP: {1}\n", new Object[] {port, ip}); LOGGER.log(Level.INFO, "Press Ctrl-D to shutdown the server!\n"); waitForConnections(); } catch (IOException e) { LOGGER.log(Level.SEVERE, "Failed to initialize the server! {0}\n", e.getMessage()); e.printStackTrace(); } } /** * Wait for clients connections requests * */ private void waitForConnections() { new Thread(this).start(); try { //noinspection InfiniteLoopStatement while (true) { Socket clientSocket = serverSocket.accept(); LOGGER.log(Level.INFO, "New client connected! {0}\n", clientSocket); clientSocket.getOutputStream().write("You're now connected to the server\n".getBytes()); clientSocket.getOutputStream().flush(); allocateClient(clientSocket); } } catch (IOException e) { // No need for printing stacktrace if the serverSocket was closed by the shutdown method if (!serverSocket.isClosed()) e.printStackTrace(); } } /** * This method is responsible to delegate the communication with the client to the {@link ClientListener}. * @param clientSocket the client socket to delegate. * */ private void allocateClient(@NotNull Socket clientSocket) { clientsSockets.add(clientSocket); new Thread(new ClientListener(clientSocket, this)).start(); } /** * Shutdown the server * */ private void shutdown () { try { LOGGER.log(Level.INFO, "Trying to shutdown the server...\n"); // TODO Clear resources for (Socket soc : clientsSockets) removeClient(soc); serverSocket.close(); LOGGER.log(Level.INFO, "Server successfully shut down!\n"); } catch (IOException e) { LOGGER.log(Level.SEVERE, "Failed to shutdown the server! {0}\n", e.getMessage()); e.printStackTrace(); } } /** * Send a message to a single client. * @param message the message to be sent. * @param clientSocket the socket of the client that will receive the message * */ private void sendMessage (@NotNull Object message, @NotNull Socket clientSocket) { try (PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true)) { writer.println(message); } catch (IOException e) { e.printStackTrace(); } } /** * Send a message to all clients except the given one. * @param message the message to be sent. * @param excludedClient the client that won't receive the message. * */ void broadcast (@NotNull Object message, @NotNull Socket excludedClient) { for (Socket client : clientsSockets) { if (excludedClient == client) continue; sendMessage(message, client); } } /** * Remove the given client from server. * @param clientSocket the client to be removed. * */ void removeClient (@NotNull Socket clientSocket) { try { clientSocket.close(); clientsSockets.remove(clientSocket); LOGGER.log(Level.INFO, "Client removed! {0}\n", clientSocket); // TODO broadcast the client disconnection } catch (IOException e) { e.printStackTrace(); } } } 

ClientListener.java

 package server; import com.sun.istack.internal.NotNull; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; import java.util.logging.Level; import java.util.logging.Logger; /** * @author Michael Pacheco [email protected] * @version 1.0 * * This class is responsible to listen messages of a single client and send them to the server and then to the other clients. * */ public class ClientListener implements Runnable { /** * The socket used to communicate with the delegated client. * */ private Socket clientSocket; /** * A reference to the {@link GameServer} used to call the {@link GameServer} broadcast method. * @see GameServer * */ private GameServer server; /** * A {@link Logger} used to print messages for debug purpose. * */ private final static Logger LOGGER = Logger.getLogger(ClientListener.class.getName()); /** * Instantiate a new {@link ClientListener} with a given client socket. * * @param clientSocket the socket of the delegated client. * @param server the server reference used to call the broadcast method. * */ ClientListener(@NotNull Socket clientSocket, @NotNull GameServer server) { this.clientSocket = clientSocket; this.server = server; } /** * Listen for client messages and send it to the server. * */ @Override public void run() { try (BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()))) { Ssortingng message; while ((message = reader.readLine()) != null) { // send received message to the server LOGGER.log(Level.INFO, "Message received!\n\t From: {0}\n\tMessage: {1}\n", new Object[]{clientSocket, message}); server.broadcast(message, clientSocket); } } catch (IOException e) { if (!clientSocket.isClosed()) e.printStackTrace(); } finally { // send the client to server to be disconnected server.removeClient(clientSocket); } } } 

Les classes du module client :

GameClient.java

 package client; import java.io.IOException; import java.net.Socket; import java.util.logging.Level; import java.util.logging.Logger; public class GameClient { public static void main(Ssortingng[] args) { final Ssortingng serverAddress = args.length == 2 ? args[0] : "localhost"; final int port = args.length == 2 ? Integer.parseInt(args[1]) : 5000; final Logger LOGGER = Logger.getLogger(GameClient.class.getName()); try { Socket serverSocket = new Socket(serverAddress, port); LOGGER.log(Level.INFO, "Connection successful! {0}\n", serverSocket); new Thread(new ClientWriterThread(serverSocket)).start(); new Thread(new ClientReaderThread(serverSocket)).start(); } catch (IOException e) { LOGGER.log(Level.SEVERE,"Failed to connect with the server\n", e); e.printStackTrace(); } } } 

ClientWriterThread.java

 package client; import com.sun.istack.internal.NotNull; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; /** * @author Michael Pacheco * @version 1.0 * This class is responsible to read client messages and send it to the server. * */ public class ClientWriterThread implements Runnable { /** * The socket used to send messages to the server. * */ private Socket serverSocket; /** * Instantiate a new {@link ClientReaderThread} with a given server socket. * @param serverSocket the socket used to send messages to the server. * */ ClientWriterThread(@NotNull Socket serverSocket) { this.serverSocket = serverSocket; } /** * Read messages typed by the client and send it to the server. * */ @Override public void run() { try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} BufferedReader keyboardReader = null; try (PrintWriter socketWriter = new PrintWriter(serverSocket.getOutputStream(), true)) { keyboardReader = new BufferedReader(new InputStreamReader(System.in)); Ssortingng line; while ((line = keyboardReader.readLine()) != null) socketWriter.write(line); } catch (IOException e) { if (!serverSocket.isClosed()) e.printStackTrace(); } finally { try { if (keyboardReader != null) keyboardReader.close(); } catch (IOException e) { e.printStackTrace(); } } } } 

ClientReaderThread.java

 package client; import com.sun.istack.internal.NotNull; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; /** * @author Michael Pacheco * @version 1.0 * This class is responsible to read messages sent by the server and show them to the client. */ public class ClientReaderThread implements Runnable { /** * The socket used to read messages sent by the server. */ private Socket serverSocket; ClientReaderThread(@NotNull Socket serverSocket) { this.serverSocket = serverSocket; } /** * Read messages sent by the server. * */ @Override public void run() { try (BufferedReader reader = new BufferedReader(new InputStreamReader(serverSocket.getInputStream()))) { Ssortingng message; while ((message = reader.readLine()) != null) System.out.println(message); } catch (IOException e) { if (!serverSocket.isClosed()) e.printStackTrace(); } } } 

Assurez-vous que PrintWriter.flush() les messages que vous envoyez après chaque write() dans ClientWriterThread.java .

En parallèle, pour que le code soit plus clair, changez le nom de la variable clientSocket en clientSocket dans les classes appropriées ( GameClient.java et ClientWriterThread.java ).

Vous utilisez PrintWriter avec autoflush, mais vous n’utilisez pas println (…) ou format (…). Le fichier .write () n’est pas soumis à l’autoflush.

FYI, clientsSockets n’est pas thread-safe, vous ajoutez / supprimez sur des threads distincts. Pas nécessairement le bug, mais prudent. De plus, tourner des threads autres que des démons sans conserver de référence est risqué; gardez toujours les threads sous contrôle et ne comptez pas trop sur interruption () / InterruptedException / InterruptedIOException …