您有一个协议,客户端可以读取三种可能的消息之一:
-
OK(你的举动被接受了)
-
ERR(你的举动被拒绝了)
move PLAYERID <co-ord1,co-ord2>
合理的假设是消息OK 和ERR 只会被发送回请求move 的套接字。然而,合法的move 会广播给所有其他玩家(可能不包括移动的玩家)。
由于您可以收到不请自来的响应(其他玩家做出的动作),因此您已经正确地创建了一个监听线程。您还没有描述您的应用程序在收到来自另一个客户端的move 消息时所采取的操作,但我会假设您的侦听器线程会处理这种情况。剩下的就是如何协调您的 move 命令,以及对将出现在侦听器线程中的响应的响应。
为了同步您的move 命令的提交和响应,将使用一个阻塞队列(称为queue),并在客户端和侦听器之间共享。它的形式是:
客户:
out.println(command); // Where out is the socket PrintWriter stream
String response = queue.take(); // Where queue is the BlockingQueue
// Process either `OK` or `ERR`
监听线程:
while ((command = in.readLine()) != null) {
if (command.equalsIgnoreCase("OK") || command.equalsIgnoreCase("ERR"))
queue.put(command);
else if (command.startsWith("move")) {
// Process a move
}
else
System.out.println("Unrecognized command="+command);
}
如您所见,客户端只需提交一个命令,并阻止响应“OK”或“ERR”。处理其他玩家移动的需求已移至侦听器线程。
监听器处理所有三个条件(另一个玩家移动,“OK”或“ERR”)。消息响应“OK”和“ERR”被发送回客户端。移动命令是单独处理的,因此移动不是客户端的责任。
下面我模拟了演示这些概念的工作代码。服务器将随机(以相同的概率)响应:
- 好的
- 错误
- 包含
OK 和另一个玩家的移动的多行响应
代码:
public class MoveGame {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String command = "";
new Thread(new MoveServer()).start();
Socket socket = null;
PrintWriter out = null;
BlockingQueue<String> queue = new ArrayBlockingQueue<String>(10);
try {
socket = new Socket("localhost", 5001);
out = new PrintWriter(socket.getOutputStream(), true);
new Thread(new ClientReader(socket, queue)).start();
while (!command.equals("quit")) {
command = scanner.nextLine();
if (command.startsWith("move")) {
out.println(command);
String response = queue.take();
System.out.println("Client got response="+response);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
scanner.close();
out.close();
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
static class ClientReader implements Runnable {
private final Socket socket;
private final BlockingQueue<String> queue;
public ClientReader(Socket socket, BlockingQueue<String> queue) {
super();
this.socket = socket;
this.queue = queue;
}
@Override
public void run() {
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String command;
while ((command = in.readLine()) != null) {
if (command.equalsIgnoreCase("OK") || command.equalsIgnoreCase("ERR"))
queue.put(command);
else if (command.startsWith("move")) {
System.out.println("A player made a move: command="+command);
}
else
System.out.println("Unrecognized command="+command);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
static class MoveServer implements Runnable {
@Override
public void run() {
Random random = new Random();
Socket socket = null;
try {
ServerSocket ss = new ServerSocket(5001);
while (true) {
System.out.println("Listening for new connections");
socket = ss.accept();
System.out.println("New session has started");
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String command;
while ((command = in.readLine()) != null) {
System.out.println("Got command="+command);
int responseType = random.nextInt(3);
if (responseType == 0)
out.println("OK");
else if (responseType == 1)
out.println("ERR");
else {
out.println("move 1 [3,4]");
out.println("OK");
}
}
in.close();
out.close();
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}