前段时间使用Netty实现udp和多个无线wifi数据采集模块进行异步通信,结果发现丢包很严重,丢包率达到了百分之一,实在让人犯愁。领导说可能是底层代码出了问题,然后我使用java原生的udp进行同步通信,发现木有存在丢包问题。目前还不知道自己到底踩了什么坑,先记录于此,等哪天有空了再回来想想这个坑到底在哪。使用udp异步通信的客户端代码如下:
public class UdpClient { public void run(String hostName,int port,Frame sendRecvFrame) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group).channel(NioDatagramChannel.class) .option(ChannelOption.SO_BROADCAST, true) .handler(new UdpClientHandler(sendRecvFrame)); //绑定端口 若为0 可为任意端口 9990 Channel ch = b.bind(UdpMThreadClient.srcPort).sync().channel(); byte[] hexByte= HexUtil.hexStringToByte(sendRecvFrame.getSendFrame()); //startTime UdpMThreadClient.startTime=System.currentTimeMillis(); UdpMThreadClient.fileWriter.write(UdpMThreadClient.startTime+","); System.out.println("起始时间为:"+ UdpMThreadClient.startTime); ch.writeAndFlush( new DatagramPacket(Unpooled.copiedBuffer(hexByte), new InetSocketAddress( hostName, port))).sync(); UdpMThreadClient.sendCommandCount++; if (!ch.closeFuture().await(1000)) { //超时时间设置为1000ms UdpMThreadClient.errorReceiveCount++; System.out.println("等待超时"); System.out.println("目前的误包数为:"+ UdpMThreadClient.errorReceiveCount); UdpMThreadClient.endTime=System.currentTimeMillis(); UdpMThreadClient.fileWriter.write(sendRecvFrame.getSendFrame()+",");//发送数据 UdpMThreadClient.fileWriter.write(UdpMThreadClient.endTime+",");//接收数据时间戳 UdpMThreadClient.fileWriter.write("timeout"+",");//接收数据 UdpMThreadClient.fileWriter.write(UdpMThreadClient.errorReceiveCount+",");//错误个数 UdpMThreadClient.fileWriter.write(UdpMThreadClient.sendCommandCount+","); //发送总数 UdpMThreadClient.fileWriter.write((UdpMThreadClient.endTime-UdpMThreadClient.startTime)+","); //响应时间 UdpMThreadClient.fileWriter.write("2s\r\n"); //发送数据时间间隔 UdpMThreadClient.fileWriter.flush(); } } finally { group.shutdownGracefully(); } }这个主要的坑在于很多包会超时,将程序丢包结果截图如下:
使用PC和十个模块进行通信,结果每个udp通信都遭遇了大量的丢包。因此,再使用同步通信,测试一下。
udp同步通信的代码如下:
public class UdpClientSync { public static void main(String[] args){ DatagramSocket sendSocket=null; long sendCommandCount=0; long errCount=0; long startTime=0; long endTime=0; long fileIndex=0; String textFilePath="E:/IdeaProjects/wirelesscom/data/"; FileWriter fileWriter=UdpMThreadClient.createFileWriter(textFilePath, fileIndex); try{ //创建发送方的套接字,IP默认为本地,端口号9990 InetAddress srcInetAddress=InetAddress.getByName("192.168.1.127"); int srcPort=9990; sendSocket=new DatagramSocket(srcPort,srcInetAddress); //确定要发送的数据 String mes="01020000000555AA"; byte[] buf= HexUtil.hexStringToByte(mes); InetAddress ip=InetAddress.getByName("192.168.1.150"); //创建发送类型的数据报 int port=6010; while(sendCommandCount<30000){ DatagramPacket sendPacket=new DatagramPacket(buf,buf.length,ip,port); startTime=System.currentTimeMillis(); sendSocket.send(sendPacket); sendCommandCount++; byte[] getBuf=new byte[1024]; //创建接收型的数据报 DatagramPacket getPacket=new DatagramPacket(getBuf,getBuf.length); //通过套接字接收数据 sendSocket.receive(getPacket); endTime=System.currentTimeMillis(); //解析反馈的信息,并打印 byte[] responseByte = new byte[getPacket.getLength()]; System.arraycopy(getBuf,0,responseByte,0,getPacket.getLength()); String response= HexUtil.bytesToHexString(responseByte); String originData="01020100A188"; if(response.equals(originData)) { System.out.println("True:" + mes + ":" + response); }else{ errCount++; System.out.println("false:" + mes + ":" + response); } System.out.println("发送时间为:"+startTime); System.out.println("接收时间为:"+endTime); System.out.println("响应时间为:"+(endTime-startTime)+"ms"); System.out.println("误包数为:" + errCount + ",总数为:" + sendCommandCount); fileWriter.write(startTime+",");//发送数据时间戳 fileWriter.write(mes+",");//发送数据 fileWriter.write(endTime+",");//接收数据时间戳 fileWriter.write( response+",");//接收数据 fileWriter.write(errCount+",");//错误个数 fileWriter.write(sendCommandCount+","); //发送总数 fileWriter.write((endTime-startTime)+","); //响应时间 fileWriter.write("1000\r\n"); //回车换行 fileWriter.flush(); long index=sendCommandCount/2000; if(index>fileIndex){ fileIndex=index; if(fileWriter!=null){ fileWriter.flush(); fileWriter.close(); } fileWriter=UdpMThreadClient.createFileWriter(textFilePath, fileIndex);; } Thread.sleep(1000); } }catch(Exception e){ e.printStackTrace(); }finally{ //关闭套接字 if(sendSocket!=null){ sendSocket.close(); } } } }结果发现并没有遇到错误,截图如下:
因为wifi采集模块自带服务端,所以一般不需要写服务端,为了方便以后自己对udp进行测试,特将udp服务端代码贴图如下:
public class UdpServerSync { public static void main(String[] args){ DatagramSocket getSocket=null; try{ //确定接收方的IP和端口号,IP地址为本地机器地址 InetAddress ip=InetAddress.getByName("192.168.1.127"); int port =10030; //创建接收方的套接字,并制定端口号和IP地址 getSocket=new DatagramSocket(port,ip); while(true){ byte[] buf=new byte[1024]; DatagramPacket getPacket=new DatagramPacket(buf,buf.length); getSocket.receive(getPacket); //解析发送方传递的信息 byte[] responseByte = new byte[getPacket.getLength()]; System.arraycopy(buf,0,responseByte,0,getPacket.getLength()); String response= HexUtil.bytesToHexString(responseByte); System.out.println("对方发送的信息:"+response); //通过数据报得到发送方的IP和端口号,并打印 InetAddress sendIP=getPacket.getAddress(); int sendPort=getPacket.getPort(); System.out.println("对方的IP地址是:"+sendIP.getHostAddress()); System.out.println("对方的端口号是:"+sendPort); //通过数据报得到发送方的套接字地址 SocketAddress sendAddress=getPacket.getSocketAddress(); String feedback="01010100A188"; byte[] backBuf= HexUtil.hexStringToByte(feedback); //创建发送类型的数据报 DatagramPacket sendPacket=new DatagramPacket(backBuf,backBuf.length,sendAddress); //通过套接字发送数据 getSocket.send(sendPacket); } }catch(Exception e){ e.printStackTrace(); }finally{ if(getSocket!=null){ getSocket.close(); } } }