我正在制作一个小型网络游戏来学习 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);
}
}
}