Java Socket:如果多于1个客户端,客户端 - 服务器通信陷入多线程 [英] Java Socket: Client-server communication is stuck with multi-threading if more than 1 client

查看:183
本文介绍了Java Socket:如果多于1个客户端,客户端 - 服务器通信陷入多线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,我的代码只是我的多人游戏(2个或更多玩家可以同时玩)的演示,以演示我的问题,没有任何额外的东西。我已经成功地在我的游戏中实施对等(P2P)通信。后来,我决定在我的游戏中添加对客户端/服务器通信(即中央服务器也是一个播放器)的支持。它应该比P2P容易得多。但奇怪!不幸的是,我面对的问题,我没有线索来解决它!现在这里是问题:

Firstly, my code is just a demo of my multiplayer game (2 or more players can play simultaneously) to demonstrate my problem without any extra things. I have successfully implemented peer-to-peer (P2P) communication in my game. Later, I decided to add support for client/server communication (ie a central server which is also a player) in my game. It should be much easier than P2P. But strange! Unfortunately I'm facing the problem which I have no clue to solve it! Now here is the problem:

假设,我有1个服务器和一些客户端(可能是1个或多个客户端)。他们都应该给出以下输出:

Suppose, I have 1 server and some clients (may be 1 or more clients). They all should give the following output:

Starting...
A
B
C
D
E
F
...
...
Done!

它们都提供上述输出而不使用多线程。但是使用多线程时,只有当有1个服务器和1个客户端时,它才会提供上述输出。

They all give the above output without using multi-thread. But using multi-threading, it gives the above output only when there're 1 server and 1 client.

服务器代码。只显示了重要的部分; TODO 注释以指示发送/接收数据。 Common.totalClients 是要连接的客户端数量。

Here is the server code. Only the important part is shown; TODO comment to indicate send/receive data. Common.totalClients is the number of clients to be connected with.

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!");
    }
}

这里是客户代码(只有重要部分)。第一个客户端具有 1 clientID 。第二个客户端具有 2 clientID ,等等。和第一个客户端应该先运行,然后第二个这样。 TODO 注释以指示发送/接收数据。

Here is the client code (only important part). First client has clientID of 1. Second client has clientID of 2 and such that. And first client should be run first, then second and such that. TODO comment to indicate send/receive data.

        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(); }
        }



我不知道哪个是没有得到期望输出的罪魁祸首当使用多线程时。

I don't know which is the culprit of not getting the expected output when using multi-thread. Again, using multi-thread, it works only for 1 client (and the server)!

这里是 ReceiveThread,可以使用多线程, 。注意,服务器和客户端都停留在 try {ch = queue.take();

Here is ReceiveThread. Note, both the server and the clients are stuck at try { ch = queue.take(); } if more than 1 client are connected.

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 code:

Here is SendThread code:

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个客户端运行),然后我得到以下输出:

Now, if Common.totalClient is 2 (ie 2 clients to run), then I get the following output:

服务器:先运行)

Starting...
A

客户端1( clientID 为1): / p>

Client 1 (clientID is 1): (Runs after the server)

Starting...
A
B
B

客户端2( clientID 为2):在客户端之后运行1)

Client 2 (clientID is 2): (Runs after Client 1)

Starting...
A

它们停留在那里,即使没有异常。为什么是这种行为?如何解决呢?注意,我已经使用相同的 SendThread ReceiveThread 类,通过它我已成功实施P2P通信。

They are stuck at there, even no exception. Why is this behaviour? How to solve it? Note that I have used the same SendThread and ReceiveThread classes by which I have succcessfully implemented P2P communication. Feel free to ask more detailed code which I have used here if you have concern.

编辑
为方便起见,请随时询问更详细的代码,我添加了完整的runnable项目(它只包含5个小的.java文件:2个线程类;服务器,客户端类和公共类)。当使用其他线程时,它当前是错误的。但它工作原理没有额外的线程。要测试它没有额外的线程,请只做:1.评论 \\ TODO 行,2.取消注释 \\\ TODO 行。 3.注释附加螺纹构造线(4条线)。这是链接:(我已经删除了链接,因为它不需要解决问题!)

For convenience, I've added the full runnable project (which only contains 5 small .java files: 2 thread classes; server, client classes and common class). It is currently faulty when using additional threads. But it works as expected without additional threads. To test it without the additional threads, please just do: 1. Comment \\ TODO lines, 2. Uncomment the single lines just after \\ TODO lines. 3. Comment the additional thread construction lines (4 lines). Here is the link: (I've deleted the link because it is not needed to solve the problem!)

推荐答案

p>这里有一个明显的问题:发送数据到 sendThreads [i] 而不是 sendThreads [j] j 是循环变量,实际上我想使用它但是错了。但 \\ TODO 后的注释是正确的!这就是为什么它没有使用额外的线程工作。这里没有什么奇怪的问题!

There is an obvious problem right here: sending data to sendThreads[i] instead of sendThreads[j]. j is the loop variable, and actually I wanted to use it but mistyped it. But the comments after \\ TODO is correct! That's why it was working without using additional threads. And nothing strange here as stated in the question!

所以 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);
}

其实这是一种愚蠢的错误!但这是我的问题的实际答案。

Actually it is some kind of silly mistakes! But this is the actual answer of my question.

这篇关于Java Socket:如果多于1个客户端,客户端 - 服务器通信陷入多线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆