upd组播使用nio接收 有序
最近客户需求很苛刻,要求1ms接收到 udp 1条,极限下2微秒1条记录,还不能大量丢包
传统io个人感觉应该也能应付过来,但是还是选用nio ,因为我们是接收端,所以代码很简单。不管是bio(传统io)还是nio其实都是使用一个线程一直处于while循环状态,一直监听
我们还需要处理业务,同时也要保持接收过来的udp报文有序存储,所以需要使用队列
package com.hjkj.udp.cyc.formal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.*; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Iterator; import java.util.concurrent.BlockingQueue; /** * @author cyc * @version 2020-9-28 * * nio流接收udp 数据 并且把数据存放在队列中 * * (生产者) * */ public class NioUdpServer implements Runnable{ private static Logger logger = LoggerFactory.getLogger(NioUdpServer.class); //私有队列(队列设置10w条数据顶峰.超过这个值就报错,如果不想报错,就继续扩大队列// ) private BlockingQueue<byte[]> queue; private int count=0; //通道 private DatagramChannel datagramChannel; //轮询器 private Selector selector; public NioUdpServer(BlockingQueue<byte[]> queue) { this.queue = queue; } //接收方法 @Override public void run(){ //ipv4组协议 try { datagramChannel=DatagramChannel.open(StandardProtocolFamily.INET); datagramChannel.configureBlocking(false); datagramChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true).bind(new InetSocketAddress(18465)); NetworkInterface networkInterface = NetworkInterface.getByInetAddress(InetAddress.getByName("192.168.1.206")); /*添加到组播*/ datagramChannel.join(InetAddress.getByName("230.1.1.61"), networkInterface); //打开轮询器 selector = Selector.open(); datagramChannel.register(selector, SelectionKey.OP_READ); while (selector.select() > 0){ Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); /*缓冲区大小*/ ByteBuffer byteBuffer = ByteBuffer.allocate(1500); while(iterator.hasNext()){ SelectionKey selectionKey = iterator.next(); // 可读事件,有数据到来 if (selectionKey.isReadable()) {
datagramChannel.receive(byteBuffer);
byteBuffer.flip();
SimpleDateFormat format= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); System.err.println("接收到的数量:"+(++count)+"收到客户端消息:{}"+"时间:"+ format.format(new Date()));
//存入到队列 queue.put(byteBuffer.array());
byteBuffer.clear(); } } iterator.remove(); } } catch (SocketException e) { e.printStackTrace(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }
下面是数据处理类,由于公司的业务保密,所以具体的业务代码就不写了
package com.hjkj.udp.cyc.formal; import com.hjkj.udp.cyc.entity.T_Monitor_Manager; import com.hjkj.udp.cyc.utils.RedisUtil; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.math.BigInteger; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.stream.Collectors; /** * @author cyc * @version 2020-9-28 * udp数据处理类 */ @Slf4j public class DataServer implements Runnable { private static Logger logger = LoggerFactory.getLogger(DataServer.class); private BlockingQueue<byte[]> queue; private BlockingQueue<Map<String, Object>> blockcount; private RedisUtil objRedisTemplateUtils; private List<T_Monitor_Manager> lstMonitorItems; public DataServer(BlockingQueue<byte[]> queue, BlockingQueue<Map<String, Object>> blockcount, RedisUtil objRedisTemplateUtils, List<T_Monitor_Manager> listMonitorItems) { this.queue = queue; this.blockcount = blockcount; this.objRedisTemplateUtils = objRedisTemplateUtils; this.lstMonitorItems = listMonitorItems; } @Override public void run() { /* while (true) { try { Thread.sleep(0); } catch (InterruptedException e) { e.printStackTrace(); } if (queue.size() > 0) {*/ read(); /* } }*/ } public void read() {
byte[] buff = queue.poll(); ....省略业务代码
Map map=new HashMap();
blockcount.put(map);
}
}