我有一个(你猜对了)Java 的家庭作业,这让我很难受。我正在制作一个基于 TCP 的聊天客户端,最终将使用 Swing 作为 GUI,但现在我只是想让它在 CLI 上运行。
我在代码(ClientHandler)中标记了服务器似乎挂起的位置。我确定这是 IO 流的问题,但至于究竟是什么,我有点难过。
当客户端输入他/她的用户名时,程序应该添加一个客户端(在同一个端口上,否则程序会崩溃(我还没有那么远)),但是服务器线程挂起并且实际上并没有添加客户。
标记代码位的输入流似乎不起作用。完全没有。我对整个 ObjectInputStream/OutputStream 位还是有点陌生(好吧,一般来说流不是我的专长)。
我有一个在 WindowBuilderPro3 中制作的 Swing 窗口,稍后我将实现它,但同样,我只是想让模型工作。
我在这里搜索了许多类似的问题——它们有所帮助——但我仍然很难过。如果有人能指出我正确的方向(即使是一个可以很好地回答我的问题的已回答问题),我将不胜感激。非常感谢大家。
注意:我删除了导入和一些注释以及非常简单的 Message 和 User 类(它们只是(分别)保存从用户名到用户名、时间戳、消息正文(所有字符串);用户名、套接字)以满足字符限制
服务器:
/**
* Defines the server of a TCP-based chat program
*/
public class TCPChatServer
{
private static final Logger _LOG = Logger.getLogger( TCPChatServer.class );
// Default value for the server port
private static final int DEFAULT_PORT = 5555;
/**
* Main method
* @param args
* @throws IOException
*/
public static void main( String[] args ) throws IOException
{
_LOG.trace( "Entering main()" );
_LOG.info( "args == " + args );
// Set up the server for accepting clients
ServerSocket serverSocket = setupServer( args );
while( true )
{
// Wait for a client to connect to the server
Socket socket = serverSocket.accept();
_LOG.debug( "socket == " + socket.toString() );
// When a client connects, display their info
// TODO this is really only for testing
printClientSocketInfo(socket);
// Start the clientHandler thread for each client
ClientHandler clientHandler = new ClientHandler( socket );
_LOG.debug( "clientHandler == " + clientHandler.toString() );
clientHandler.start();
}
}
/**
* Prints information of the client based on the client socket
* NOTE: this will be removed in the final version and is being used for
* testing purposes
*/
private static void printClientSocketInfo( Socket socket )
{
_LOG.trace( "Entering printClientSocketInfo()" );
// Get the IP address
InetAddress clientAddress = socket.getInetAddress();
_LOG.info( "clientAddress == " + clientAddress );
// Get the port that the connection came from
int clientPort = socket.getPort();
System.out.println( "Adding client at port:[" + clientPort + "]" );
_LOG.info( "clientPort == " + clientPort );
// Print out client address and hostname
System.out.println( "Client IP Address:[" + clientAddress
+ "], hostname:[" + clientAddress.getHostName() + "]" );
_LOG.info( "hostName == " + clientAddress.getHostName() );
_LOG.trace( "Exiting printClientSocketInfo()" );
}
/**
* Starts the server up based on port number given on the command line
*/
private static ServerSocket setupServer( String[] args ) throws IOException
{
_LOG.trace( "Entering setupServer()" );
// The default port
int serverPort = DEFAULT_PORT;
// Get the port to use from the command line
if( args.length > 0 )
{
serverPort = Integer.valueOf( args[0] ).intValue();
System.out.println( "Server using port=[" + serverPort + "]" );
}
// Otherwise, use the default port
else
{
System.out.println( "Server using port=[" + serverPort + "]" );
}
_LOG.info( "serverPort == " + serverPort );
// Open a new server socket on the specified port
ServerSocket serverSocket = new ServerSocket( serverPort );
_LOG.debug( "serverSocket == " + serverSocket );
// Print a status message TODO again, really only for testing
System.out.println( "Server loaded" );
System.out.println();
_LOG.trace( "Exiting setupServer()" );
return serverSocket;
}
}
/**
* A thread class that handles the I/O of a client
*/
class ClientHandler extends Thread
{
private static final Logger _LOG = Logger.getLogger( ReceiverThread.class );
// Tag for joining users
private static final String JOIN_TAG = ".JOIN";
// Tag for client exiting the server
private static final String EXIT_TAG = ".EXIT";
// Tag for all users
private static final String ALL_CLIENTS = ".ALL";
// Make a set of the Users on the server
private static Set<User> userSet = new HashSet<User>();
// The socket the client is connected to
private Socket _socket = new Socket();
// Constructor
public ClientHandler( Socket socket )
{
_LOG.info( "Constructing ClientHandler" );
_socket = socket;
_LOG.debug( "socket == " + socket );
}
@Override
public void run()
{
_LOG.trace( "Entering run()" );
// These first few bits handle a client joining
try
{
// Create out input streams
ObjectInputStream input = new ObjectInputStream(
new DataInputStream( _socket.getInputStream() ) );
_LOG.debug( "input == " + input.toString() );
// Used for accepting incoming messages
Message incomingMessage = null;
Object initialObject = null;
initialObject = input.readObject();
_LOG.debug( "initialObject == " + initialObject.toString() );
// Check to see if the incoming object is a Message
if( initialObject instanceof Message )
{
_LOG.debug( "initialObject instanceof Message" );
incomingMessage = (Message) initialObject;
_LOG.info( "incoming message == "
+ incomingMessage.toString() );
}
else
{
_LOG.fatal( "initialObject not a Message" );
// This case shouldn't happen, but if it does...
return;
}
// Username of the client sending the message
String username = incomingMessage.getFromUserName();
// Register the client as a user
User user = new User( username, _socket);
//TODO testing stuff
System.out.println( "User: " + user.toString() );
System.out.println( "Message: " + incomingMessage.toString() );
// Set the message to be returned to the incoming message
Message returnMessage = incomingMessage;
// Get who to send the message to
String toUser = incomingMessage.getToUserName();
// Once we have connected with a client, continue working with them
while( true )
{
//TODO IT'S THIS ONE
// For some reason, this isn't working and is keeping the
// clients from properly joining
incomingMessage = (Message) input.readObject();
_LOG.info( "incoming message == " + incomingMessage.toString() );
// The message to send out to the clients
returnMessage = incomingMessage;
System.out.println( "return message pre-type check: "
+ returnMessage.toString() );
toUser = returnMessage.getToUserName();
_LOG.info( "toUser == " + toUser );
// If it's a join message, add the client to the server
if( JOIN_TAG.equals( toUser ) )
{
returnMessage = registerClient( incomingMessage, user );
}
else if( EXIT_TAG.equals( toUser ) )
{
returnMessage = removeClient( incomingMessage, user, _socket );
}
_LOG.info( "returnMessage == " + returnMessage.toString() );
// Print the message to be sent on the Server's console TODO
System.out.println( "return message post-type check: "
+ returnMessage.toString() );
// toUser may have changed in joining or exiting
toUser = returnMessage.getToUserName();
_LOG.info( "toUser (may have changed if client added or removed) == " + toUser );
// Send the message as a broadcast to all clients
if( ALL_CLIENTS.equals( toUser ) )
{
_LOG.info( "Broadcasting message" );
broadcast( returnMessage );
}
// Send a message to a specific client
else
{
_LOG.info( "Sending private message to: ["
+ toUser
+ "]" );
sendPrivateMessage( returnMessage );
}
}
}
catch( IOException ioe )
{
_LOG.error( "IOException caught" );
ioe.printStackTrace();
}
catch( ClassNotFoundException cnfe )
{
_LOG.error( "ClassNotFoundException caught" );
cnfe.printStackTrace();
}
_LOG.trace( "Exiting run()" );
}
/**
* Adds a new client to the server
*/
private Message registerClient( Message joinMessage, User user )
{
_LOG.trace( "Entering registerClient()" );
_LOG.info( "client joining" );
// Get the username of the new user
String username = joinMessage.getFromUserName();
_LOG.info( "username == " + username );
// Add client to the user set
System.out.println( "Adding " + username + " to chat registry" );
user.setUsername( username );
boolean added = userSet.add( user );
_LOG.info( user + " has been added: " + added );
// Set up a broadcast message showing the user joined to send
String joinBody = ( username + " joined the chatroom" );
joinMessage.setBody( joinBody );
joinMessage.setToUserName( ALL_CLIENTS );
// Return the message to be sent
System.out.println( "Join message: " + joinMessage.toString() );
_LOG.info( "joinMessage == " + joinMessage.toString() );
// return the message to send
_LOG.trace( "Exiting registerClient()" );
return joinMessage;
}
/**
* Removes a client from the server
*/
private Message removeClient( Message clientMessage, User user, Socket socket )
{
_LOG.trace( "Entering removeClient()" );
// Set the message to an exit message
String username = user.getUsername();
String exitBody = ( username + " has left the chatroom" );
_LOG.info( "client [" + username + "] leaving" );
// The message will be sent as a broadcast
clientMessage.setBody( exitBody );
clientMessage.setToUserName( ALL_CLIENTS );
// Remove the user from the userSet
boolean removedUser = userSet.remove( user );
_LOG.debug( "Removed user [" + username + "] == " + removedUser );
// Close the socket with the user
try
{
_LOG.info( "Closing socket" );
socket.close();
}
catch( IOException ioe )
{
_LOG.error( "IOException caught" );
ioe.printStackTrace();
}
_LOG.trace( "Exiting removeClient()" );
return clientMessage;
}
/**
* Sends a message to all clients registered on a server
*/
private void broadcast( Message broadcastMessage )
{
_LOG.trace( "Entering broadcast()" );
// Create an iterator of all the users registered
Iterator<User> userIterator = userSet.iterator();
_LOG.debug( "userIterator == " + userIterator );
// Used to temporarily store user info
User tempUser = new User();
// Used to send the message
ObjectOutputStream output = null;
try
{
// Go through and send the message to each user
while( userIterator.hasNext() )
{
// Get the socket of each user
tempUser = userIterator.next();
_LOG.debug( "tempUser == " + tempUser.toString() );
Socket socket = tempUser.getSocket();
// Send the message
System.out.println( "Broadcast message to " + tempUser.getUsername() );
_LOG.info( "Broadcast message to " + tempUser.getUsername() );
output = new ObjectOutputStream( new DataOutputStream( socket.getOutputStream() ) );
_LOG.debug( "output == " + output.toString() );
output.writeObject( broadcastMessage );
_LOG.info( "closing output" );
output.close();
}
}
catch( IOException ioe )
{
_LOG.error( "IOException caught" );
ioe.printStackTrace();
}
_LOG.trace( "Exiting broadcast()" );
}
/**
* Sends a message to a specific user
*/
private void sendPrivateMessage( Message privateMessage )
{
_LOG.trace( "Entering sendPrivateMessage()" );
// Create an iterator to find the recipient from a list of clients
Iterator<User> userIterator = userSet.iterator();
_LOG.debug( "userIterator == " + userIterator );
// Used to store the recipient of the private message
User recipient = new User();
// Used to compare the message target
String sendTo = privateMessage.getToUserName();
_LOG.info( "sendTo == " + sendTo );
// Used to send the message
ObjectOutputStream output = null;
try
{
// Go through the set of users looking for the recipient
while( userIterator.hasNext() )
{
_LOG.info( "userIterator hasNext" );
recipient = userIterator.next();
_LOG.debug( "potential recipient == " + recipient.toString() );
// If the username matches the intended target send the message
if( recipient.getUsername() == sendTo )
{
_LOG.info( "recipient found" );
// Get the socket of the recipient
Socket socket = recipient.getSocket();
// Send the message to the recipient
output = new ObjectOutputStream( new DataOutputStream( socket.getOutputStream() ) );
_LOG.debug( "output == " + output.toString() );
output.writeObject( privateMessage );
// As we've sent the message, break from the loop
_LOG.info( "breaking from loop" );
break;
}
}
}
catch( IOException ioe )
{
ioe.printStackTrace();
System.exit( 1 );
}
_LOG.trace( "Exiting sendPrivateMessage()" );
}
}
客户
/**
* Defines the client of a TCP-based chat program
*/
public class TCPChatClient
{
private static final Logger _LOG = Logger.getLogger( TCPChatClient.class );
// The default port the client will use
private static final int DEFAULT_PORT = 5555;
public static void main( String args[] ) throws Exception
{
_LOG.trace( "Entering main()" );
_LOG.info( "args == " + args );
// The default port
int clientPort = DEFAULT_PORT;
// NOTE: localhost will only work on the client's machine
String host = "localhost";
// Username of the client
String username = new String();
// enter the client's username
System.out.print( "Please enter desired username: " );
Scanner scanner = new Scanner( System.in );
username = scanner.nextLine();
_LOG.info( "username == " + username );
// Get the port number to use from the command line
if( args.length > 0 )
{
host = args[0];
clientPort = Integer.valueOf( args[0] ).intValue();
System.out.println( username + " now using host:[" + host
+ "], port:[" + clientPort + "]" );
}
// Else, use the default port
else
{
System.out.println( username + " now using host:[" + host
+ "], port:[" + clientPort + "]" );
}
_LOG.info( "host == " + host );
_LOG.info( "port == " + clientPort );
// Get the IP address of the local machine
InetAddress address = InetAddress.getByName( host );
_LOG.info( "address == " + address );
// Print out an intro to the client
System.out.println( "Welcome to the chatroom, " + username + "." );
// Attempt to connect to the server
Socket socket = new Socket( address, clientPort );
_LOG.debug( "socket == " + socket.toString() );
// Start our sender thread
ObjectOutputStream output = new ObjectOutputStream( new DataOutputStream( socket.getOutputStream() ) );
SenderThread sender = new SenderThread( socket, username, output );
sender.start();
_LOG.debug( "sender == " + sender );
// Start our receiver thread
ObjectInputStream input = new ObjectInputStream( new DataInputStream( sender.getSocket().getInputStream() ) );
ReceiverThread receiver = new ReceiverThread( sender.getSocket(), input );
receiver.start();
_LOG.debug( "receiver == " + receiver );
_LOG.trace( "Exiting main()" );
}
}
/**
* Defines the thread that handles the sending of data to the server
*/
class SenderThread extends Thread
{
private static final Logger _LOG = Logger.getLogger( SenderThread.class );
// Tag for all users
private static final String ALL_CLIENTS = ".ALL";
// Tag for joining users
private static final String JOIN_TAG = ".JOIN";
// Tag for client exiting the server
private static final String EXIT_TAG = ".EXIT";
// The socket of the client used
private Socket _clientSocket;
// Determines whether or not thread continues
private boolean _stopped = false;
// Username of the current client
private String _username = new String();
//TODO
private ObjectOutputStream _output = null;
// Constructor
public SenderThread( Socket clientSocket, String username, ObjectOutputStream output ) throws SocketException
{
_LOG.info( "constructing SenderThread" );
_clientSocket = clientSocket;
_LOG.info( "_clientSocket == " + _clientSocket );
_username = username;
_LOG.info( "_username == " + _username );
//TODO
_output = output;
_LOG.info( "_output == " + _username );
}
public void halt()
{
_LOG.trace( "Entering/exiting halt()" );
_stopped = true;
}
public Socket getSocket()
{
_LOG.trace( "Entering/exiting getSocket()" );
return _clientSocket;
}
public void run()
{
_LOG.trace( "entering run()" );
try
{
// send join message (only done once)
Message joinMessage = new Message( _username, JOIN_TAG, "" );
_LOG.debug( "joinMessage == " + joinMessage.toString() );
_output.writeObject( joinMessage );
_output.flush();
while( true )
{
// Exit if the stopped flag has been flipped (shouldn't happen)
_LOG.info( "_stopped == " + _stopped );
if( _stopped )
{
_LOG.info( "returning" );
return;
}
// Create input stream
Scanner scanner = new Scanner( System.in );
_LOG.debug( "input == " + scanner );
// Message to send
String messageBody = scanner.nextLine().trim();
_LOG.info( "messageBody == " + messageBody );
// TODO Figure a way to set the message to a specific user
Message outgoingMessage = new Message( _username, ALL_CLIENTS, messageBody );
// Exit the chatroom program
if( outgoingMessage.getToUserName() == EXIT_TAG )
{
_LOG.info( "client exiting server" );
// Close our streams (output closed by server)
scanner.close();
// Send the ".EXIT" message to the server
_output.writeObject( outgoingMessage );
_output.flush();
// Give exit message to client
System.out.println( "Goodbye, " + _username );
// Exit
halt();
_LOG.info( "Exiting system" );
System.exit( 1 );
}
// Send the message to the server
_output.writeObject( outgoingMessage );
_LOG.info( "message sent == " + outgoingMessage.toString() );
_output.flush();
// Allow the receiver thread to run
_LOG.info( "yielding thread" );
Thread.yield();
}
}
catch( IOException ioe )
{
_LOG.error( "IOException caught" );
ioe.printStackTrace();
System.exit( 1 );
}
_LOG.trace( "leaving run()" );
}
}
/**
* Defines the thread that handles the receiving of data from the server
*/
class ReceiverThread extends Thread
{
/** Our Logger */
private static final Logger _LOG = Logger.getLogger( ReceiverThread.class );
// Socket that the current client will use
private Socket _clientSocket = null;
// Flag to determine if the thread has stopped
private boolean _stopped = false;
//TODO
private ObjectInputStream _input = null;
// Constructor
public ReceiverThread( Socket socket, ObjectInputStream input ) throws SocketException
{
_LOG.info( "constructing ReceiverThread" );
_clientSocket = socket;
_LOG.info( "_clientSocket == " + _clientSocket.toString() );
//TODO
_input = input;
_LOG.debug( "_input == " + _input.toString() );
}
public void halt()
{
_LOG.trace( "entering/exiting halt()" );
_stopped = true;
}
public void run()
{
_LOG.trace( "entering run()" );
while( true ) // Infinite loop
{
_LOG.info( "_stopped == " + _stopped );
if( true == _stopped )
{
return;
}
Message reply = null;
try
{
reply = (Message) _input.readObject();
_LOG.debug( "reply == " + reply );
if( reply == null )
{
_LOG.debug( "Null reply" );
}
// print the message
reply.printMessage();
_LOG.debug( "reply (past null check) == " + reply.toString() );
// Allow the sender thread to run
_LOG.info( "yielding thread" );
Thread.yield();
}
catch( IOException ioe )
{
_LOG.error( "IOException caught" );
ioe.printStackTrace();
System.exit( 1 );
}
catch( ClassNotFoundException cnfe )
{
_LOG.error( "ClassNotFoundException caught" );
cnfe.printStackTrace();
System.exit( 1 );
}
_LOG.trace( "leaving run()" );
}
}
}