我正在使用Java DatagramSocket类将UDP数据报文发送到端点。数据报必须以60ms的间隔到达端点。
我发现DatagramSocket.Send通常可以采用> 1 ms(接近2)打包并发送不超过56个字节的数据包。这导致我的数据包以62 ms的间隔传送,而不是60 ms。
这是在Windows Vista机器上。以下是我测量时间的方法:
DatagramPacket d = new DatagramPacket(out, out.length, address, port);
long nanoTime = System.nanoTime();
socket.send(d);
long diff = System.nanoTime() - nanoTime;
System.out.println( out.length + " in " + diff + "ms." );
是否有人有提示或技巧来加快这一过程?
答案 0 :(得分:6)
您可以使用Timer类来安排活动。
Timer timer = new Timer();
TimerTask task = new TimerTask() {
public void run() {
//send packet here
}};
timer.scheduleAtFixedRate(task, 0, 60);
这将每60ms创建一个重复发生的事件来执行“run”命令。所有事情都保持不变,数据包应该每60分钟接通一次(虽然,第一个数据包将被延迟一些数量,垃圾收集/其他任务/等可能会略微延迟这个数字)。
答案 1 :(得分:4)
您将看到将数据从用户空间复制到内核空间所需的时间。通过UDP,IP和以太网层发送需要更长的时间,数据报跨越物理网络到达目的地可能需要不同的时间。
假设您的网络没有抖动(每个数据包传输时间不一致),并且您的进程以实时优先级运行,并且没有其他任何东西可以与CPU竞争......
无论send()方法执行多长时间,您都需要每隔60ms调用一次send。你不能在两次通话之间等待60ms。你需要测量执行循环体(send()和其他任何东西)所需的时间,然后从60ms中减去它以获得等待时间。
答案 2 :(得分:2)
如James Van Huis所述,使用计时器。这样,您至少可以获得正确的平均频率。
来自javadoc的引用:
如果由于任何原因(例如垃圾收集或其他后台活动)延迟执行,则会快速连续执行两次或更多次执行以“赶上”。从长远来看,执行频率将恰好是指定周期的倒数(假设Object.wait(long)下的系统时钟是准确的)。
另外,要回答你的实际问题,但也许是一个有点误导的问题:在我的机器上重用一个实例DatagramPacket并且只设置一个平均“大”微秒的新输出缓冲区...
datagram.setData(out);
socket.send(datagram);
它会略微降低gc的负载,因此如果您以高速率发送它可能是一个好主意。
答案 3 :(得分:1)
除了“仅等待59毫秒”这一显而易见的智能反应之外,实际上还没有很多东西可以实现。您采取的任何操作都将花费一些时间,这可能不一致。因此,无法保证您的数据包将以60 ms的间隔精确传送。
请记住,在UDP和IP层所需的标头中包含微小的56字节消息需要花费时间,并且还有更多时间将其分流到网卡并在途中发送。这为UDP层增加了8个字节,为IP层增加了20个字节,而对于链路层需要的更多内容则更多。你无能为力避免这种情况。
此外,由于您使用的是UDP,因此您无法保证数据包实际到达,或者如果他们这样做,则无法按顺序到达。 TCP可以做出这些保证,但两者都不能保证它们能够按时到达。特别是,网络拥塞可能会降低到达目的地的数据速度,导致数据延迟,甚至与其他数据相比也是如此。因此,尝试使用远程应用程序以精确的间隔控制另一个应用程序是不合理的。如果您的信号实际上在您想要的2毫秒内到达,您应该认为自己很幸运。
答案 4 :(得分:1)
我和你有同样的问题。我找到了解决方案。长话短说,这就是:
查找Java类ScheduledThreadPoolExecutor
,它位于Java 5 JDK / JRE中。
(我只能发布一个链接,因为我刚发现,否则我会指向Oracle JavaDoc)
ScheduledThreadPoolExecutor schedThPoolExec = new ScheduledThreadPoolExecutor(1);
/*
*
* cue a byte buffer for sending in equal segments on the udp port with a inter-pkt-delay
*
*/
public void send(byte[] data, String destinationHost, int destinationPort, double interPacketDelayMs) {
long interDelayNanos = (long) ( interPacketDelayMs * 1000000.0 );
schedThPoolExec.scheduleAtFixedRate( new SendPacketsTimerTask(data, destinationHost, destinationPort), 0, interDelayNanos , TimeUnit.NANOSECONDS);
}
/*
*
*
*
*/
class SendPacketsTimerTask implements Runnable {
int offset = 0;
byte[] buffer;
String host;
int port;
public SendPacketsTimerTask(byte[] buffer, String destinationHost, int destinationPort) {
this.buffer = buffer;
host = destinationHost;
port = destinationPort;
}
@Override
public void run() {
if(offset + PKT_SIZE < buffer.length) {
//copy from cue to packet
byte[] tmp_pkt_buffer = new byte[PKT_SIZE];
System.arraycopy(buffer, offset, tmp_pkt_buffer, 0, PKT_SIZE);
try {
//send packet
socket.send( new DatagramPacket(tmp_pkt_buffer, tmp_pkt_buffer.length, InetAddress.getByName(host), port) );
//increment offset
offset += tmp_pkt_buffer.length;
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
与TimerTask
类(如上所述)一起,您可以定期安排活动。现在您只需编写将发送消息的TimerTask,或者在我的情况下编写数据缓冲区。
我的问题是我正在处理实时媒体流,而且我需要15 Mbits +(视频数据)的吞吐量。这样就可以实现0.5 ms的数据包间延迟。
因此Thread.sleep
方法的粒度(纳米秒参数为真,但仍具有毫秒级粒度 - 也是真的)。所以我坚持使用6 mbit的发送速率。当我查看Timer
课程时,我认为我找到了解决方案。最终发现这个课程也没有处理我的低执行期。搜索有类似问题的人我发现this article
这非常有帮助。您可以使用上面提到的线程调度程序类而不是Timer
类,它可以使用您的完整系统性能绑定到本机代码,从而以尽可能高的分辨率定期运行send方法。
注意:一般意义(也在我的雇主)Java将“太慢”,“有不精确的时间”)来做关键时序应用程序和高网络数据吞吐量最终被认为是错误的。这是正确的。最后使用Java 5,我们可以实现完全可能的计时功能,从而实现应用程序性能:)
答案 5 :(得分:0)
如何以58毫秒的间隔发送数据包?
无论你如何优化(并且确实没有机会这样做;使用面向渠道的NIO也会做同样的工作),需要一些时间来发送数据,而且可能会有一些时间那里的可变性。如果需要精确定时,则需要一些确认传输时间的策略。
另外,关于测量的注意事项:确保在几千次迭代完成之前不测量延迟。这使优化器有机会完成其工作并提供更具代表性的时间。
有一段时间,Windows上的时间分辨率很差。但是,现在通常有1毫秒的分辨率。尝试以下测试,看看您的机器有多精确。
public static void main(String... argv)
throws InterruptedException
{
final int COUNT = 1000;
long time = System.nanoTime();
for (int i = 0; i < COUNT; ++i) {
Thread.sleep(57);
}
time = System.nanoTime() - time;
System.out.println("Average wait: " + (time / (COUNT * 1000000F)) + " ms");
}
在我的Windows XP计算机上,平均等待时间为57.7毫秒。
答案 6 :(得分:0)
如果以60ms的间隔发送数据包,理论上数据包将在目的地以60ms的间隔到达,但这不能保证。一旦数据包到达链路,它们就会成为网络链路的怜悯,这可能包括网络流量,甚至会丢弃沿着路由路径的数据包。
是否有必要在相隔60ms的时间内收到数据包?如果是这样,还有其他协议可以帮助您实现这一目标。
答案 7 :(得分:0)
由于您没有使用实时Java,因此无法确保每隔60ms始终发送一个数据包。我会设置一个计时器线程,它将对另外两个实际发送数据包的等待线程进行“通知”。你可以只用一个线程发送,但我有点肛门有备份,以防出现问题。
答案 8 :(得分:0)
你正在测量nanoTime,所以它会给你纳秒而不是毫秒。
long diff = System.nanoTime() - nanoTime; System.out.println(out.length +“in”+ diff +“ms。”);