Java Instant Messenger - 将多个客户端连接到服务器和另一个客户端

时间:2014-04-08 01:07:41

标签: java

我编写了一个简单的客户端/服务器即时消息程序,它具有基本功能。

目前,我可以在服务器和一个连接的客户端之间发送/接收消息。但是,后续客户端将无法连接到服务器。

我希望实现允许以下内容的功能:

成功连接2+个客户端到服务器;

多个客户端连接到服务器并成功相互通信并与服务器通信。

我想我需要为每个连接设置一个新线程,但我不确定如何解决这个问题。

请告知(参见下面的代码)。

非常感谢任何帮助。

非常感谢。

Client.java:

import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Date; //timestamp functionality

import javax.swing.*;

public class Client extends JFrame { //inherits from JFrame

    //1. Creating instance variables

    private JTextField userText; //where user inputs text
    private JTextArea chatWindow; //where messages are displayed
    private String fullTimeStamp = new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(new Date());
    //fullTimeStamp - MM = months; mm = minutes; HH = 24-hour cloc
    private ObjectOutputStream output; //output from Client to Server
    private ObjectInputStream input; //messages received from Server
    private String message ="";
    private String serverIP;
    private Socket connection;

    //2. Constructor (GUI)
    public Client(String host){ //host=IP address of server
        super("Mick's Instant Messenger [CLIENT]");
        serverIP = host; //placed here to allow access to private String ServerIP
        userText = new JTextField();
        userText.setEditable(false);
        userText.addActionListener(
            new ActionListener(){
                public void actionPerformed(ActionEvent event){
                    sendMessage(event.getActionCommand()); //For this to work, must build sendData Method
                    userText.setText(""); //resets userText back to blank, after message has been sent to allow new message(s)
                }
            }

                );
        add(userText, BorderLayout.SOUTH);
        chatWindow = new JTextArea();
        add(new JScrollPane(chatWindow), BorderLayout.CENTER); //allows you to scroll up and down when text outgrows chatWindow
        chatWindow.setLineWrap(true); //wraps lines when they outgrow the panel width
        chatWindow.setWrapStyleWord(true); //ensures that above line wrap occurs at word end
        setSize(400,320);
        this.setLocationRelativeTo(null); //places frame in center of screen
        setVisible(true);
    }

    //3. startRunning method
    public void startRunning(){
        try{
            connectToServer(); //unlike Server, no need to wait for connections. This connects to one specific Server.
            setupStreams();
            whileChatting();
        }catch(EOFException eofException){


            //Display timestamp for disconnection
            showMessage("\n\n" + fullTimeStamp);
            showMessage("\nConnection terminated by CLIENT! ");


        }catch(IOException ioException){
            ioException.printStackTrace();
        }finally{
                closeCrap();
            }

    }

    //4. Connect to Server
    void connectToServer() throws IOException{
        showMessage(" \n Attempting connection to SERVER... \n");
        connection = new Socket(InetAddress.getByName(serverIP), 6789);//Server IP can be added later
        showMessage(" Connected to: " +connection.getInetAddress().getHostName() ); //displays IP Address of Server
    }

    //5. Setup streams to send and receive messages
    private void setupStreams() throws IOException{ 
        output = new ObjectOutputStream(connection.getOutputStream());
        output.flush();
        input = new ObjectInputStream(connection.getInputStream());
        showMessage("\n Streams are now setup! \n");

    }

    //6. While chatting method
    private void whileChatting() throws IOException{


        //Display timestamp for connection
        showMessage("\n" + fullTimeStamp);

        ableToType(true);
        String timeStamp = new java.text.SimpleDateFormat("HH:mm:ss").format(new Date());//timestamp
        do{
            try{
                message = (String) input.readObject(); //read input, treat as String, store in message variable
                showMessage("\n" + message);
            }catch(ClassNotFoundException classNotfoundException){
                showMessage("\n I don't know that object type");
            }

        //***broken by timestamp?***    
        }while(!message.equalsIgnoreCase("SERVER " + "[" + timeStamp  + "]" + ": " + "END")); //Conversation happens until Server inputs 'End'

    }

    //7. Close the streams and sockets
    private void closeCrap(){
        showMessage("\n\nClosing streams and sockets...");
        ableToType(false);//disable typing feature when closing streams and sockets
        try{
            output.close();
            input.close();
            connection.close();
        }catch(IOException ioException){
            ioException.printStackTrace(); //show error messages or exceptions
        }
    }

    //8. Send Messages to Server
    private void sendMessage(String message){
        try{
        String timeStamp = new java.text.SimpleDateFormat("HH:mm:ss").format(new Date());//timestamp
        output.writeObject("CLIENT" + " [" + timeStamp + "]" + ": " + message);
        output.flush();
        showMessage("\nCLIENT" + " [" + timeStamp + "]" + ": " + message);
    }catch(IOException ioexception){
        chatWindow.append("\n Error: Message not sent!");
    }

    }

    //9.change/update chatWindow
    private void showMessage(final String m){
        SwingUtilities.invokeLater(
                new Runnable(){
                    public void run(){
                        chatWindow.setEditable(false); //disallows text editing in chatWindow
                        chatWindow.append(m); //appends text, which was passed in from above
                    }
                }

                );
    }

    //10. Lets user type
            private void ableToType(final boolean tof){
                SwingUtilities.invokeLater(
                        new Runnable(){
                            public void run(){
                                userText.setEditable(tof); //passes in 'true'
}
        }

                        );
            }
}

Server.Java:

import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

import java.util.Date;//timestamp functionality


public class Server extends JFrame{ //inherits from JFrame

    //1. Instance Variables

    private JTextField userText; //where messages are typed
    private JTextArea chatWindow; //where messages are displayed
    private String fullTimeStamp = new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(new Date());
    //fullTimeStamp - MM = months; mm = minutes; HH = 24-hour clock

    //setting up the streams
    private ObjectOutputStream output; //messages being sent by user
    private ObjectInputStream input; //messages being received by user;

    private ServerSocket server;

    private Socket connection; //Socket = sets up connection between one computer and another.

    //2. Constructor (GUI)
    public Server(){
    super("Mick's Instant Messenger [SERVER]"); //window title
    userText = new JTextField();
    userText.setEditable(false); //you cannot type anything, unless you are connected to someone else
    userText.addActionListener( 
        new ActionListener(){
        public void actionPerformed(ActionEvent event){
            sendMessage(event.getActionCommand());
            userText.setText(""); //Resets editable text field after you send message
        }
        }
    );
    add(userText, BorderLayout.SOUTH);//places user text input (JTextArea) field at bottom
    chatWindow = new JTextArea(15,30); //displays conversation
    add(new JScrollPane(chatWindow));
    chatWindow.setLineWrap(true); //wraps lines when they outgrow the panel width
    chatWindow.setWrapStyleWord(true); //ensures that above line wrap occurs at word end
    setSize(400,320);
    this.setLocationRelativeTo(null); //places frame in center of screen
    setVisible(true); //set visible on screen
    }

    //3.Setup and Run the Server
    public void StartRunning(){
        try{
            server = new ServerSocket(6789, 100);
            //Client connects at Port # 6789
            //100 = QueueLength - the backlog of clients who can wait at port #6789 to connect to the Server

            //while(true) ... means this while loop is going to run forever
            while(true){
                try{
                    waitForConnection();
                    setupStreams();
                    whileChatting(); //allows messages to pass back and forth through streams
                }catch(EOFException eofException){

                    //connect and have conversation
                    //Display timestamp for disconnection
                    showMessage("\n\n" + fullTimeStamp);
                    showMessage("\nConnection terminated by SERVER! "); //displays end of stream/connection


                }finally{
                    closeCrap();
                }
            }
        }catch(IOException ioException){
            ioException.printStackTrace(); //Displays info where there's an error!
    }
    }

        //4. wait for connection method, then display connection info
        private void waitForConnection() throws IOException{
            showMessage("\n SERVER : Waiting for user(s) to connect... \n "); //tells user Server is waiting for a connection
            connection = server.accept();
            //socket "connection" will accept connections. Creates a socket for each new connection.
            showMessage("Connected with " +connection.getInetAddress().getHostName()); //displays IP address and hostname

        }

        //5. setup streams method.. get streams to send and receive data
        private void setupStreams() throws IOException{
            output = new ObjectOutputStream(connection.getOutputStream());
            //creates pathway to allow connection to whichever computer the 'connnection' socket created.
            output.flush(); //clears data that gets left over in buffer when you try to connect to someone else. Flushes it over to the other person.
            input = new ObjectInputStream(connection.getInputStream());
            //no flush here, because you cannot flush someone else's stream
            showMessage("\n Streams are now setup! \n");

        }

        //6. during conversation method

        private void whileChatting() throws IOException{

            //Display timestamp for connection
            showMessage("\n" + fullTimeStamp);

            String message = "You are now connected! \n ";
            sendMessage(message);
            ableToType(true); //will allow user to type into text box after a connection
            String timeStamp = new java.text.SimpleDateFormat("HH:mm:ss").format(new Date());//timestamp

            do{
                try{
                    message = (String) input.readObject(); //read incoming message as a String and store in 'message' variable.
                    showMessage("\n" + message);//displays each message you receive on a new line
                }catch(ClassNotFoundException classNotFoundException){
                    showMessage("/n I don't know what object the user has sent!");
                }

            //***broken by timestamp?***    
            }while(!message.equalsIgnoreCase("CLIENT " + "[" + timeStamp  + "]" + ": " + "END")); //allows conversation until Client enters "END"

        }

        //7. Close Crap method - close streams and sockets after you are finished chatting
        private void closeCrap(){
            showMessage("\n\n Closing connections... \n");
            ableToType(false);
            try{
                output.close(); //close your stream to other users
                input.close(); //close incoming streams
                connection.close(); //close the socket

            }catch(IOException ioException){
                ioException.printStackTrace();
            }
}
        //8. send message method - send message to client
        private void sendMessage(String message){
            try{

                //writeObject method is built into Java.

                String timeStamp = new java.text.SimpleDateFormat("HH:mm:ss").format(new Date());//timestamp
                output.writeObject("SERVER" + " [" + timeStamp + "]" + ": " + message);
                showMessage("\nSERVER" + " [" + timeStamp + "]" + ": " + message); //shows the ouput message in our conversation window
                output.flush();
            }catch(IOException ioException){
                chatWindow.append("ERROR: Unable to send message!");
            }
        }

        //9. updates chatWindow - instead of creating entirely new GUI each time
        private void showMessage(final String text){
        SwingUtilities.invokeLater(
                new Runnable(){
                    public void run(){
                        chatWindow.setEditable(false); //disallows text editing in chatWindow
                        chatWindow.append(text); //appends text, which was passed in from above
                    }
                }
                );  
}
        //10. Lets user type
        private void ableToType(final boolean tof){
            SwingUtilities.invokeLater(
                    new Runnable(){
                        public void run(){
                            userText.setEditable(tof); //passes in 'true'
                        }
                    }
                    );  

        }
}

1 个答案:

答案 0 :(得分:0)

ServerSockets在java中工作的方式是,想要连接的客户端排队等待,直到你可以ServerSocket.accept()将第一个队列出列并返回它。因此,要将多个客户端连接到单个服务器,您需要多次调用accept。如您所知,接受也将阻止,直到客户实际可用。

您需要创建一个处理单个客户端连接的连接处理程序线程,然后在连接新客户端时关闭线程。基本上

while(true){
    Socket s=server.accept();
    new ConnectionHandlerThread(s).start();
    // write the ConnectionHandlerThread yourself
}

我会为你写一些更详细的代码,但我在移动设备上抱歉。