双向多线程插座连接

时间:2018-04-03 19:29:09

标签: java multithreading sockets bidirectional

情况如下: 有一个服务器和一个客户端,它们都可以互相发起命令/消息。

因为服务器可以随时发送消息,所以套接字的监听是在一个单独的线程(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。

1 个答案:

答案 0 :(得分:2)

您有一个协议,客户端可以读取三种可能的消息之一:

  1. OK(你所做的举动被接受了)
  2. ERR(你所做的举动被拒绝了)
  3. move PLAYERID <co-ord1,co-ord2>
  4. 合理的假设是,消息OKERR只会被发送回请求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”被发送回客户端。移动命令是单独处理的,因此不是客户负责移动。

    下面我嘲笑了演示这些概念的工作代码。服务器将随机(以相同的概率)响应:

    1. ERR
    2. 包含OK和其他玩家移动的多线响应
    3. 代码:

      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();
                  }
              }
      
          }
      
      }
      
      }