0

我有一个(你猜对了)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()" );
        }
    }
}
4

1 回答 1

1

您不需要将 DataInput/OutputStream 与 ObjectInput/OutputStream 一起使用。

我建议使用 PrintWriter/BufferedReader,因为它们很简单。例如,您可以远程登录到您的服务器并查看它发送的内容并输入您想要发送的内容。

如果您使用您的调试器(在您的 IDE 中),您不需要任何跟踪消息。

于 2013-05-07T06:35:34.927 回答