2015-08-08 51 views
0

我目前有一个游戏,为此我实现了一个客户端和一个服务器。CPU-Wise,如何优化UDP数据包发送?

我然后让服务器向客户端关于它的位置发送数据,客户端发送移动输入到服务器等

的问题是,在CPU焰火至100%。我已经直接连接在高使用率下面的代码,这是在被每秒称为十倍的更新()方法:

try{ 
     sendToClientUDP(("ID:" + String.valueOf(uid))); 
     sendToClientUDP(("Scale:" + GameServer.scale)); 

     for (Clients cl : GameServer.players){ 
      //sendToClient(("newShip;ID:" + cl.uid).getBytes(), packet.getAddress(), packet.getPort()); 
      sendToClientUDP((("UID:" + cl.uid +";x:" + cl.x))); 
      sendToClientUDP((("UID:" + cl.uid +";y:" + cl.y))); 
      sendToClientUDP((("UID:" + cl.uid +";z:" + cl.z))); 
      sendToClientUDP((("UID:" + cl.uid +";Rotation:" + (cl.rotation)))); 
      cl.sendToClientUDP(new String("newShip;ID:" + uid)); 
      sendToClientUDP(new String("newShip;ID:" + cl.uid)); 
     } 
     }catch (Exception e){ 
      e.printStackTrace(); 
     } 

卸下代码和CPU使用率过高消失。

这是我的sendToClientUDP()方法。

public void sendToClientUDP(String str){ 
     if (!NPC){ //NPC is checking if it is a computer-controlled player. 
     UDP.sendData(str.getBytes(), ip, port); 
     } 
    } 

这里是我的UDP.sendData()方法:

public static void sendData(String data, InetAddress ip, int port) { 
    sendData(data.getBytes(), ip, port); 
} 

public static void sendData(byte[] data, InetAddress ip, int port) { 
    DatagramPacket packet = new DatagramPacket(data, data.length, ip, port); 
    try { 
     socket.send(packet); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

为什么如此多的CPU被简单地通过发送UDP数据包使用?如果有的话,我能做些什么来减少它?

+0

错误,发送所有的东西在一个单一的数据报? – EJP

回答

2

我建议你拿出或优化产生这么多CPU的代码,CPU分析器是最好的启动地点,但这些可能是CPU消耗的原因。

  • 创建字符串和字节[]是昂贵的,我会避免做那些。
  • 创建多个数据包而不是批处理它们也很昂贵。
  • 可以避免创建一个新的DatagramPacket。
  • 我会消除消息之间的重复,因为这会增加您可以避免的冗余工作。
  • 您可能会考虑使用二进制格式来避免转换为文本/从文本转换的开销。
  • 几乎没有好时间使用new String()它几乎肯定是多余的。

编辑:这是我的想法。不是每个客户端发送5个数据包,总共只发送一个数据包。对于十个客户端,你发送1/50的数据包,减少开销。

import java.io.IOException; 
import java.net.*; 
import java.nio.ByteBuffer; 
import java.nio.ByteOrder; 
import java.util.ArrayList; 
import java.util.List; 

/** 
* Created by peter on 31/07/15. 
*/ 
public class PacketSender { 
    public static void main(String[] args) throws IOException { 
     PacketSender ps = new PacketSender(InetAddress.getByName("localhost"), 12345); 
     List<Client> clients = new ArrayList<>(); 
     for(int i=0;i<10;i++) 
      clients.add(new Client()); 

     for(int t = 0; t< 3;t++) { 
      long start = System.nanoTime(); 
      int tests = 100000; 
      for (int i = 0; i < tests; i++) { 
       ps.sendData(1234, 1, clients); 
      } 
      long time = System.nanoTime() - start; 
      System.out.printf("Sent %,d messages per second%n", (long) (tests * 1e9/time)); 
     } 
    } 


    final ThreadLocal<ByteBuffer> bufferTL = ThreadLocal.withInitial(() -> ByteBuffer.allocate(8192).order(ByteOrder.nativeOrder())); 
    final ThreadLocal<DatagramSocket> socketTL; 
    final ThreadLocal<DatagramPacket> packetTL; 

    public PacketSender(InetAddress address, int port) { 
     socketTL = ThreadLocal.withInitial(() -> { 
      try { 
       return new DatagramSocket(port, address); 
      } catch (SocketException e) { 
       throw new AssertionError(e); 
      } 
     }); 
     packetTL = ThreadLocal.withInitial(() -> new DatagramPacket(bufferTL.get().array(), 0, address, port)); 
    } 

    public void sendData(int uid, int scale, List<Client> clients) throws IOException { 
     ByteBuffer b = bufferTL.get(); 
     b.clear(); 
     b.putInt(uid); 
     b.putInt(scale); 
     b.putInt(clients.size()); 
     for (Client cl : clients) { 
      b.putInt(cl.x); 
      b.putInt(cl.y); 
      b.putInt(cl.z); 
      b.putInt(cl.rotation); 
      b.putInt(cl.uid); 
     } 
     DatagramPacket dp = packetTL.get(); 
     dp.setData(b.array(), 0, b.position()); 
     socketTL.get().send(dp); 
    } 

    static class Client { 
     int x,y,z,rotation,uid; 
    } 
} 

当这个性能测试运行它打印

Sent 410,118 messages per second 
Sent 458,126 messages per second 
Sent 459,499 messages per second 

编辑:写/读课文,你可以做到以下几点。

import java.nio.ByteBuffer; 

/** 
* Created by peter on 09/08/2015. 
*/ 
public enum ByteBuffers { 
    ; 
    /** 
    * Writes in ISO-8859-1 encoding. This assumes string up to 127 bytes long. 
    * 
    * @param bb to write to 
    * @param cs to write from 
    */ 
    public static void writeText(ByteBuffer bb, CharSequence cs) { 
     // change to stop bit encoding to have lengths > 127 
     assert cs.length() < 128; 
     bb.put((byte) cs.length()); 
     for (int i = 0, len = cs.length(); i < len; i++) 
      bb.put((byte) cs.charAt(i)); 
    } 

    public static StringBuilder readText(ByteBuffer bb, StringBuilder sb) { 
     int len = bb.get(); 
     assert len >= 0; 
     sb.setLength(0); 
     for (int i = 0; i < len; i++) 
      sb.append((char) (bb.get() & 0xFF)); 
     return sb; 
    } 

    private static final ThreadLocal<StringBuilder> SB = new ThreadLocal<>() { 
     @Override 
     protected Object initialValue() { 
      return new StringBuilder(); 
     } 
    }; 

    public static String readText(ByteBuffer bb) { 
     // TODO use a string pool to reduce String garbage. 
     return readText(bb, SB.get()).toString(); 
    } 
} 

如果你需要更复杂的东西,你应该考虑使用Chronicle-Bytes这是我写的。它有

  • 支持64位内存大小,包括内存映射64位。
  • 线程安全操作堆外。
  • 字符串的UTF-8编码。
  • 压缩类型,如停止位编码。
  • 自动字符串池化减少垃圾。
  • 确定性通过引用计数清理掉堆资源。
+0

这就是我要问你的。我已经尝试拿出解决问题的代码,但它对游戏至关重要,并且无法取出。创建多个数据包的确很昂贵,但肯定不会达到每秒100个数据包等于100%CPU的程度?如果真的如此,* ANY *公共游戏如何在实时环境中使用CPU? – Joehot200

+0

我刚看到你的编辑。我怎样才能避免创建一个DatagramPacket?另外,我怎么不能创建一个字符串或字节[]? – Joehot200

+0

@ Joehot200没有分析你的应用程序,你只能猜测它在做什么,但我怀疑你的字符串创建和getBytes()实际上比发送这么多的数据包更昂贵。顺便说一句,你为什么不每次只看一个? –