却诚Salong 2022-06-08 11:37 采纳率: 83.3%
浏览 299
已结题

关于JAVA SOCKET UDP的高并发丢包问题

问题遇到的现象和发生背景

我在springboot启动类上开了一个线程,用来监听本地端口9072收到的udp包数据,并且将数据进行封装后续操作,
并发不是很高的时候没问题,但是高并发之后一直丢包,我一次性发的数据(0延迟,一起发)条数超过500左右的时候,控制台就显示直接到了500条数据,后续的包都没有获取到,请问这个问题要怎么处理,求指点。

问题相关代码,请勿粘贴截图
public UDPProcess(final int port) throws SocketException {
        //创建服务器端DatagramSocket,指定端口
        socket = new DatagramSocket(port);
    }

    @Override
    public void run() {
        while (true) {
            byte[] buffer = new byte[MAX_UDP_DATA_SIZE];
            packet = new DatagramPacket(buffer, buffer.length);
            try {
                log.info("waiting udp data!");
                socket.receive(packet);
                new Thread(new sumNum(packet)).start();

//                ThreadPoolTaskExecutor executor = ApplicationContextUtil.getBean(ThreadPoolTaskExecutor.class);
//                executor.execute(new Process(packet));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    class sumNum implements Runnable {
        private String data;

        sumNum(DatagramPacket packet) throws UnsupportedEncodingException {
            this.data = new String(packet.getData(), "UTF-8").trim();
        }

        @Override
        public void run() {
            System.out.println(data);
            RedisTemplate redis = (RedisTemplate) ApplicationContextUtil.getBeanByName("RedisTemplate");
            redis.opsForValue().increment("ss", 1);
        }
    }

运行结果及报错内容
我的解答思路和尝试过的方法

我在启动类上启动了一个线程用来监控数据,没收到数据前,此线程会被阻塞,收到数据之后,新开一个线程,把数据交给新线程并回去继续监控,之前想过搞多个线程一起监控,可以提高并发,但是开多个线程之后就会报错,端口已被占用的问题。现在上面的代码只能接收到并发条数500以内数据,超过就会丢包。

我想要达到的结果

能够支撑不丢包的情况

  • 写回答

2条回答 默认 最新

  • 吕布辕门 后端领域新星创作者 2022-06-08 11:47
    关注

    1.调用recv方法接收端收到数据后,处理数据花了一些时间,处理完后再次调用recv方法,在这二次调用间隔里,发过来的包可能丢失。对于这种情况可以修改接收端,将包接收后存入一个缓冲区,然后迅速返回继续recv。

    2.发送的包巨大丢包。虽然send方法会帮你做大包切割成小包发送的事情,但包太大也不行。例如超过30K的一个udp包,不切割直接通过send方法发送也会导致这个包丢失。这种情况需要切割成小包再逐个send。

    3.发送的包较大,超过mtu size数倍,几个大的udp包可能会超过接收者的缓冲,导致丢包。这种情况可以设置socket接收缓冲。以前遇到过这种问题,我把接收缓冲设置成64K就解决了。

    int nRecvBuf=32*1024;//设置为32K

    setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));

    4.发送的包频率太快,虽然每个包的大小都小于mtu size 但是频率太快,例如40多个mut size的包连续发送中间不sleep,也有可能导致丢包。这种情况也有时可以通过设置socket接收缓冲解决,但有时解决不了。

    5.发送的广播包或组播包在windws和Linux下都接收正常,而arm上接收出现丢包。这个还不好解决,我的解决方法是大包切割成大小为1448的小包发送,每个包之间sleep 1毫秒,虽然笨,但有效。我这里mtu size为1500字节,减去udp包头8个字节,减去传输层几十个字节,实际数据位1448字节。

    除此之外还可以试试设置arm操作系统缓冲:

    //设置mtu size 1500最大

    ifconfig eth0 mtu 1500

    //查看接收缓冲最大和默认大小。

    sysctl -A | grep rmem

    //设置接收缓冲的最大大小

    sysctl -w net.core.rmem_max=1048576

    sysctl -w net.core.rmem_default=1048576

    sysctl -w net.ipv4.udp_mem=1048576

    sysctl -w net.ipv4.udp_rmem_min=1048576

    6,局域网内不丢包,公网上丢包。这个问题我也是通过切割小包并sleep发送解决的。如果流量太大,这个办法也不灵了。

    总之udp丢包总是会有的,如果出现了用我的方法解决不了,还有这个几个方法: 要么减小流量,要么换tcp协议传输,要么做丢包重传的工作。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 系统已结题 6月16日
  • 已采纳回答 6月8日
  • 创建了问题 6月8日

悬赏问题

  • ¥20 docker里部署springboot项目,访问不到扬声器
  • ¥15 netty整合springboot之后自动重连失效
  • ¥15 悬赏!微信开发者工具报错,求帮改
  • ¥20 wireshark抓不到vlan
  • ¥20 关于#stm32#的问题:需要指导自动酸碱滴定仪的原理图程序代码及仿真
  • ¥20 设计一款异域新娘的视频相亲软件需要哪些技术支持
  • ¥15 stata安慰剂检验作图但是真实值不出现在图上
  • ¥15 c程序不知道为什么得不到结果
  • ¥15 键盘指令混乱情况下的启动盘系统重装
  • ¥40 复杂的限制性的商函数处理