我目前正忙着为“使命召唤1”的早期版本开发IP禁用工具。(显然这些功能并没有在这些版本中实现)。
我已经完成了单线程应用程序,但它对多个服务器的性能不够好,这就是我尝试实现线程的原因。
现在,每个服务器都有自己的线程。我有一个Networking类,它有一个方法; “GetStatus” - 此方法已同步。此方法使用DatagramSocket与服务器通信。由于这个方法是静态的和同步的,所以我不应该遇到麻烦并收到一大堆“已经在使用的地址”的例外情况。
但是,我有第二个名为“SendMessage”的方法。该方法应该向服务器发送消息。如果已经在“GetStatus”中运行了一个线程,我怎么能确保无法调用“SendMessage”,反过来呢?如果我同时进行同步,如果线程A在端口99999上打开套接字并调用“SendMessage”而线程B在同一端口上打开套接字并调用“GetStatus”,我仍会遇到麻烦吗? (游戏服务器通常托管在相同的端口上)
我想我真正想要的是一种使整个类同步的方法,这样一个线程一次只能调用和运行一个方法。
希望我在本文中明确表达我想要完成/避免的事情。
非常感谢任何帮助。
答案 0 :(得分:2)
现在,每个服务器都有自己的线程。
为什么在同一个应用程序中需要两台服务器?!?如果你将两个服务器分解为单独的应用程序,那么如果它们都尝试使用相同的端口,你仍然会遇到同样的问题...即你必须为每个服务器专用一个端口。如果它确实是线程问题,那么请阅读下面有关如何解决这个问题的想法。
两个线程不可能执行同一类的正确同步方法...如果你有正确的同步那么就没有办法体验你的'重新描述。以下是您的课程应该是什么样的:
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
关键字为您所做的事情:
首先,不可能对两个同步方法进行调用 交错的同一个对象。什么时候 一个线程正在执行同步 对象的方法,所有其他 调用synchronized的线程 同一对象块的方法 (暂停执行)直到第一个 线程是用对象完成的。
其次,当同步方法退出时,它会自动建立一个 发生在与任何人的关系之前 随后调用一个 同步方法相同 宾语。这保证了变化 到对象的状态是可见的 到所有线程。 (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
// ...
}
}
}
答案 1 :(得分:2)
如果线程A,[我]仍然会遇到麻烦 在99999端口打开一个插座 线程B时调用“SendMessage” 正在同一个端口上打开一个套接字 并调用“GetStatus”?
这里有两个不同的问题。 (超出了99999不是有效端口的事实#)
UDP本质上是针对多路复用的一对多样式通信。您可以打开单个套接字并使用该单个套接字与任意数量的服务器进行通信。您不必担心同一个线程发送和另一个接收在同一个套接字或两个线程尝试同时发送的意义上的同步,因为从应用程序的角度来看,对套接字的读写操作是原子的。当你发送UDP套接字时,你正在调用一个系统调用,它将N个字节的数据从应用程序的内存空间复制到OS内核的内存空间中的缓冲区中,然后内核将该数据组装成一个UDP数据包放入队列中用于发送 - 所有这些都以对应用程序看起来很原始的方式进行。从UDP套接字读取时反向除外;内核中的接收缓冲区中存在不同的数据包,当应用程序读取套接字时,这些数据包中的数据会从内核缓冲区原子复制到应用程序的缓冲区中,每次读取操作一个数据包。
第二个问题是管理到特定服务器的传入和传出数据。听起来您希望每个服务器都有一个线程来维护该服务器的状态/状态。发送时,您根本不必担心同步。所有线程都可以从同一个套接字发送,同步由OS内核有效处理。
然而,接收是一个完全不同的问题。我建议有一个线程,其唯一的工作是从套接字读取传入的数据包并解复用它们。每个服务器线程都有一个线程安全队列,读取器线程将复制传入的数据包。然后每个服务器线程不必担心除了从它自己的传入数据包队列中读取数据包之外的任何事情 - 它根本不需要处理从套接字读取的数据。
答案 2 :(得分:0)
我认为你可能会误解套接字如何工作以及使套接字的客户端和服务器端混淆。如果您要发送消息,通常是从客户端套接字完成的。它们不绑定到静态端口号 - 它是绑定到特定端口的服务器套接字(您调用accept()
的那个)。
您可以从任何一个网络接口拥有所需数量的客户端(达到一定的合理限制 - 最多约60,000个客户端连接)。
有关客户端和服务器端套接字的介绍,请参阅Sun课程:All About Sockets