如何将这个简单聊天客户端中的输入和广播功能分离到不同的线程中?



这是我用来实例化测试客户端的客户端类。它应该做的只是连接到服务器,允许用户输入一个简单的字符串作为消息,并从服务器接收消息。问题似乎是nextLine函数阻止从服务器接收消息,直到用户发送新消息。我知道我可以使用线程来解决这个问题,但我不确定如何解决。

package Thread_Test;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;
class Client{
private Socket socket;
private int port = 9000;
private InetAddress host;
private Scanner scanner;
private Scanner receiver;
private PrintWriter printWriter;
private String message = "";
public void runClient(){
try {
host = InetAddress.getLocalHost();
socket = new Socket(host, port);
scanner = new Scanner(System.in);
receiver = new Scanner(socket.getInputStream());
printWriter = new PrintWriter(socket.getOutputStream(), true);
}
catch(IOException iex){
iex.printStackTrace();
}
while(!message.toLowerCase().equals("close")){
System.out.print("Outgoing message: ");
message = scanner.nextLine();
printWriter.println(message);
message = receiver.nextLine();
System.out.println("Incoming message: " + message);
}
}
}

这只是我用来实例化两个测试客户端以在 IDE 中测试代码的 ClientHandler 类。

package Thread_Test;
class ClientHandlerOne{
public static void main(String[] args) {
Client clientOne = new Client();
clientOne.runClient();
}
}
class ClientHandlerTwo{
public static void main(String[] args) {
Client clientTwo = new Client();
clientTwo.runClient();
}
}

这是我的服务器类,首当其冲。我使用它在我的私有 ConnectionHandler 类中创建一个新线程,这样每当新用户连接时,它都会存储收到的第一个字符串以及将其发送到哈希映射中的 PrintWriter。这样做的原因是我打算发送的第一个字符串始终是用户名(主要只是为了方便和测试目的)。它应该遍历哈希映射,将收到的消息发送到所有不是发送它的客户端的客户端,以防止发送者从服务器接收自己的消息副本。

package Thread_Test;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
//import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;

public class Server{
private ServerSocket serverSocket;
private int portNumber = 9000;
private Socket socket;
private HashMap<String, PrintWriter> hashMap = new HashMap<>();

public static void main(String[] args) {
Server server = new Server();
server.output();
}
public Server(){
try{
serverSocket = new ServerSocket(portNumber);
}
catch(IOException iex){
iex.printStackTrace();
}
}
//While loop to generate new ConnectionHandler for each connected client.
public void output(){
try{
while (true) {
socket = serverSocket.accept();
ConnectionHandler CH = new ConnectionHandler(socket);
Thread connection = new Thread(CH);
connection.start();
}
}
catch(IOException iex){
iex.printStackTrace();
}
}
//Private inner class to help handle individual client connections to server.
private class ConnectionHandler implements Runnable {
private Socket connectionSocket;
private Scanner scanner;
private String message;
private PrintWriter pw;

public ConnectionHandler(Socket socket) {
this.connectionSocket = socket;
}
//next step is to test the hashmap and then iterate through it, getting each printwriter
//and using it to send the message to everyone.
public void run() {
try{
scanner = new Scanner(connectionSocket.getInputStream());
pw = new PrintWriter(connectionSocket.getOutputStream(), true);
message = scanner.nextLine();
hashMap.put(message,pw);
//The issue is that the readLine function in the client class waits for a new input, so
//This broadcast function is only called when I actually enter a new message in a client.
//But how to separate the broadcast and input threads?
while (!message.toLowerCase().equals("close")) {
System.out.println(message);
for(PrintWriter out : hashMap.values()){
if(out!=pw) {
System.out.println("Sending message.");
out.println(message);
}
}
message = scanner.nextLine();
}
System.out.println(hashMap.toString());
connectionSocket.close();
}
catch (IOException iex) {
iex.printStackTrace();
}
}
}

}

如果我的任何代码草率或不遵循良好实践,我深表歉意,因为我仍在学习。任何关于如何或在哪里实例化新线程的输入,该线程将允许我的客户端进程在另一个客户端进程发送消息后立即接收消息,这将非常有帮助。

您可以使用BufferReader代替ScannerBufferReader可以检查是否有可用的缓冲数据而不会阻塞:

class Client {
private Socket socket;
private int port = 9000;
private InetAddress host;
private BufferedReader scanner;
private BufferedReader receiver;
private PrintWriter printWriter;
private String message = "";
public void runClient() {
try {
host = InetAddress.getLocalHost();
socket = new Socket(host, port);
scanner = new BufferedReader(new InputStreamReader(System.in));
receiver = new BufferedReader(new InputStreamReader(socket.getInputStream()));
printWriter = new PrintWriter(socket.getOutputStream(), true);
} catch (IOException iex) {
iex.printStackTrace();
}
while (!message.toLowerCase().equals("close")) {
try {
if (scanner.ready()) {
System.out.print("Outgoing message: ");
message = scanner.readLine();
printWriter.println(message);
}
if (receiver.ready()) {
message = receiver.readLine();
System.out.println("Incoming message: " + message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} 

最新更新