1

我有个问题。

在我的代码中,我有一组玩家,每个人都带着他的“进”和“出”到服务器。

从服务器到客户端,我可以很好地沟通,没有任何问题,每个客户端都在他的 GUI 中显示服务器发送的更改。

问题是从客户端到服务器的通信。作为服务器,我想收到每个玩家的请愿书来做一些工作。问题是我不知道我从哪个索引收到请愿书,我无法检查players[index].in.readUTF()它阻止程序的所有原因。

一些帮助?in.avaiable()对我不起作用。

为了更清楚地显示它:

当玩家登录服务器时,我在服务器端执行此操作。

socket = serverSocket.accept();
out = new DataOutputStream(menuPrincipal.socket.getOutputStream());
in = new DataInputStream(menuPrincipal.socket.getInputStream());
players[i] = new player(i,out,in);

然后我在服务器端有一个线程等待消息。

问题是我不知道来自玩家数组的哪个索引(来自哪个玩家)的消息。我无法检查未向服务器发送任何内容的玩家索引,因为它会阻止程序。

这就是它的工作原理:

从服务器到客户端我只是这样做:

  1. player[i].out.writeUTF(message)从服务器端
  2. 客户只是做in.readUTF()了,一切正常

从客户端到服务器我这样做:

  1. out.writeUTF(message)来自客户
  2. 但是在服务器端我必须选择一个player[i].in 来阅读我不知道哪个玩家是。
4

2 回答 2

2

所以你说你已经设法将数据从服务器发送到客户端,它工作正常。同样的原则也适用于从客户端向服务器发送数据。

在服务器端有一个监听线程。在数据包中,您可以包含发送数据的用户的名称或 IP 地址。IE:

Data Recieved by Server: 'message, hello chris, sender, reciever'

然后,您的服务器可以使用字符串标记器将数据包分成有意义的部分。

Action: 'message'
Message: 'hello chris'
Sender: 'sender'
Reciever: 'reciever'.

然后,您拥有继续所需的所有数据。

这种实现意味着您需要的所有数据都包含在您收到的数据包中,您不必检查它的来源。额外的几个字节数据不会像每次收到数据包时都必须执行搜索那样减慢您的速度。

编辑响应 OP 编辑

或者,您可以实现一个HashMap<String,Player>. 这样,您需要做的就是使用我上面的描述从发送的数据包中获取用户名,并将其传递给 hashmap。hashmap 可以返回代表客户端的 User 对象,并且不需要再次进行索引查找。

您可以在此处找到有关Java Hashmap的文档。

最后,我注意到你有一player堂课。Java 约定是所有类都以大写字母开头。那是:

player应该是Player

根据您的评论进行最终编辑

恐怕你的问题是糟糕的设计问题。在这种情况下,客户端都应该通过相同的通道与服务器通信。这样,服务器所要做的就是监听任何消息,解析它们,然后继续移动它们。调整您的实现,以便您的客户端都通过相同的流与服务器对话,这将消除您的问题。

于 2013-03-06T12:49:05.990 回答
2

好的,首先让我给你一个关于套接字如何工作的小背景,然后再给你解决方案,了解发生了什么是至关重要的。

套接字解释参考

套接字是在服务器程序和一个或多个客户端程序之间建立双向通信的软件端点。注意双向这个词。这意味着对于客户端和服务器之间的每个连接,您都有两个流:一个输入流和一个输出流。这些可以通过在服务器端和客户端设置的两个套接字对象访问。

在此处输入图像描述

你的问题

从服务器到客户端,我只做 player[i].out.writeUTF(message) 和客户端然后检查他的“in”,做 in.readUTF() 并且一切正常。从客户端到服务器,我从客户端执行 out.writeUTF(message),但是在服务器端,我必须选择一个 player[i].in 来读取我不知道哪个玩家是的。

因此,如果我理解正确,您很难确定是哪个客户向您发送了消息。如果您正确使用多线程,这很容易解决,因为每个客户端都将在单独的线程上处理。

一个至关重要的步骤是最初如何处理客户。考虑以下代码。为了保持简短,我没有过多地关注约定。

ArrayList<ClientWorker> clientWorkers = new ArrayList<ClientWorker>();
int idCounter = 0;

    public void listenSocket(){
      try{
        int port = 9090
        server = new ServerSocket();
      } catch (IOException e) {
        System.exit(-1);
      }
      while(true){
        ClientWorker w;
        try{
    //server.accept returns a client connection
          clientWorker = new ClientWorker(server.accept(), idCounter, );
          idCounter++;
          clientWorkers.add(clientWorker); 
          Thread t = new Thread(clientWorker);
          t.start();
        } catch (IOException e) {
          System.exit(-1);
        }
      }
    }

好的,通过该代码,我们正在处理新客户并为他们分配一个唯一的密钥。我使用数组列表代替数组,以便它可以根据连接到服务器的用户数量增加和减少大小。收到客户端后,我创建一个 clientWorker 实例。此外,为每个连接的用户分配一个唯一键,可用于从数组列表中获取其对象(如果需要)。

ClientWorker 类看起来像这样:

    class ClientWorker implements Runnable {
      private Socket client;
      public int id

    //Constructor
      ClientWorker(Socket client, int id) {
        this.client = client;
        this.id = id;
      }

      public void run(){
        String line;
        BufferedReader in = null;
        PrintWriter out = null;
        try{
          in = new BufferedReader(new InputStreamReader(client.getInputStream()));
          out = new PrintWriter(client.getOutputStream(), true);
        } catch (IOException e) {
          System.out.println("in or out failed");
          System.exit(-1);
        }

        while(true){
          try{
//ANY DATA I GET FROM THIS STREAM IS FROM THIS PARTICULAR CLIENT ONLY!
            line = in.readLine();
            System.out.println("RECEIVED FROM CLIENT "+ id +" " + line);
           }catch (IOException e) {
            System.exit(-1);
           }
        }
      }
    }

该线程调用 run() 方法。由于每个客户端都有一个单独的线程,因此从特定线程处理的数据是从该特定客户端而不是其他人发送的。因此,可以很容易地区分每个客户的数据。

抱歉,答案很长。我花了一些时间才达到我的目的。

参考

于 2013-03-06T14:18:26.883 回答