2

我正在制作一个小型网络游戏来学习 Java 网络,我需要对我的程序遇到的问题有一点了解。我的服务器程序用尽了堆并消耗了 100% 的 CPU。我确定我在代码中遇到了主要的新手陷阱,我想知道是否有人愿意向我指出它们,并可能详细说明为什么这是一种如此可怕的做法。

基本上,Server 类的工作是在 socket.accept() 中等待处理新的客户端。每个客户端都有自己的 ConnectionThread(处理输入)和附加的 OutputStream(处理输出)。我知道这对于大型应用程序来说可能是浪费的,但是服务器只与三个客户端一起运行(它们被设置为跳过渲染并且只通过套接字发送数据,输入和输出每 ~20 毫秒)它会烹饪 CPU 和服务器溢出堆栈。

我有一个 Packet 类,它将数据转换为字符串以进行发送,接收者将其解码回 Packet。我怀疑我有一些数据包放置时间过长,但我不知道在哪里。如果不是数据包,我相当肯定我有某种不受控制的指数对象增长。

以下是一些相关代码片段。如果问题出在其他地方,我很乐意提供更多信息。

仅供参考,这里是完整代码:https ://github.com/taylorrobert/ProjectM2O

服务器:

public Server() {
    network = new NetworkManager(this);
    network.setConnectionCounter(0);
    entityManager = new EntityManager(this);
    setListenState(true);

    try {
        serverSocket = new ServerSocket(PORT);

    } catch (IOException e) {
        System.out.println("Error in server constructor.");
        System.exit(1);
    }
}

public void listen() {

        System.out.println("Current connectionCounter: " + network.getConnectionCounter()); 
        while (shouldListen) {
            ConnectionThread conn = null;

            try {
                conn = new ConnectionThread(serverSocket, this);
            }
            catch (Exception e) {
                System.out.println("____Error constructing ConnectionThread. Could there be another instance of the server running?");
                e.printStackTrace();
                System.exit(1);
            }

            (new Thread(conn)).start();


            System.out.println("Connection count: " + network.getConnectionCounter());
        }
    }

连接线程:

public ConnectionThread(ServerSocket s, Server ser) {
    resetTimer();
    setActiveState(false);
    server = ser;

    //This UUID becomes the client's controllable player ID
    //and the ID of this ConnectionThread.
    connectionID = String.valueOf(UUID.randomUUID());

    try {
        socket = s.accept();
        System.out.println("Socket ID " + connectionID + " established on: " + socket);
    } catch (IOException e) {
        System.out.println("Error in ConnectionThread. Is there a server already running on this port?");
    }
    init();
}

public void init() {
        try {
            out = new PrintWriter(socket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

        }
        catch (IOException e) {
            System.out.println("Error in intializing I/O streams.");
            System.exit(1);
        }

        //Create the output thread
        OutputStream outputHandler = new OutputStream(this);
        new Thread(outputHandler).start();


        //Get the client up to date on all relevant data
        server.getEntityManager().addPlayerEntity(getConnectionID());
        server.getNetwork().pushClientInitState(getConnectionID(), this);
        server.getNetwork().addConnection(this);
        server.getNetwork().notifyClientsAboutNewPlayer(getConnectionID());
        int s = server.getNetwork().getConnections().size();
        server.getNetwork().sendConsoleMessage("Players online: " + s, this);

    }

public void run() {
    setActiveState(true);
    System.out.println("Running ConnectionThread...");
    while (isActive()) {


        //System.out.println("Entity size: " + server.getEntityManager().getEntities().size());
            String op = readInputStream();

            if (op.equals("")) continue;
            Packet packet = Packet.populateNewPacketFromString(op);
            try {
                incomingOpQueue.put(packet);
            } catch (InterruptedException e) {
                System.out.println("Server failed to add packet to outgoing queue!");
            }
            //Take all packets off the incoming queue and execute them in order
            while (incomingOpQueue.size() > 0) {
                Packet p = incomingOpQueue.poll();
                PacketExecutor.executePacket(server, p, this);


            }
    }
}

public String readInputStream() {
    String msg = "";

    try {
        msg = in.readLine();
        msg = msg.replace("\n", "");
        msg = msg.trim();
    } catch (IOException e) {
        return "";
    }
    return msg;

}

输出流:

public void output() {
    while (parentCT.isActive()) {
        UnitTester.updateAllEntityLocationsToAllClients(parentCT, parentCT.server.getEntityManager().getEntities());
        while (parentCT.getOutgoingOpQueue().size() > 0) {
            String packet = (parentCT.getOutgoingOpQueue().poll().getString());
            if (packet.equals("")) continue;
            //System.out.println("Sending " + packet + " to " + parentCT.getConnectionID());
            parentCT.getOutput().println(packet);

        }

    }
}
4

1 回答 1

2

你可能需要变得shouldListen易变。否则,它的值很有可能会被缓存,并且在其他线程中将其设置为 false 将没有任何区别。您其设置为 false 正确,以便主循环不会产生大量线程,直到它最大化堆并烧毁 CPU?

于 2013-11-13T20:03:54.217 回答