繁体   English   中英

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

[英]Java UDP Chat Application with two concurrent users

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

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

  1. 启动Server类的一个实例,然后启动Client类的两个实例
  2. 输入其中一个客户端的用户名(从此处开始称为client1)
  3. 输入其他客户端的用户名(从此处开始称为客户端2)
  4. 从连接到服务器的第一个客户端发送消息(client1)
  5. 从连接到服务器的第二个客户端发送消息(client2)
  6. 继续进行对话,直到任一客户端发送“ End_of_Communication”

如果切换了步骤5和6,则第二个客户端在第一个客户端之前发送一条消息,则第一个客户端永远不会收到该消息。 所有后续消息均已正确传递,但是无论哪个客户端发起对话,我都希望传递第一条消息。

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

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

服务器类的代码:

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

我首先看到的是您没有关闭插槽。 这意味着即使没有数据传输,连接仍然有效。

正确处理关闭行为对于任何IO都非常重要,这样连接就不会活跃。 这通常是通过尝试,捕获和最终阻止来完成的。 以您的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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM