使用californium进行CoAP通信的时候,客户端发送速度过快的时候,有时候会出现连续4分钟(实际上是247秒)发不出数据的。
原因是,californium实现了一个UDP异步发送可靠性框架。根据CoAP协议,它每个请求需要等待服务端的响应返回。它在发送报文的时候,实际上是会把上次发送报文的时间,保存到内存表中,等到收到服务器的报文的时候,那么就会刷新这个时间。
那么下次发送报文的时候,它就根据这个内存表记录的时间,决定要不要真的发送数据。
如果默认的4分钟超时后,依然没有返回,那么就可以发送下一个数据了。
但是UDP报文是不可靠的,可能会有概率性丢失,那么这个时候,就会等待超时4分钟。
那么就会造成4分钟,虽然你不断调用CoapClient的发送接口,但实际上无法真实发送数据的问题。
4分钟啊,很多应用都是不可接受的。
// 问题代码
@Override
public int getNextMessageId() {
final long now = ClockUtil.nanoRealtime();
synchronized (this) {
// mask mid to the min-max range
int mid = (currentMID & 0xffff) % range;
int index = mid / sizeOfGroups;
int nextIndex = (index + 1) % numberOfGroups;
// 问题代码
if ((midLease[nextIndex] - now) < 0) {
midLease[index] = now + exchangeLifetimeNanos;
currentMID = mid + 1;
return mid + min;
}
}
String time = TimeUnit.NANOSECONDS.toSeconds(exchangeLifetimeNanos) + "s";
throw new IllegalStateException(
"No MID available, all [" + min + "-" + (min + range) + ") MID-groups in use! (MID lifetime " + time + "!)");
}
问题的补救措施。
在创建CoapClient对象的时候,调用下列代码,将生命周期设置为2000秒,那么这时候实际上时间窗口比较短,即使UDP丢包,也不会造成造成长时间等待
EndpointManager.getEndpointManager().getDefaultEndpoint(CoAP.COAP_URI_SCHEME).getConfig().set(CoapConfig.EXCHANGE_LIFETIME, 2000L, TimeUnit.MILLISECONDS);
范例测试代码
public static void hello() throws Exception {
// 创建一个资源请求hello资源,注意默认端口为5683
URI uri = new URI("localhost:5684/hello");
CoapClient client = new CoapClient(uri);
// 关键代码:设置发送缓冲区的生命周期
EndpointManager.getEndpointManager().getDefaultEndpoint(CoAP.COAP_URI_SCHEME).getConfig().set(CoapConfig.EXCHANGE_LIFETIME, 2000L, TimeUnit.MILLISECONDS);
CoapResponse response = null;
for (int i = 0; i < 1000000; i++) {
try {
response = client.get();
if (response == null) {
int x = 0;
} else {
int x = 0;
}
} catch (IOException ioe) {
if (ioe.getCause() instanceof IllegalStateException && "automatic message IDs exhausted".equals(ioe.getCause().getMessage())) {
// Thread.sleep(100);
}
} catch (Exception e) {
e.printStackTrace();
continue;
}
}