首先,我的代码只是我多人游戏的演示(2个或更多玩家可以同时玩)来演示我的问题而不需要任何额外的东西。我在游戏中成功实现了点对点(P2P)通信。后来,我决定在我的游戏中添加对客户端/服务器通信的支持(即中央服务器也是一个玩家)。它应该比P2P容易得多。但奇怪!不幸的是我面临着我无法解决的问题!现在问题在于:
假设我有1台服务器和一些客户端(可能是1个或更多客户端)。他们都应该提供以下输出:
Starting...
A
B
C
D
E
F
...
...
Done!
他们都在不使用多线程的情况下提供上述输出。但是使用多线程时,只有当有1个服务器和1个客户端时才会提供上述输出。
以下是服务器代码。只显示了重要部分; TODO
发表评论以表明发送/接收数据。 Common.totalClients
是要连接的客户端数量。
class ServerMain {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket[] sockets = new Socket[Common.totalClients];
ObjectOutputStream[] sendStreams = new ObjectOutputStream[Common.totalClients];
ObjectInputStream[] receiveStreams = new ObjectInputStream[Common.totalClients];
SendThread[] sendThreads = new SendThread[Common.totalClients];
ReceiveThread[] receiveThreads = new ReceiveThread[Common.totalClients];
// ... (here, all assignment of the above variables and closing serverSocket)
System.out.println("Starting...");
final char lastSendChar = 'Z' - (26 % (Common.totalClients + 1)) - Common.totalClients;
for (char sendChar = 'A'; sendChar <= lastSendChar; sendChar += (Common.totalClients + 1)) {
// sending server data to all clients
for (int i = 0; i < Common.totalClients; i++) {
sendThreads[i].send(sendChar); // TODO
//Common.send(sendStreams[i], sendChar);
}
System.out.println(sendChar);
for (int i = 0; i < Common.totalClients; i++) {
char receivedChar = receiveThreads[i].receive(); // TODO
//char receivedChar = Common.receive(receiveStreams[i]);
// sending received data to other clients except the one from which data has been received
// (so that all clients can receive other clients' data indirectly via this server)
for (int j = 0; j < i; j++) {
sendThreads[i].send(receivedChar); // TODO
//Common.send(sendStreams[j], receivedChar);
}
for (int j = i + 1; j < Common.totalClients; j++) {
sendThreads[i].send(receivedChar); // TODO
//Common.send(sendStreams[j], receivedChar);
}
System.out.println(receivedChar);
}
try { Thread.sleep(Common.loopSleep); }
catch (InterruptedException e) { e.printStackTrace(); }
}
// ... (here, closing all sockets and interrupt all threads)
System.out.println("Done!");
}
}
以下是客户端代码(仅限重要部分)。第一位客户有clientID
1 。第二位客户有clientID
2 等等。首先应该运行第一个客户端,然后运行第二个客户端。 TODO
发表评论以表明发送/接收数据。
System.out.println("Starting...");
final char lastSendChar = 'Z' - (26 % (Common.totalClients + 1)) - Common.totalClients + clientID;
for (char sendChar = 'A' + clientID; sendChar <= lastSendChar; sendChar += (Common.totalClients + 1)) {
// receiving data from server and other clients whose "clientID" is less than this client's "clientID" (via server)
for (int j = 0; j < clientID; j++) {
System.out.println(receiveThread.receive()); // TODO
//System.out.println(Common.receive(receiveStream));
}
// sending this client's data
sendThread.send(sendChar); // TODO
//Common.send(sendStream, sendChar);
System.out.println(sendChar);
// receiving data from other clients whose "clientID" is greater than this client's "clientID" (via server)
for (int j = clientID; j < Common.totalClients; j++) {
System.out.println(receiveThread.receive()); // TODO
//System.out.println(Common.receive(receiveStream));
}
try { Thread.sleep(Common.loopSleep); }
catch (InterruptedException e) { e.printStackTrace(); }
}
我不知道在使用多线程时哪个是没有获得预期输出的罪魁祸首。同样,使用多线程,它只适用于1个客户端(和服务器)!
这是ReceiveThread
。请注意,如果连接了多个客户端,则服务器和客户端都会停留在try { ch = queue.take(); }
。
class ReceiveThread extends Thread {
private ObjectInputStream receiveStream;
private BlockingQueue<Character> queue = new ArrayBlockingQueue<Character>(Common.totalClients);
public ReceiveThread(ObjectInputStream receiveStream) {
this.receiveStream = receiveStream; start();
}
public void run() {
while (!Thread.interrupted()) {
try { queue.put(receiveStream.readChar()); }
catch (InterruptedException e) { return; }
catch (IOException e) { return; }
}
}
public char receive() {
char ch = '#';
try { ch = queue.take(); }
catch (InterruptedException e) { e.printStackTrace(); }
return ch;
}
}
以下是SendThread
代码:
class SendThread extends Thread {
private ObjectOutputStream sendStream;
private volatile boolean pending = false;
private volatile char sendChar;
public SendThread(ObjectOutputStream sendStream) {
this.sendStream = sendStream; start();
}
public void run() {
while (!Thread.interrupted()) {
if (pending) {
pending = false;
try {
sendStream.writeChar(sendChar);
sendStream.flush();
} catch (IOException e) { return; }
}
try { Thread.sleep(10); }
catch (InterruptedException e) { return; }
}
}
public void send(char ch) {
sendChar = ch; pending = true;
}
}
现在,如果Common.totalClient
为2(即运行2个客户端),那么我得到以下输出:
服务器:(先运行)
Starting...
A
客户端1(clientID
为1):(在服务器之后运行)
Starting...
A
B
B
客户2(clientID
为2):(在客户1之后运行)
Starting...
A
他们被困在那里,甚至也不例外。为什么会这样?怎么解决?请注意,我使用了相同的SendThread
和ReceiveThread
类,通过这些类我成功实现了P2P通信。如果您有疑虑,请随时询问我在此处使用的更详细的代码。
<击> 修改
为方便起见,我添加了完整的runnable项目(它只包含5个小的.java文件:2个线程类;服务器,客户端类和公共类)。使用其他线程时当前有问题。但它没有额外的线程就可以正常工作。要在没有其他线程的情况下进行测试,请执行以下操作:1。注释\\ TODO
行,2。在\\ TODO
行之后取消注释单行。 3.注释其他螺纹结构线(4条线)。这是链接:(我删除了链接,因为它不需要解决问题!)
答案 0 :(得分:1)
您的服务器“多线程错误”本身。虽然你有2 * totalClients的线程,但你仍然只在服务器上运行一个线程(主线程)。我的意思是你的主线程有一个for
循环遍历每个客户端;如果其中一个客户端被卡住,您的主线程将被阻止,您将无法接收或发送其他客户端。
如何解决这个问题:将接收和发送代码放在每个相应的客户端线程中,而不是放在主线程上。你的主要应该看起来更像(伪代码)
main func {
while true {
accept a socketconnection
make a sender thread for the new socket connection {
thread code (always ready to send)
}.start();
make a listener thread for the new socket connection {
thread code (listens continously)
}.start();
}
}
答案 1 :(得分:0)
这里有一个明显的问题:将数据发送到sendThreads[i]
而不是sendThreads[j]
。 j
是循环变量,实际上我想使用它但输入错误。但\\ TODO
之后的评论是正确的!这就是为什么它在没有使用额外线程的情况下工作的原因。这里没有什么奇怪的问题!
所以ServerMain
类应该是(只有应修改的部分):
// sending received data to other clients except the one from which data has been received
// (so that all clients can receive other clients' data indirectly via this server)
for (int j = 0; j < i; j++) {
sendThreads[j].send(receivedChar); // instead of sendThreads[i]
//Common.send(sendStreams[j], receivedChar);
}
for (int j = i + 1; j < Common.totalClients; j++) {
sendThreads[j].send(receivedChar); // instead of sendThreads[i]
//Common.send(sendStreams[j], receivedChar);
}
实际上这是一些愚蠢的错误!但这是我问题的实际答案。