【问题标题】:Polling servers at the same port - Threads and Java在同一端口轮询服务器 - 线程和 Java
【发布时间】:2010-05-30 04:37:55
【问题描述】:

我目前正忙于为使命召唤 1 的早期版本开发 IP 禁止工具。(显然这些版本中没有实现这样的功能)。

我已经完成了一个单线程应用程序,但它对于多台服务器的性能不够好,这就是我尝试实现线程的原因。

现在,每个服务器都有自己的线程。我有一个 Networking 类,它有一个方法; “GetStatus”——这个方法是同步的。此方法使用 DatagramSocket 与服务器通信。由于此方法是静态和同步的,因此我不应该遇到麻烦并收到一大堆“地址已在使用”异常。

但是,我有第二种方法,名为“SendMessage”。此方法应该向服务器发送消息。当“GetStatus”中已经有一个线程正在运行时,我如何确保不能调用“SendMessage”,反之亦然?如果我使两者同步,如果线程 A 在端口 99999 上打开一个套接字并调用“SendMessage”,而线程 B 在同一端口上打开一个套接字并调用“GetStatus”,我仍然会遇到麻烦? (游戏服务器通常托管在相同的端口上)

我想我真正追求的是一种使整个类同步的方法,以便单个线程一次只能调用和运行一个方法。

希望我试图完成/避免的事情在本文中得到明确。

非常感谢任何帮助。

【问题讨论】:

  • 顺便说一句,99999 不是有效端口 - 端口是 16 位无符号数字
  • SendMessage 和 GetStatus 是在什么情况下同步的?
  • 端口 99999 只是一个例子,如果我写了,你能告诉我“端口 XXXXX”不是有效端口吗?那么“端口 ”呢?

标签: java multithreading sockets


【解决方案1】:

现在,每个服务器都有自己的线程。

为什么在同一个应用程序中需要两台服务器?!?如果您将两台服务器分成单独的应用程序,那么如果它们都尝试使用相同的端口,您仍然会遇到同样的问题......即您必须为每台服务器专用一个端口。如果线程确实存在问题,请阅读下文,了解如何解决此问题。

两个线程不可能正确地执行同一个类的同步方法...如果你有正确的同步那么你就没有办法体验你的东西'重新描述。这是您的课程的外观:

class Networking 
{
    public synchronized Status getStatus() {
        Status stat =  new Status();
        // ...
        // Get status logic
        // ...
        return stat;// return the status
    }

    public synchronized void sendMessage(Message msg) {
        // ...
        // Send the message logic
        // ...
    }
}

所以,只要您在同一个 Networking 实例上调用这些方法(即,您没有每个线程的 Networking 类的单独实例),那么您应该看不到任何问题。以下是 synchronized 关键字的作用:

首先,同步方法的两次调用是不可能的 交错的同一个对象。什么时候 一个线程正在执行同步 对象的方法,所有其他的 调用同步的线程 相同对象块的方法 (暂停执行)直到第一个 线程完成了对象。

其次,当一个同步方法退出时,它会自动建立一个 与任何事前发生的关系 随后调用 a 相同的同步方法 目的。这保证了变化 到对象的状态是可见的 到所有线程。 (ref)

如果您想在 Networking 类的所有实例中同步方法,那么您需要使用同步语句:

class Networking 
{
    private static final Object lock = new Object();

    public synchronized Status getStatus() {
        synchronized(lock){
            Status stat =  new Status();
            // ...
            // Get status logic
            // ...
            return stat;// return the status
        }
    }

    public synchronized void sendMessage(Message msg) {
        synchronized(lock){
            // ...
            // Send the message logic
            // ...
        }
    }
}

【讨论】:

    【解决方案2】:

    如果线程 A [I] 仍然会遇到麻烦 正在打开端口 99999 上的套接字,并且 在线程 B 时调用“SendMessage” 在同一个端口上打开一个套接字 并调用“GetStatus”?

    这里有两个不同的问题。 (除了 99999 不是有效端口#)

    UDP 本质上是用于多路复用的一对多风格的通信。您可以打开单个套接字并使用该单个套接字与任意数量的服务器进行通信。您不必担心一个线程在同一个套接字上发送和另一个接收的同步,或者两个线程试图同时发送,因为从应用程序的角度来看,对套接字的读取和写入操作是原子的。当您在 UDP 套接字上发送时,您正在调用一个系统调用,它将 N 个字节的数据从应用程序的内存空间复制到 OS 内核内存空间的缓冲区中,然后内核将该数据组装成一个 UDP 数据包,该数据包被放入队列用于发送 - 所有这些都以对应用程序来说是原子的方式进行。从 UDP 套接字读取时也会发生相同的情况,但相反;不同的数据包存在于内核的接收缓冲区中,当您的应用程序读取套接字时,这些数据包中的数据会自动从内核缓冲区复制到应用程序的缓冲区中,每次读取操作一个数据包。

    第二个问题是管理特定服务器的传入和传出数据。听起来您希望每个服务器有一个线程来维护有关该服务器的状态/状态。发送时,您完全不必担心同步问题。所有线程都可以从同一个套接字发送,同步由操作系统内核有效地处理。

    但是,接收是一个完全不同的问题。我建议使用一个线程,其唯一工作是从套接字读取传入的数据包并将它们解复用。每个服务器线程都有一个线程安全队列,读取器线程会将传入的数据包复制到该队列中。然后每个服务器线程除了从它自己的传入数据包队列中读取数据包外,无需担心其他任何事情——它根本不需要处理从套接字读取数据。

    【讨论】:

      【解决方案3】:

      我认为您可能误解了套接字的工作原理,并且混淆了套接字的客户端和服务器端。如果您要发送消息,通常是从客户端套接字完成的。这些不绑定到静态端口号 - 它是绑定到特定端口的服务器套接字(您调用 accept() 的那个)。

      您可以从任何一个网络接口拥有任意数量的客户端(不超过某个合理的限制 - 最多有大约 60,000 个客户端连接)。

      有关客户端和服务器端套接字的介绍,请参阅 Sun 课程:All About Sockets

      【讨论】:

      • 您不会在 UDP 套接字上调用 accept,而只能在 TCP 套接字上调用。
      • 对 - 我没有看到它是一个数据报套接字。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-31
      • 2013-01-28
      • 1970-01-01
      • 1970-01-01
      • 2014-02-02
      • 2019-02-20
      • 1970-01-01
      相关资源
      最近更新 更多