我的代码中某处存在无限循环

发布于 2024-12-02 08:49:24 字数 1364 浏览 2 评论 0原文

我有这个 Java 游戏服务器,最多可处理 3,000 个 tcp 连接,每个玩家或每个 tcp 连接都有自己的线程,每个线程都是这样的:

public void run()
{
    try
    {
        String packet = "";
        char charCur[] = new char[1];

        while(_in.read(charCur, 0, 1)!=-1 && MainServer.isRunning)
        {
            if (charCur[0] != '\u0000' && charCur[0] != '\n' && charCur[0] != '\r')
            {
                packet += charCur[0];
            }else if(!packet.isEmpty())
            {
                parsePlayerPacket(packet);
                packet = "";
            }
        }

        kickPlayer();

    }catch(IOException e)
    {
        kickPlayer();
    }catch(Exception e)
    {
        kickPlayer();
    }
    finally
    {
        try{
            kickPlayer();
        }catch(Exception e){};

        MainServer.removeIP(ip);
    }
}

代码运行良好,我知道每个玩家的每个线程都是坏的想法,但我现在也得保持这种方式。服务器在快速机器(6cor x2、64 位、24GB RAM、Windows Server 2003)上运行良好。

但在某个时刻,大约 12 小时的正常运行时间之后,服务器开始在某个地方循环...我知道这是因为 java 进程无限地占用 99% 的 CPU,直到下次重新启动。 我很难分析该应用程序,因为我不想打扰玩家。我使用的探查器(visualvm)总是最终破坏服务器而不告诉我问题出在哪里。

无论如何,在上面的那段代码中,我认为问题可能来自于此:(

while(_in.read(charCur, 0, 1)!=-1)

_in是客户端套接字的BufferedReader)。

_in.read() 是否可以无限地返回其他内容,从而使我的代码保持运行并占用 99% 的资源?我的代码有问题吗?我没全部看懂,只写了一半。

I have this Java game server that handles up to 3,000 tcp connections, each player, or each tcp connection has its own thread, each thread goes something like this:

public void run()
{
    try
    {
        String packet = "";
        char charCur[] = new char[1];

        while(_in.read(charCur, 0, 1)!=-1 && MainServer.isRunning)
        {
            if (charCur[0] != '\u0000' && charCur[0] != '\n' && charCur[0] != '\r')
            {
                packet += charCur[0];
            }else if(!packet.isEmpty())
            {
                parsePlayerPacket(packet);
                packet = "";
            }
        }

        kickPlayer();

    }catch(IOException e)
    {
        kickPlayer();
    }catch(Exception e)
    {
        kickPlayer();
    }
    finally
    {
        try{
            kickPlayer();
        }catch(Exception e){};

        MainServer.removeIP(ip);
    }
}

The code runs fine, and I know that each thread for each player is a bad idea, but I'll have too keep it this way for now. The server runs fine on a fast machine (6cor x2, 64bits, 24GB RAM, Windows Server 2003).

But at some point, after about 12 hours of UpTime, the server starts to loop somewhere... I know that because the java process takes 99% of the CPU infinitely until the next reboot.
And I'm having hard time to profile the application because I don't want to disturb the players. The profiler I use (visualvm) always end up chashing the server without telling me where's the problem.

Anyways, in that piece of code above I think maybe the problem comes from this:

while(_in.read(charCur, 0, 1)!=-1)

(the _in is a BufferedReader of the client's socket).

Is it possible that _in.read() can return something else infinitely that will keep my code runing and taking 99% of ressources? Is there something wrong with my code? I don't understand everything, I only wrote half of it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

心房敞 2024-12-09 08:49:25

一次读取一个字符几乎与使用 += 构建字符串一样慢。我无法告诉你哪个更糟糕。如果单个连接使用这种方法绑定整个核心,我不会感到惊讶。

最简单的“修复”是使用 BufferedReader 和 StringBuilder。

然而,读取数据的最有效方法是将字节读取到 ByteBuffer 中并解析“行”。我假设您正在接收 ASCII 文本。您可以编写解析器,使其能够在一个阶段(即一次传递数据)处理内容和行尾。

使用最后一种方法,下面是我解析 XML 消息的示例(包括代码)来自套接字并以 XML 进行回复。典型延迟为 16 微秒,吞吐量为每秒 264K。

http://vanillajava.blogspot.com/2011/07 /send-xml-over-socket-fast.html


您可以执行类似以下操作,这可能足够快

BufferedReader br = new BufferedReader(_in);
for(String line; ((line = br.readline()) != null;) {
    if(line.indexOf('\0') >= 0)
       for(String part: line.split("\0"))
          parsePlayerPacket(part);
    else
       parsePlayerPacket(line);
}

如果您发现此解决方案非常简单并且您熟悉 ByteBuffer 您可能会考虑使用 那些。

Reading one char at a time is almost as slow as building a String with +=. I wouldn't be able to tell you which is worse. It wouldn't surprise me if a single connection tied an entire core using this approach.

The simplest "fix" to do would be to use a BufferedReader and a StringBuilder.

However the most efficient way to read data is to read bytes, into a ByteBuffer and parse the "lines". I assume you are receiving ASCII text. You could write the parser to be able to process the content and the end of line in one stage (ie with one pass of the data)

Using the last approach, here is an example (including code) of where I am parsing an XML message from a socket and replying in XML. The typical latency was 16 micro-seconds and the throughput was 264K per second.

http://vanillajava.blogspot.com/2011/07/send-xml-over-socket-fast.html


You can do something like the following which likely to be fast enough

BufferedReader br = new BufferedReader(_in);
for(String line; ((line = br.readline()) != null;) {
    if(line.indexOf('\0') >= 0)
       for(String part: line.split("\0"))
          parsePlayerPacket(part);
    else
       parsePlayerPacket(line);
}

If you find this solution dead simple and you are familiar with ByteBuffer you might consider using those.

娇纵 2024-12-09 08:49:25

我在我编写的一个应用程序中遇到了同样的问题。我的应用程序占用了 50% 的 cpu(在双核中)。

为了解决这个问题,我所做的就是让线程休眠 1 个时间点,

Thread.sleep(1);

我希望这对您有所帮助

编辑:

哦,那是什么?
}catch(IOException e)
{
kickPlayer();
}catch(异常e)
{
kickPlayer();
}

我认为你不需要 IOException Catch (异常捕获,捕获各种异常)

I had the kind of a same problem at one of my applications I wrote. My Application took 50% cpu (in a dual core).

What I made then to resolve the Problem, is let the Thread sleeping 1 timetick

Thread.sleep(1);

I hope this is helpfull for you

edit:

oh and for what is that ?
}catch(IOException e)
{
kickPlayer();
}catch(Exception e)
{
kickPlayer();
}

I think you don't need the IOException Catch (the Exception catch, catches every kind of exception)

朮生 2024-12-09 08:49:25

异常处理只是伤害了我的眼睛。在 catch 块内调用 kickPlayer() 是没有意义的,因为您在 finally 中再次调用它。最后(几乎)总是执行。

现在关于你的问题,忘记我之前的答案,我有点睡着了XD。我没有看到任何容易在发布的 while 循环中永远循环的内容。当没有更多数据时,InputStream.read() 要么返回 -1,要么抛出异常。问题肯定出在其他代码中,或者可能是线程问题。

正如他们在其他答案中告诉您的那样,尝试使用缓冲流,读取一个字符块而不是一次只读取一个字符,并替换 StringBuilder 的追加方法的字符串连接。这应该会提高性能,但不确定是否能解决问题(也许它出现在 24 小时而不是 12 小时)。

That exception handling just hurted my eyes. There's no point in calling kickPlayer() inside catch blocks since you are calling it again in finally. Finally executes (almost) always.

And now about your problem, forget my previous answer, I was a bit asleep XD. I don't see anything prone to loop forever in the posted while loop. InputStream.read() either returns -1 when there's no more data or throws an exception. The problem must be in other code, or maybe is a threading problem.

As they have told you in other answers, try to use buffered streams, reading a block of characters instead of only one at a time, and replace the string concatenation for StringBuilder's append method. This should improve performance, but not sure if it will solve the problem (maybe it appears in 24h instead of 12).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文