昨天跟厂家对接UDP协议信息发送与接收,发现使用原代码发送数据,工具测试接收时延较大,客户端收不到回复信息,且出现第一次发送消息,服务端无响应问题,晚上想了很久,一是怀疑服务器问题,二是怀疑代码问题。
今早,将代码迁移到另一台服务器(华为云华北四区),测试发现响应正常,但是时延依旧较大,客户端依旧无法收到信息。跟厂家沟通,厂家发来一个C#的服务端代码,运行后,发现C#项目接收端口与发送端口为同一个端口号,响应时延特别小,2秒以内,且客户端可以收到回复消息;初步怀疑是服务端在不指定端口的情况下,在服务器发送信息是,开始寻找发送端口,然后本机发送端口与公网端口映射时耗费时间较长,导致时延较大;判断客户端只识别服务器的接收端口返回的信息,其他端口信息均被过滤掉了;华为云两台服务器配置相同,代码相同,已提交工单,等待官方回复。
发现问题就要解决问题,修改java代码,由服务器端随机端口发送返回消息,改为由接收端口发送返回消息,测试后,工具测试接收时延明显改善,客户端可以收到回复消息。
代码参考Java SpringBoot 循环监听UDP同一个Socket实现接收与发送,再次表示感谢。
UDP业务代码如下:
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/**
*
*/
@WebListener
public class UdpServer implements ServletContextListener {
public static final int MAX_UDP_DATA_SIZE = 4096;
public static final int UDP_PORT = 23;
public static DatagramPacket packet = null;
public static DatagramSocket socket = null;
private final static Logger logger = LoggerFactory.getLogger(UdpServer.class);
@Override
public void contextInitialized(ServletContextEvent sce) {
try {
logger.info("========启动一个线程,监听UDP数据报.PORT:" + UDP_PORT + "=========");
new Thread(new UdpProcess(UDP_PORT)).start();
} catch (Exception e) {
e.printStackTrace();
}
}
static class UdpProcess implements Runnable {
private int times1;
public UdpProcess(final int port) throws SocketException {
socket = new DatagramSocket(port);
}
@SneakyThrows
@Override
public void run() {
while (true) {
Thread.sleep(1);
byte[] buffer = new byte[MAX_UDP_DATA_SIZE];
packet = new DatagramPacket(buffer, buffer.length);
try {
socket.receive(packet);
logger.info("++++++++++++" + packet.getAddress().getHostAddress());
new Thread(new Process(packet)).start();
} catch (IOException e) {
times1 = times1++;
logger.error(times1 + "次数");
e.printStackTrace();
}
}
}
}
static class Process implements Runnable {
public Process(DatagramPacket packet) {
byte[] buffer = packet.getData();
String srt2 = new String(buffer, StandardCharsets.UTF_8).trim();
logger.info("=======接收到的数据======" + srt2);
logger.info("连接地址" + packet.getAddress().getHostAddress() + ":" + packet.getPort());
Map<String, String> stringMap = packetAnalysis(srt2);
}
@Override
public void run() {
int times1 = 0;
String msg = "BYE;";
try {
logger.info("====向客户端响应数据====="+msg);
InetAddress address = packet.getAddress();
int port = packet.getPort();
byte[] data2 = msg.getBytes();
DatagramPacket packet2 = new DatagramPacket(data2, data2.length, address, port);
socket.send(packet2);
} catch (Exception e) {
times1++;
logger.error(times1 + "次");
e.printStackTrace();
}
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
logger.info("========UdpListener销毁=========");
}
}
@ServletComponentScan Servlet扫描,启动时把servlet、filter、listener自动扫描注入
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.ConfigurableApplicationContext;
/**
* @author tiany
*/
@SpringBootApplication
@ServletComponentScan
public class BlackAntsApplication {
public static void main(String[] args) throws InterruptedException {
ConfigurableApplicationContext run = SpringApplication.run(BlackAntsApplication.class, args);
}
}
测试代码:
public static final String SERVER_HOSTNAME = "localhost";
/**
* 本地发送端口
* 不能接收与端口相同
*/
public static final int LOCAL_PORT = 24;
/**
* 测试100万请求下会不会出现问题
* @param args
* @throws SocketException
*/
public static void main(String[] args) throws SocketException {
new Thread(new UdpProcess(UDP_PORT)).start();
try {
DatagramSocket socket = new DatagramSocket(LOCAL_PORT);
byte[] buf = "我是來測試的".getBytes();
DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName(SERVER_HOSTNAME),
UDP_PORT);
for (int i = 0; i < 1000000; i++) {
System.out.println(i);
socket.send(dp);
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
测试结束,异常为零。