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

关于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日

悬赏问题

  • ¥50 易语言把MYSQL数据库中的数据添加至组合框
  • ¥20 求数据集和代码#有偿答复
  • ¥15 关于下拉菜单选项关联的问题
  • ¥20 java-OJ-健康体检
  • ¥15 rs485的上拉下拉,不会对a-b<-200mv有影响吗,就是接受时,对判断逻辑0有影响吗
  • ¥15 使用phpstudy在云服务器上搭建个人网站
  • ¥15 应该如何判断含间隙的曲柄摇杆机构,轴与轴承是否发生了碰撞?
  • ¥15 vue3+express部署到nginx
  • ¥20 搭建pt1000三线制高精度测温电路
  • ¥15 使用Jdk8自带的算法,和Jdk11自带的加密结果会一样吗,不一样的话有什么解决方案,Jdk不能升级的情况