具有两个并发用户的Java UDP Chat应用程序

时间:2014-04-08 00:30:25

标签: java multithreading udp chat datagram

我正在尝试实现一个聊天应用程序,其中两个用户(客户端)可以在彼此之间发送和接收消息。我有两个类(服务器和客户端),并且可以在某些情况下成功地在客户端之间发送消息。我现在有两个问题。

第一个问题是,如果按照以下确切的顺序执行以下步骤,则只能成功传递消息:

  1. 启动Server类的一个实例,然后启动Client类的两个实例
  2. 输入其中一个客户端的用户名(此处称为client1)
  3. 输入其他客户端的用户名(从此处称为客户端2)
  4. 从连接到服务器的第一个客户端(client1)发送消息
  5. 从连接到服务器的第二个客户端(client2)发送消息
  6. 继续进行对话,直到任一客户发送' End_of_Communication'
  7. 如果切换了步骤5和6,那么第二个客户端在第一个客户端之前发送消息,第一个客户端永远不会收到该消息。所有后续消息都已正确传递,但我希望无论哪个客户端发起对话,都会传递第一条消息。

    第二个问题是' End_of_Communication'串。如果任一客户端发送了“End_of_Communication”'作为消息,服务器以及两个客户端都应显示" [用户名]已断开连接",然后两个客户端都应终止。服务器应继续运行,并打印初始"侦听客户端请求"信息。

    现在,断开连接消息将打印在两个客户端上,但两个客户端程序都没有实际终止。服务器不会打印任何断开连接消息,也不会转到while循环的顶部。我怀疑这是因为客户端程序没有终止,所以服务器阻塞,等待“clientThread [X] .join()'”。服务器和客户端类的代码都在下面复制/粘贴。

    服务器类代码:

    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetAddress;
    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.Arrays;
    import java.util.Date;
    
    public class Server {
    
        public static final int MAXIMUM_DATAGRAM_SIZE = 255;        // Maximum size of datagram
        public static final String ECS = "End_of_Communication";        // End Communication String
    
        public static void main(String[] args) throws IOException {
    
            byte[] clientData1 = new byte[MAXIMUM_DATAGRAM_SIZE];       // Buffer for first client requesting to connect
            byte[] clientData2 = new byte[MAXIMUM_DATAGRAM_SIZE];       // Buffer for second client requesting to connect
            DatagramPacket clientPacket1 = new DatagramPacket(clientData1, clientData1.length);     // Packet for first client requesting to connect
            DatagramPacket clientPacket2 = new DatagramPacket(clientData2, clientData2.length);     // Packet for second client requesting to connect
            DatagramSocket serverSocket;        // Socket for server to listen on
            int serverPort;     // Port to start serverSocket on
            int clientPort1;
            int clientPort2;
            Runnable clientRun1;        // Runnable object for first client requesting to connect
            Runnable clientRun2;        // Runnable object for second client requesting to connect
            Thread clientThread1;       // Thread for first client requesting to connect
            Thread clientThread2;       // Thread for second client requesting to connect
            String clientAlias1;        // Alias of first client requesting to connect
            String clientAlias2;        // Alias of second client requesting to connect
    
            // Check for correct # of arguments
            if(args.length != 1)
                throw new IllegalArgumentException("Parameter(s): <Port>");
    
            // Initialize serverPort and serverSocket
            serverPort = Integer.parseInt(args[0]);
            serverSocket = new DatagramSocket(serverPort);
    
            // Loop forever and accept requests from clients
            while(true) {
    
                // Block until a client request is received, and get client alias and 
                System.out.println("[" + getTime() + "] | Listening for client requests... |");
                serverSocket.receive(clientPacket1);
                clientAlias1 = new String(clientPacket1.getData());
                clientPort1 = clientPacket1.getPort();
                System.out.println("[" + getTime() + "] | Connected to first client <" + clientAlias1 
                        + "> with socket address [" + clientPacket1.getSocketAddress() + "] |");
    
                // Block until a second client request is received, and get its alias
                serverSocket.receive(clientPacket2);
                clientAlias2 = new String(clientPacket2.getData());
                clientPort2 = clientPacket2.getPort();
                System.out.println("[" + getTime() + "] | Connected to second client <" + clientAlias2 
                        + "> with socket address [" + clientPacket2.getSocketAddress() + "] |");
    
                // Send clientAlias2 to first client
                clientData2 = clientAlias2.getBytes();
                clientPacket1.setData(clientData2);
                serverSocket.send(clientPacket1);
                // Send clientAlias1 to second client
                clientData1 = clientAlias1.getBytes();
                clientPacket2.setData(clientData1);
                serverSocket.send(clientPacket2);
    
                // Send second client's port to first client
                clientData2 = String.valueOf(clientPort2).getBytes();
                clientPacket1.setData(clientData2);
                serverSocket.send(clientPacket1);
    
                clientData1 = String.valueOf(clientPort1).getBytes();
                clientPacket2.setData(clientData1);
                serverSocket.send(clientPacket2);
    
                // Create a new thread for each client request received
                clientRun1 = new ServerThread(serverSocket, clientPacket1, clientPacket2, clientAlias1);
                clientThread1 = new Thread(clientRun1);
                clientRun2 = new ServerThread(serverSocket, clientPacket2, clientPacket1, clientAlias2);
                clientThread2 = new Thread(clientRun2);
    
                // Start each thread
                clientThread1.start();
                clientThread2.start();
    
                // Wait for threads to finish before looping again
                try{
                    clientThread1.join();
                    clientThread2.join();
                } catch(InterruptedException interrupt) {
                    System.out.println("InterruptedException: " + interrupt);
                }// End try/catch block
    
            }// End while loop
        }// End main
    
        private static String getTime() {
            DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
            Date date = new Date();
            return dateFormat.format(date);
        }
    
    }// End Server
    
    /*************************************End Server**************************************/
    /*************************************************************************************/
    /*********************************Start ServerThread**********************************/
    
    class ServerThread implements Runnable {
    
        protected DatagramSocket socket;
        protected DatagramPacket readPacket;
        protected DatagramPacket writePacket;
        protected InetAddress readAddress;
        protected InetAddress writeAddress;
        protected int readPort;
        protected int writePort;
        protected String userName;
    
        public ServerThread(DatagramSocket serverSocket, DatagramPacket readPacket, DatagramPacket writePacket, String userName) {
            this.socket = serverSocket;
            this.readPacket = readPacket;
            this.writePacket = writePacket;
            this.readAddress = readPacket.getAddress();
            this.writeAddress = writePacket.getAddress();
            this.readPort = readPacket.getPort();
            this.writePort = writePacket.getPort();
            this.userName = userName;
        }
    
        public void run() {
            try {
                String message;
                byte[] readBytes = new byte[Server.MAXIMUM_DATAGRAM_SIZE];
                byte[] writeBytes = new byte[Server.MAXIMUM_DATAGRAM_SIZE];
    
                while(true) {
    
                    // Create byte array to read data from packet into
                    readBytes = new byte[Server.MAXIMUM_DATAGRAM_SIZE];
                    readPacket = new DatagramPacket(readBytes, readBytes.length, readAddress, readPort);
    
                    // Block until packet is received, and extract its data
                    socket.receive(readPacket);
                    if(readPacket.getPort() == writePort)
                        continue;
                    message = new String(readPacket.getData());
                    if(message.equals(Server.ECS))
                        System.out.println("[" + getTime() + "] | <" + userName + "> has disconnected. |");
                    readBytes = Arrays.copyOfRange(readPacket.getData(), readPacket.getOffset(), readPacket.getOffset()+readPacket.getLength());
    
                    // Create byte array to write extracted data to
                    writeBytes = new byte[Server.MAXIMUM_DATAGRAM_SIZE];
                    writeBytes = readBytes;
                    writePacket = new DatagramPacket(writeBytes, writeBytes.length, writeAddress, writePort);
    
                    // Send the packet to its destination
                    socket.send(writePacket);
    
    
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
        }
    
        private String getTime() {
            DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
            Date date = new Date();
            return dateFormat.format(date);
        }
    
    
    }
    

    客户类代码:

    import java.net.DatagramSocket;
    import java.net.DatagramPacket;
    import java.net.InetAddress;
    import java.net.SocketException;
    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    
    public class Client {
    
        public static final int MAXIMUM_DATAGRAM_SIZE = 255;        // Maximum size of datagram
        public static final String ECS = "End_of_Communication";        // End Communication String
    
        public static void main(String[] args) throws IOException {
    
            BufferedReader userInput = new BufferedReader(new InputStreamReader(System.in));        // BufferedReader to get user input
            byte[] myData = new byte[MAXIMUM_DATAGRAM_SIZE];
            byte[] clientData = new byte[MAXIMUM_DATAGRAM_SIZE];
            DatagramSocket clientSocket;        // Socket for this client to connect to server
            DatagramPacket myDataPacket;
            DatagramPacket clientDataPacket;
            InetAddress serverIP;       // IP address of server
            int serverPort;     // Port that server is listening on
            int clientPort;     // Port to send messages to
            WriteThread write;      // Thread to write data to the server
            ReadThread read;        // Thread to read data from the server
            String userName;        // Alias to use for this client
            String clientName;      // Alias to use for other client
    
            // Check for correct # of arguments
            if((args.length < 1) || (args.length > 2))
                throw new IllegalArgumentException("Parameter(s): <Server> [<Port>]");
    
            // Create DatagramSocket on specified port and IP address
            serverIP = InetAddress.getByName(args[0]);
            serverPort = Integer.parseInt(args[1]);
            clientSocket = new DatagramSocket();
    
            // Connect the socket the server 
            clientSocket.connect(serverIP, serverPort);
    
            // Collect data to connect
            System.out.println("Please enter username(No Spaces): [Guest]");
            userName = userInput.readLine();
            if(userName.isEmpty())
                userName = "Guest";
    
            System.out.println("| Username set to <" + userName + ">. Sending to server... |");
            myData = userName.getBytes();
    
            // Send packet with userName to server
            myDataPacket = new DatagramPacket(myData, myData.length, serverIP, serverPort);
            clientSocket.send(myDataPacket);
    
            // Create packet to receive data about the other client from the server
            clientDataPacket = new DatagramPacket(clientData, clientData.length);
            clientDataPacket.setLength(MAXIMUM_DATAGRAM_SIZE);
            clientSocket.receive(clientDataPacket);
            clientName = new String(clientDataPacket.getData());
            clientData = new byte[MAXIMUM_DATAGRAM_SIZE];
            clientDataPacket = new DatagramPacket(clientData, clientData.length);
            clientDataPacket.setLength(MAXIMUM_DATAGRAM_SIZE);
            clientSocket.receive(clientDataPacket);
            clientPort = Integer.parseInt((new String(clientDataPacket.getData())).trim());
    
            // Create and start threads to write to and read data from the server
            write = new WriteThread(clientSocket, serverPort, userName);
            read = new ReadThread(clientSocket, clientName);
            write.start();
            read.start();
    
            // Wait for threads to finish
            try {
                write.join();
                read.join();
            } catch(InterruptedException interrupt) {
                System.out.println("InterruptedException: " + interrupt);
            }// End try/catch block
    
        }// End main
    }// End Client
    
    /*************************************End Client**************************************/
    /*************************************************************************************/
    /**********************************Start WriteThread**********************************/
    
    class WriteThread extends Thread implements Runnable {
    
        protected InetAddress serverIP;     // IP address of the server
        protected int serverPort;       // Port server is listening on
        protected DatagramSocket writeSocket;       // DatagramSocket to SEND data to server
        protected String userName;
    
        public WriteThread(DatagramSocket clientSocket, int serverPort, String userName) {
            this.writeSocket = clientSocket;
            this.serverPort = serverPort;
            this.serverIP = clientSocket.getInetAddress();
            this.userName = userName;
        }
    
        public void run() {
    
            try {
                BufferedReader userInput = new BufferedReader(new InputStreamReader(System.in));
                String writeString;
                byte[] writeBytes;
                DatagramPacket writePacket;
    
                while(true) {
                    writeBytes = new byte[Client.MAXIMUM_DATAGRAM_SIZE];
                    writeString = userInput.readLine();
                    writeBytes = writeString.getBytes();
                    writePacket = new DatagramPacket(writeBytes, writeBytes.length, serverIP, serverPort);
                    writeSocket.send(writePacket);
                    if((writeString).equals(Client.ECS))
                        break;
                    System.out.println("[" + getTime() + "]<" + userName + "> " + new String(writePacket.getData()));
                }// End while
    
                // End_of_Communiation received, print disconnect message
                System.out.println("[" + getTime() + "] | <" + userName + "> has disconnected. |");
                writeBytes = new byte[Client.MAXIMUM_DATAGRAM_SIZE];
                writeBytes = writeString.getBytes();
                writePacket = new DatagramPacket(writeBytes, writeBytes.length, serverIP, serverPort);
                writeSocket.send(writePacket);
    
            } catch (IOException ex) {
                System.out.println("IOException: " + ex);
            }// End try/catch block
        }// End run()
    
        private String getTime() {
            DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
            Date date = new Date();
            return dateFormat.format(date);
        }
    
    }// End writeThread
    
    /***********************************End WriteThread***********************************/
    /*************************************************************************************/
    /**********************************Start ReadThread***********************************/
    
    class ReadThread extends Thread implements Runnable {
    
        protected InetAddress serverIP;     // IP address of the server
        protected int serverPort;       // Port server is listening on
        protected DatagramSocket clientSocket;      // DatagramSocket to READ data to server
        protected String clientName;
    
        public ReadThread(DatagramSocket clientSocket, String clientName) throws SocketException {
            this.serverIP = clientSocket.getInetAddress();
            this.serverPort = clientSocket.getPort();
            this.clientSocket = clientSocket;
            this.clientName = clientName;
        }
    
    
        public void run() {
            try {
                byte[] readData = new byte[Server.MAXIMUM_DATAGRAM_SIZE];       // Buffer for data READ from server
                DatagramPacket readPacket;      // Packet to READ data to server
                String readMessage;     // String of the message READ from server
    
                // Loop until user requests disconnect
                while(true) {
    
    
                    // Set up datagram packet to READ from server
                    readPacket = new DatagramPacket(readData, readData.length);
    
                    // Wait for a packet to READ from server
                    clientSocket.receive(readPacket);
    
                    // Extract and print message READ from server
                    readMessage = new String(readPacket.getData(), 0, readPacket.getLength());
                    if(readMessage.equals(Client.ECS))
                        break;
                    System.out.println("[" + getTime() + "]<" + clientName + "> " + readMessage);
                }// End while
    
                // End_of_Communication received, exit
                System.out.println("[" + getTime() + "] | <" + clientName + "> has disconnected. |");
    
            } catch(IOException ex) {
                System.err.println("IOException caught: " + ex);
            }// End try/catch block
            return;
        }// End run
    
        private String getTime() {
            DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
            Date date = new Date();
            return dateFormat.format(date);
        }
    
    }// End readThread
    

1 个答案:

答案 0 :(得分:0)

我看到的第一件事是你没有关闭你的插座。这意味着即使没有数据传输,连接仍然存在。

对于任何IO来说,正确处理关闭行为非常重要,因此连接不会保持活动状态。这通常是通过try,catch,finally块完成的。以您的ReadThread为例

try {
    // Loop until user requests disconnect
    while(true) {
            //...bla bla...
    }// End while

    // End_of_Communication received, exit
    System.out.println("[" + getTime() + "] | <" + clientName + "> has disconnected. |");

} catch(IOException ex) {
    System.err.println("IOException caught: " + ex);
} finally {
    // here we want to close the socket. 
    //Even if an exception is thrown we always want to make sure that the client connection is terminated.
    clientSocket.close();
}

我敢打赌,如果你关闭所有客户,你的问题就会消失。此外,在完成资源完成后养成正确关闭资源的习惯也很重要。

如果你想了解更多关于尝试的信息,请抓住最后一个阻止,你可以在这里阅读http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html