云服务器UDP通讯时延大,客户端无法收到返回信息的解决方案

昨天跟厂家对接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;
/**
 * https://blog.csdn.net/qq_40369944/article/details/89292906
 */
@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();
        }
    }

测试结束,异常为零。
测试结束异常记录为0

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值