前段时间使用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();
}
}
}