0

私は (ご想像のとおり) Java の宿題があり、それが私を壁に押し上げてきました。最終的に Swing を GUI として使用する TCP ベースのチャット クライアントを作成していますが、現在は 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

ObjectInput/OutputStream で DataInput/OutputStream を使用する必要はありません。

これらは単純なので、PrintWriter/BufferedReader を使用することをお勧めします。たとえば、サーバーに telnet して、送信内容を確認し、送信したい内容を入力できます。

(IDE で) デバッガーを使用する場合、トレース メッセージの数は必要ありません。

于 2013-05-07T06:35:34.927 に答える