【问题标题】:determining which client sent data using sockets确定哪个客户端使用套接字发送数据
【发布时间】:2013-03-06 12:43:49
【问题描述】:

我有问题。

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

从服务器到客户端我可以很好地沟通,没有任何问题,每个客户端都在其 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 阅读我不知道是哪个玩家。

【问题讨论】:

  • 你能发布一些你正在使用的结构吗?玩家如何登录您的服务器?
  • 玩家数组?玩家是字符串、整数还是对象,什么?请给代码

标签: java swing datainputstream


【解决方案1】:

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

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

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 here 上找到文档。

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

player 应该是Player

针对您的评论的最终编辑

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

【讨论】:

  • 或者使用 XML 而不是逗号分隔的字符串。 XStream 是一个很棒的库:xstream.codehaus.org
  • 是的,XML 总是一个很好的选择。我只是想,如果 OP 还没有完全建立通信,我们可以从更简单的开始,然后转向更复杂的行业标准通信方法。
  • 感谢您的时间和回答,但这不是我遇到的问题。从服务器到客户端,我只执行 player[i].out.writeUTF(message) 和客户端然后检查他的“in”,执行 in.readUTF() 并且一切正常。从客户端到服务器,我从客户端执行 out.writeUTF(message),但是在服务器端,我必须选择一个 player[i].in 来读取我不知道哪个玩家是的。
  • @auron344 虽然它的设计很糟糕,但一个快速解决方法是您将第 i 个值 (i) 发送到客户端,并在服务器发送信息后将其发送回服务器。
  • @Chris Cooney 我该怎么做?每次玩家登录服务器时,我都会为他生成一个输入和输出流并将他放入数组中。我真的不知道如何实现客户端与服务器的相同通道通信。
【解决方案2】:

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

套接字说明 (Reference)

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

您的问题

从服务器到客户端我只做 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() 方法。由于每个客户端都有一个单独的线程,因此从特定线程处理的数据是从该特定客户端而不是其他人发送的。因此,可以很容易地区分每个客户的数据。

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

References

【讨论】:

  • 这正是我在@Chris Cooney 回答说实施不好之后所做的。我看了一个视频 [link]youtube.com/watch?v=_1ThWf9Fkfo 解释和你完全一样。非常感谢!
  • +1 以获得绝对出色的答案。真的补充了这个问题! :)
  • @ChrisCooney 感谢您的 + 1 :)。
  • @auron344 很高兴能帮上忙 :)
猜你喜欢
  • 2016-12-16
  • 2023-03-23
  • 2012-05-21
  • 2017-01-13
  • 1970-01-01
  • 2017-12-07
  • 1970-01-01
  • 2020-07-13
  • 1970-01-01
相关资源
最近更新 更多