I have a (you guessed it) homework assignment in Java that has been driving me up the wall. I am making a chat client based on TCP that will eventually use Swing as a GUI, but right now I'm just trying to get the thing working on a CLI.
I have marked in the code (ClientHandler) where the server seems to hang. I'm sure it's a problem of IO streams, but as to what exactly, I'm a bit stumped.
The program should add a client when the client enters his/her username (on the same port, or else the program blows up (I haven't gotten that far yet)), but the server thread hangs and doesn't actually add the client.
The input stream at the marked bit of code doesn't seem to work. At all. I'm still kinda new to the whole ObjectInputStream/OutputStream bit (okay, streams in general aren't my specialty).
I have a Swing window made in WindowBuilderPro3 that I will implement later, but again, I'm just trying to get the model working.
I have searched the many similar questions here --and they have helped-- but I am still stumped. If someone can point me in the right direction (even to an answered question that does a good job answering my question), I would be most grateful. Thank you all very much.
Note: I removed imports and some comments as well as the very simple Message and User classes (they just hold (respectively) from username, to username, timestamp, message body (all Strings); username, socket) to meet the char limit
Server:
/**
* 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()" );
}
}
Client
/**
* 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()" );
}
}
}
You don't need to use DataInput/OutputStream with ObjectInput/OutputStream.
I suggest using PrintWriter/BufferedReader as these are simple. eg you can telnet to your sever and see what it sends and type what you want to send.
If you use your debugger (in you IDE) you don't need any where as many trace messages.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.