情况如下: 有一个服务器和一个客户端,它们都可以互相发起命令/消息。
因为服务器可以随时发送消息,所以套接字的监听是在一个单独的线程(ListenerThread
)中完成的。这一切都很好。客户端可以同时发送消息并接收,但是,当服务器还发起新的命令/消息以通知发生了什么事情时,您如何知道某个响应是否属于您发送的命令?
如果我向服务器发送消息,服务器响应" OK"在听线程中。你怎么知道这是你发送的消息/命令的实际响应(记住这是另一个线程)。如果服务器从另一个客户端收到更新并首先发送该更新,该怎么办?
这就像聊天应用程序一样,但每个发送的命令都有实际响应。
示例案例:
让我们说协议只包含
move <playernum> [<x>,<y>]
命令,表示玩家已完成移动(服务器通知客户端)或玩家想要移动(客户端通知服务器)。此外,服务器响应&#34; OK&#34;如果移动没问题,或者使用&#34; ERR&#34;如果没有。
安全状态:
move 1 [3,4]
client ---> server
OK
client <--- server
不安全状态:
move 1 [3,4]
client ---> server
move 2 [1,2]
client <--- server
OK
client <--- server
客户没想到这个回复......应该回答OK。
答案 0 :(得分:2)
您有一个协议,客户端可以读取三种可能的消息之一:
OK
(你所做的举动被接受了)ERR
(你所做的举动被拒绝了)move PLAYERID <co-ord1,co-ord2>
合理的假设是,消息OK
和ERR
只会被发送回请求move
的套接字。然而,合法的move
被广播给所有其他玩家(可能不包括移动的玩家)。
由于您可以接收未经请求的响应(其他玩家所做的动作),因此您已正确创建了一个侦听器线程。您没有描述应用程序从另一个客户端收到move
消息时所采取的操作,但我将假设您的侦听器线程处理该情况。剩下的是如何协调您的move
命令,以及对将出现在侦听器线程中的命令的响应。
要同步提交move
命令和响应,将使用BlockingQueue(称为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();
}
}
}
}
}