[英]Java Socket: Client-server communication is stuck with multi-threading if more than 1 client
首先,我的代码只是我的多人游戏(两个或两个以上的玩家可以同时玩)的演示,以演示我的问题而无需做任何其他事情。 我已经在游戏中成功实现了对等(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 , clientID
。 并且应该首先运行第一个客户端,然后运行第二个。 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(); }
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
实现P2P通信的相同SendThread
和ReceiveThread
类。 如果您有任何疑问,请随时询问我在这里使用的更详细的代码。
编辑:为方便起见,我添加了完整的可运行项目(仅包含5个小的.java文件:2个线程类;服务器,客户端类和公共类)。
当前在使用其他线程时出现故障。
但是它可以按预期工作,而无需其他线程。
要在没有其他线程的情况下进行测试,请执行以下操作:1.注释
\\\\ TODO
行,2.取消注释
\\\\ TODO
行之后的单行。
3.注释其他线程构造行(4行)。
这是链接:(我已删除该链接,因为不需要它来解决问题!)
你的服务器是“多线程错误” 本身 。 尽管确实有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();
}
}
这里有一个明显的问题:将数据发送到sendThreads[i]
而不是sendThreads[j]
。 j
是循环变量,实际上我想使用它,但键入错误。 但是\\\\ TODO
之后的\\\\ 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);
}
其实这是一种愚蠢的错误! 但这是我的问题的实际答案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.