Java 套接字未从服务器接收或向服务器发送消息
我正在实现一个Java多线程服务器,它接收来自客户机的消息并将它们广播到其他客户机,但它不起作用。服务器仅在客户端应用程序关闭(客户端套接字关闭)时接收客户端消息。该应用程序分为两个模块:Java 套接字未从服务器接收或向服务器发送消息,java,multithreading,sockets,server,io,Java,Multithreading,Sockets,Server,Io,我正在实现一个Java多线程服务器,它接收来自客户机的消息并将它们广播到其他客户机,但它不起作用。服务器仅在客户端应用程序关闭(客户端套接字关闭)时接收客户端消息。该应用程序分为两个模块:客户端和服务器。代码有点长,我知道读起来很烦人,但是请帮我解决这个问题 是GitHub应用程序链接,以方便阅读请签出测试分支。 服务器模块类: GameServer.java /* * This file contains the application core server responsible to
客户端
和服务器
。代码有点长,我知道读起来很烦人,但是请帮我解决这个问题
是GitHub应用程序链接,以方便阅读请签出测试
分支。
服务器
模块类:
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 <b>pacheco@decom.ufop.br</b>
* @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<Socket> 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();
}
}
}
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 <b>pacheco@decom.ufop.br</b>
* @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()))) {
String 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);
}
}
}
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(String[] args) {
final String 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();
}
}
}
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));
String 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();
}
}
}
}
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()))) {
String message;
while ((message = reader.readLine()) != null) System.out.println(message);
} catch (IOException e) {
if (!serverSocket.isClosed())
e.printStackTrace();
}
}
}
客户端
模块类:
GameClient.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 <b>pacheco@decom.ufop.br</b>
* @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<Socket> 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();
}
}
}
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 <b>pacheco@decom.ufop.br</b>
* @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()))) {
String 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);
}
}
}
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(String[] args) {
final String 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();
}
}
}
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));
String 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();
}
}
}
}
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()))) {
String message;
while ((message = reader.readLine()) != null) System.out.println(message);
} catch (IOException e) {
if (!serverSocket.isClosed())
e.printStackTrace();
}
}
}
ClientWriterThread.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 <b>pacheco@decom.ufop.br</b>
* @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<Socket> 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();
}
}
}
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 <b>pacheco@decom.ufop.br</b>
* @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()))) {
String 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);
}
}
}
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(String[] args) {
final String 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();
}
}
}
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));
String 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();
}
}
}
}
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()))) {
String message;
while ((message = reader.readLine()) != null) System.out.println(message);
} catch (IOException e) {
if (!serverSocket.isClosed())
e.printStackTrace();
}
}
}
ClientReaderThread.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 <b>pacheco@decom.ufop.br</b>
* @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<Socket> 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();
}
}
}
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 <b>pacheco@decom.ufop.br</b>
* @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()))) {
String 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);
}
}
}
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(String[] args) {
final String 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();
}
}
}
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));
String 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();
}
}
}
}
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()))) {
String message;
while ((message = reader.readLine()) != null) System.out.println(message);
} catch (IOException e) {
if (!serverSocket.isClosed())
e.printStackTrace();
}
}
}
确保在ClientWriterThread.java中每次
write()
之后发送的消息都PrintWriter.flush()
作为旁注,为了使代码更清晰,请在适当的类(GameClient.java和ClientWriterThread.java)中将
serverSocket
变量名更改为clientSocket
。确保PrintWriter.flush()
在ClientWriterThread.java中每次write()
之后发送的消息
作为旁注,为了使代码更清晰,请在适当的类(GameClient.java和ClientWriterThread.java)中将
serverSocket
变量名更改为clientSocket
。您将PrintWriter与autoflush一起使用,但不使用println(…)或format(…)。.write()不受自动刷新的约束
仅供参考,clientsSockets不是线程安全的,您可以在不同的线程上添加/删除。不一定是错误,但要小心。而且,在不保留引用的情况下旋转非守护进程线程是有风险的;始终控制线程,不要过多地依赖中断()/InterruptedException/InterruptedIOException…您使用带有自动刷新的PrintWriter,但不使用println(…)或format(…)。.write()不受自动刷新的约束
仅供参考,clientsSockets不是线程安全的,您可以在不同的线程上添加/删除。不一定是错误,但要小心。而且,在不保留引用的情况下旋转非守护进程线程是有风险的;始终控制线程,不要过多地依赖中断()/InterruptedException/InterruptedIOException…但是我用
autoFlush
标记和true
实例化了PrintWriter
,因此,所有write()
调用都是自动刷新的。我不知道这个标记。我当时没有任何线索。GameClient.java变量中的serverSocket
,之所以命名为so,是因为返回的套接字对应于服务器端套接字。在ClientWriterThread中,我将变量命名为serverSocket
,因为这个套接字用于与服务器通信,而不是客户端。实际上,我的线索只是一个猜测,我现在写在这里:尝试避免将InputStreamReader包装到BufferedReader中。哦,我的糟糕。。。我没有很好地阅读文档。flush标志仅用于调用println()
或printf()
而不用于write()
。也许这就是问题所在。但是我用true
的autoFlush
标志实例化了PrintWriter
,所以所有write()
调用都是自动刷新的。我不知道这个标志。我当时没有任何线索。GameClient.java变量中的serverSocket
,之所以命名为so,是因为返回的套接字对应于服务器端套接字。在ClientWriterThread中,我将变量命名为serverSocket
,因为这个套接字用于与服务器通信,而不是客户端。实际上,我的线索只是一个猜测,我现在写在这里:尝试避免将InputStreamReader包装到BufferedReader中。哦,我的糟糕。。。我没有很好地阅读文档。flush标志仅用于调用println()
或printf()
而不用于write()
。也许这就是问题所在。