为 Java 中的自定义聊天室创建服务器 class 和客户端 class

Creating a Server class and Client class for a custom chatroom in Java

我的程序说明

我使用 Java 创建了一个聊天室服务器,它由 3 个 classes、一个服务器 class、一个用户 class 和一个聊天室 class。我还创建了一个客户端 class 来与服务器交互。每次客户端连接到服务器时,服务器都会创建一个用户对象。每个用户对象都跟踪用户所在的聊天室,运行 是一个监听用户输入的线程。一个服务器对象循环遍历每个聊天室,看看它是否在上周被使用过。

问题

我想知道如何使用客户端对象实际连接到我的服务器。目前我打开了两个 Eclipse 实例。我 运行 我的服务器程序在一个,我的客户端程序在另一个,但我在任何一个控制台中都没有收到任何东西,这应该发生,因为服务器应该向客户端发送信息,然后客户端将显示在控制台。客户端的人然后可以提供客户端将接收并发送到服务器的输入。

我想知道为什么现在什么都没有发生,我可以做些什么改进。


主要文件

Server.java

    /*
     * Creates a server that can host up to 10 clients who can join chat rooms, post messages in chatrooms, and view posts made by other clients within the same chat room
     */
    public class Server implements Runnable{
     protected ArrayList<User> userList; //A list of users, where each user is a client connected to the server
     protected LinkedList<Chatroom> chatRooms; //A list of chatrooms where each client can post messages in, where messages can be seen by all clients in the chatroom
     private ServerSocket serverSocket; //The socket for the server
     
     /*
      * Constructor for the server class. Initializes the server attributes,
      */
     public Server() {
      this.userList = new ArrayList<User>(10);
      this.chatRooms = new LinkedList<Chatroom>();
      try {
       this.serverSocket = new ServerSocket(5000);
      }catch (IOException e) {
       e.printStackTrace();
      }
     }
     
     /*
      * Creates a new user when a client connects to the server, and starts a user thread
      */
     public void createUser() {
      try {
       Socket userSocket = serverSocket.accept();
       Thread user = new Thread(new User(userSocket, this));
       user.start();
      } catch (IOException e) {
       e.printStackTrace();
      }
     }
     
     /*
      * Creates a chatroom for clients to interact in
      * @param roomName: The name of the chat room to be created
      */
     protected Chatroom createChatRoom(String roomName) {
      Chatroom room = new Chatroom(roomName);
      return room;
     }
     
     /*
      * Receives messages from clients and performs actions based on the requests of the client
      * (non-Javadoc)
      * @see java.lang.Thread#run()
      */
     public void run() {
      long currentTime;
      while(true) {
       try {
        currentTime = System.currentTimeMillis() / 1000;
        //Loop through each chatroom and check if the chatroom has been used(joined or had a message sent to it) and remove that chatroom if it hasn't been used in a week
        for (int i = 0; i < chatRooms.size(); i++) {
         if (currentTime - 604800 >= chatRooms.get(i).dateLastUsed) {
          chatRooms.remove(i);
          //Also remove the chatroom from clients lists of chatrooms
         }
        }
        
       } catch (Exception e) {
        e.printStackTrace();
       }
      }
     } 
     
     public static void main(String args[]) {
      Server server = new Server ();
      server.run();
     }
    }

Client.java

    public class Client extends Thread{
     private String ip = "127.0.0.1";
     private int port = 5000 ;
     private Socket socket;
     private DataInputStream iStream;
     private DataOutputStream oStream;
     private String input;
     
     public Client() {
      try {
       this.socket = new Socket(ip, port);
       this.iStream = new DataInputStream(socket.getInputStream());
       this.oStream = new DataOutputStream(socket.getOutputStream());
      }catch (Exception e) {
       e.printStackTrace();
      }
     }
     
     /*
      * Sends a message to the user
      * @param message: The message to be sent to the user
      */
     protected void send (String message) {
      try {
       oStream.writeUTF(message);
      } catch (IOException e) {
       e.printStackTrace();
      }
     }
     
     /*
      * Closes the connection to the client
      */
     protected void close () {
      try {
       iStream.close();
       oStream.close();
       socket.close();
      } catch (IOException e) {
       e.printStackTrace();
      }
     }
     
     /*
      * Runs a thread for the client to constantly receive the clients input(non-Javadoc)
      * @see java.lang.Thread#run()
      */
     public void run() {
      try {
       Scanner reader = new Scanner(System.in);
       input = iStream.readUTF();
       String userInput;//Check if there is input from the user
       while (input != null) {
        input = iStream.readUTF();
        System.out.println(input);
        userInput = reader.next();
        oStream.writeUTF(userInput);
       }
       reader.close();
      }catch (IOException e) {
       e.printStackTrace();
      }
     }
     
     public static void main(String args[]) {
      Client client = new Client();
      client.run();
     }
    
    }


Server.java

使用的对象

User.java

    //Each user represents a client that has connected to the server
    public class User implements Runnable{
     private DataInputStream inputStream;
     private DataOutputStream outputStream;
     private Socket socket;
     private String name;
     protected LinkedList<Chatroom> chatRooms;
     private String input;
     private Server server;
     
     /*
      * User Constructor, create a user for each client connecting to the server
      * @socket The socket that the user will be communicated through
      * The client is prompted to create a name for themself, they are they prompted to do an action.
      */
     public User(Socket socket, Server server) {
      this.socket = socket;
      this.server = server;
      try {
       inputStream = new DataInputStream(socket.getInputStream());
       outputStream = new DataOutputStream(socket.getOutputStream());
       outputStream.writeUTF("Enter a name");
       this.name = inputStream.readUTF();
       String message = "Create a chatroom: create \nList Chat Rooms: list \n Join Chat Room: join \n Leave Chat Room: Leave";
       send(message);
      } catch (IOException e) { 
      }
     }
     
     /*
      * Returns the current amount of chatrooms this user is in
      */
     protected int chatRoomLength () {
      return this.chatRooms.size();
     }
     
     /*
      * Gets the most recent input from the user
      */
     protected String getInput() {
      return input;
     }
     
     /*
      * Puts a user/client in a chatroom
      * @param cRoom: The chatroom that the user will join 
      */
     protected void joinRoom (Chatroom cRoom) {
      chatRooms.add(cRoom);
     }
     
     /*
      * Removes a user/client from a chatroom
      */
     protected void leaveRoom (Chatroom c) {
      chatRooms.removeFirstOccurrence(c);
     }
     
     /*
      * Sends a message to the user
      * @param message: The message to be sent to the user
      */
     protected void send (String message) {
      try {
       outputStream.writeUTF(message);
      } catch (IOException e) {
      }
     }
     
     /*
      * Closes the connection to the client
      */
     protected void close () {
      try {
       inputStream.close();
       outputStream.close();
       socket.close();
      } catch (IOException e) {}
     }
     
     /*
      * Runs a thread for the client to constantly receive the clients input(non-Javadoc)
      * @see java.lang.Thread#run()
      */
     public void run() {
      try {
       input = inputStream.readUTF(); //Check if there is input from the user
       //if the user has disconnected from the server, remove them from the list
       if (input == null) {
        this.close();
        this.server.userList.remove(this);
       }else if (input.equals("create")){ //create a chat room
        this.send("Name the Chatroom");
        input = this.getInput();
        Chatroom c = this.server.createChatRoom(input);
        this.joinRoom(c);
       }else if (input.equals("list")) { //List the current chatrooms
        String rooms = "";
        for (int j = 0; j< server.chatRooms.size(); j++) {
         rooms = rooms + server.chatRooms.get(j).getName() + "\n";
        }
        this.send(rooms);
       }else if (input.equals("join")) { //Join the user to a chat room
        int end = chatRooms.size();
        if (end == 0) {
         this.send("There's currently no chat rooms");
        }else { 
         this.send("Which room would you like to join");
         input = this.getInput();
         for (int k = 0; k < end; k++) {
          if (chatRooms.get(k).getName().equals(input)) {
           Chatroom joinRoom = chatRooms.get(k);
           this.joinRoom(joinRoom);
           String message = "Chatroom " + input + " messages. \n";
           //Print the current messages in the chatroom to the user
           for (int j = 0; j < joinRoom.messages.size(); j++ ) {
            message = message + joinRoom.messages.get(j) + "\n";
           }
           this.send(message);
          } else if (k == end - 1) {
           this.send("There's no chat rooms by that name");
          }
         }
        }
       }else if (input.equals("leave")) { //Remove the user from a chatroom
        int end = this.chatRoomLength(); //if the chatroom list of the user is empty
        if (end == 0) {
         this.send("You are not in any Chat Rooms");
        }else {
         this.send("Which room would you like to leave");
         input = this.getInput();
         
         for (int m = 0; m < end; m++) { //find the chatroom by the same name
          if (this.chatRooms.get(m).getName().equals(input)) {
           this.chatRooms.remove(m);
           this.send("Great! You've been removed from" + input);
          } else if (m == end - 1) {
           this.send("You're not in a chatroom named" + input);
          }
         }
        }
       }else { //All other input is interpreted as a message to be posted in the chatrooms that the user is in
        int end = this.chatRoomLength();
        if (end == 0) {
         this.send("You can't write to any chat rooms because you are not in any");
        }
        for (int m = 0; m < end; m++) { //Add the users message to ALL the chatrooms the user is in
         Chatroom c = this.chatRooms.get(m);
         c.addMessage(input);
         //Send this added message to all the users in this chatroom
         for (int n = 0; n < c.users.size(); n++) {
          User u = c.users.get(n);
          u.send("Chatroom" + c.getName() + ":" + input);
         }
        }
       }
       
      }catch (IOException e) {
      }
     }
    }

Chatroom.java

    public class Chatroom {
     private String name;  //Name of the chatroom
     protected LinkedList<String> messages; //List of text messages that have been sent by users to the chatroom and are displayed in the chatroom
     protected long dateLastUsed;  //The last time the chatroom was joined or had a message sent to it
     protected LinkedList<User> users; //The clients/users that are currently in the chatroom
     
     /*
      * Chatroom constructor
      * @param name The name of the chatroom, as determined by the user creating it
      */
     public Chatroom(String name) {
      dateLastUsed = System.currentTimeMillis() / 1000;  //Sent the time that the chatroom was used last to the current UNIX Epoch time
      messages = new LinkedList<String>();
      this.name = name;
     }
     
     /* 
      * Adds a message into the chatroom
      * @param message The message to be added to the chatroom
      */
     protected void addMessage(String message) {
      messages.add(message);
      dateLastUsed = System.currentTimeMillis() / 1000;
     }
     
     /*
      * Returns the name of the chatroom
      * @return String equal to the name of the chatroom
      */
     protected String getName() {
      return this.name;
     }
     
    }

首先,没有对createUser()的调用具有接受套接字连接的代码。

接下来是 run() 函数中的这段代码,

try {
            Scanner reader = new Scanner(System.in);
            input = iStream.readUTF();
            String userInput;//Check if there is input from the user
            while (input != null) {
                input = iStream.readUTF();
                System.out.println(input);
                userInput = reader.next();
                oStream.writeUTF(userInput);
}

一旦套接字被接受,服务器打印 Enter a name 这是存储的输入, 在 while 循环内还有另一个 readUTF() 调用。

readUTF() 调用的问题在于它正在阻塞,即。如果没有来自服务器的 writeUTF() 调用,它会等待数据。

我通过以下代码片段解决了这个问题,

try {
            Scanner reader = new Scanner(System.in);
            String userInput;//Check if there is input from the user
            do {
                input = iStream.readUTF();
                System.out.println(input);
                userInput = reader.next();
                oStream.writeUTF(userInput);
            } while (input != null);
            reader.close();
        }

即使这也不是最佳解决方案,因为它需要服务器每次都向流中写入一些内容。

另一种解决方案是使用不同的线程进行读写,这样我们就可以创建像聊天这样的全双工,而不像这种半双工。

然后由于chatRoomsLinkedList没有初始化,所以有一些NPE,

加上一些逻辑错误,例如没有将聊天室添加到其列表中。

错误代码:

protected Chatroom createChatRoom(String roomName) {
        Chatroom room = new Chatroom(roomName);
        return room;
    }

更正后的代码:

protected Chatroom createChatRoom(String roomName) {
        Chatroom room = new Chatroom(roomName);
        this.chatRooms.add(room);
        return room;
    } 

解决所有错误的最佳方法是 Github 和一些贡献者 :p