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



我的程序说明

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

问题所在

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

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


主文件

服务器.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();
	}
}

客户端.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();
	}

}


服务器使用的对象.java

用户.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) {
		}
	}
}

聊天室.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();
}

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

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

然后由于没有初始化chatRooms链接列表而有一些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

最新更新