UDP组播方式接收数据(监听,定时器,多线程)

1.功能描述

概述:
使用UDP主播方式接收数据,将接收到的消息对数据库进行更改 与 存入缓冲。
具体逻辑:
1.从数据库中读取接收设备的ip和端口等信息
2.根据设备数量建立监听
3.在每个监听对象建立一个计时器,超过设定时间没监听到数据,将当前监听设备设置为离线状态

2.辅助工具类

主要用途是作为主类中的属性,使得能在静态方法中使用mapper中的方法访问数据库

import com.maphao.windfarms.radar.mapper.ManageRadarConfigMapper;
import lombok.Data;
import org.springframework.data.redis.core.RedisTemplate;

/**
 * @Author: fxp
 * @Date: 2024/4/11 11:31
 * @Description
 */
@Data
public class ManageRadarConfigUtil {

    private ManageRadarConfigMapper mapper;

    private RedisTemplate objRedisTemplate;

}

3.主程序

package com.maphao.windfarms.radar.client;

import com.alibaba.fastjson.JSONObject;
import com.maohao.common.config.RedisCacheContent;
import com.maphao.windfarms.data.dto.equipment.EquipmentStateDto;
import com.maphao.windfarms.data.dto.radar.RadarClientDto;
import com.maphao.windfarms.data.dto.radar.RadarStatusMessage;
import com.maphao.windfarms.radar.mapper.ManageRadarConfigMapper;
import com.maphao.windfarms.radar.util.ManageRadarConfigUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

/**
 * @Author: fxp
 * @Date: 2024/4/11 10:42
 * @Description
 */
@Slf4j
@Component
public class RadarClient{

    @Resource
    private ManageRadarConfigMapper mapper;

    @Resource(name="objectRedisTemplate")
    private RedisTemplate objRedisTemplate;

    private static ManageRadarConfigUtil manageRadarConfigUtil;

    // 计时器计数规则,超过当前值,值方法并重制计时器
    private static final int TIMEOUT_DURATION = 60; // 1分钟,单位:秒

    // 计时器线程
    private static Map<Integer,ScheduledExecutorService> mapTimer = new HashMap<>();

    // 监听线程
    private static List<Thread> ts = new ArrayList<>();

    @PostConstruct
    public void init(){
        log.info("===========初始化=========");
        manageRadarConfigUtil = new ManageRadarConfigUtil();
        manageRadarConfigUtil.setMapper(this.mapper);
        manageRadarConfigUtil.setObjRedisTemplate(this.objRedisTemplate);
        run();
    }

    public void run(){

        log.info("=============执行run方法============");
        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

        // 定义要执行的任务
        Runnable task = () -> {
            log.info("=================执行任务===================");
            // 停止并删除线程
            if (!ts.isEmpty()) {
                log.info("==================停止并删除监听线程==================");
                Iterator<Thread> iterator = ts.iterator();
                while (iterator.hasNext()) {
                    Thread t = iterator.next();
                    // 判断线程是否需要停止
                    t.interrupt(); // 停止线程
                    iterator.remove(); // 删除线程
                }
            }
            // 停止并删除计时器
            if (!mapTimer.isEmpty()){
                log.info("==================停止并删除计时器==================");
                Iterator<Map.Entry<Integer, ScheduledExecutorService>> iterator = mapTimer.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<Integer, ScheduledExecutorService> entry = iterator.next();
                    int id = entry.getKey();
                    stopAndRemoveTimer(id);
                }
            }
            List<RadarClientDto> dtos =  manageRadarConfigUtil.getMapper().selectIpAndPort();
            if (null != dtos){
                for (RadarClientDto dto : dtos) {
                    log.info("====================加入监听线程====================");
                    Thread t = new Thread(new Task(dto));
                    ts.add(t);
                }
            }
            if (ts.size() > 0){
                for (Thread t : ts) {
                    t.start();
                }
            }
        };
        // 每隔5分钟执行一次任务
        executor.scheduleAtFixedRate(task, 0, 5, TimeUnit.MINUTES);
    }

    // 定义一个任务类,实现 Runnable 接口
    private static class Task implements Runnable {
        private final Integer pid;
        private final String ip;
        private final Integer port;
        private final String code;

        public Task(RadarClientDto dto) {
            this.pid = dto.getPid();
            this.ip = dto.getIp();
            this.port = dto.getPort();
            this.code = dto.getCode();
        }

        @Override
        public void run() {
            // 执行任务的逻辑
            log.info("pid = {},ip = {},port = {},code = {},》》》》》开始监听》》》》》",pid,ip,port,code);
            RadarClient.monitoring(pid,ip,port,code);
        }
    }

    private static void monitoring(Integer pid,String ip,Integer port,String code){
        startTimer(pid);
        try {
            // 指定组播地址和端口号
            InetAddress group = InetAddress.getByName(ip);

            // 创建 MulticastSocket 实例并加入组播组
            MulticastSocket multicastSocket = new MulticastSocket(port);
            multicastSocket.joinGroup(group);

            byte[] buffer = new byte[1024];

            while (true) {
                log.info("========监听到数据=======");
                // 接收数据包
                DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                multicastSocket.receive(packet);

                // 解析数据并打印
                String json = new String(packet.getData(), 0, packet.getLength());
                log.info("=========数据内容========{}",json);
                RadarStatusMessage radarStatusMessage = JSONObject.parseObject(json, RadarStatusMessage.class);
                if (null != radarStatusMessage && null != radarStatusMessage.getRadarState()){
                    manageRadarConfigUtil.getMapper().updateStatus(pid,radarStatusMessage.getRadarState());
                    if (radarStatusMessage.getRadarState() == 1){
                        setRedis(code);
                    }
                }else {
                    manageRadarConfigUtil.getMapper().updateStatus(pid,1);
                }
                resetTimer(pid);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void setRedis(String code){
        log.info("========放入缓存=======");
        // 将状态放入redis中
        EquipmentStateDto stateDto  = new EquipmentStateDto();
        stateDto.setCode(code);
        stateDto.setName("");
        stateDto.setUpdateTime(new Date());
        stateDto.setState(1);
        manageRadarConfigUtil.getObjRedisTemplate().opsForValue().set(RedisCacheContent.equipmentState+code,
                stateDto,RedisCacheContent.videoOffLineTime, TimeUnit.SECONDS);
    }

    private static void startTimer(Integer pid) {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
        mapTimer.put(pid,executorService);
        executorService.schedule(() -> {
            // 在超时时执行相应的操作
            log.info("=========未接收到消息,超时!=========");
            manageRadarConfigUtil.getMapper().updateStatus(pid,0);
            // 例如:关闭连接或执行其他操作
            stopAndRemoveTimer(pid);
//            resetTimer(pid);
        }, TIMEOUT_DURATION, TimeUnit.SECONDS);
    }

    private static void resetTimer(Integer pid) {
        log.info("===========取消之前的计时器任务并重新启动计时器==========");
        stopAndRemoveTimer(pid);
        startTimer(pid);
    }

    public static void stopAndRemoveTimer(int id) {
        log.info("=====停止计时器====");
        ScheduledExecutorService executorService = mapTimer.get(id);
        if (executorService != null) {
            // 停止执行器服务
            executorService.shutdownNow();
            try {
                // 等待执行器服务终止
                if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
                    // 强制终止
                    executorService.shutdownNow();
                }
            } catch (InterruptedException e) {
                // 重新中断当前线程
                Thread.currentThread().interrupt();
            }

            // 从映射中移除计时器
            mapTimer.remove(id);
            log.info("Timer with ID " + id + " stopped and removed.");
        } else {
            log.info("Timer with ID " + id + " not found.");
        }
    }
}

4.测试,发送消息

package com.fxp.util;

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

/**
 * 雷达
 * @Author: fxp
 * @Date: 2024/4/11 10:32
 * @Description
 */
public class LDUtil{

    private static final int PORT = 8808;
    private static final String MULTICAST_GROUP = "222.190.220.31";


    public static void main(String[] args) {
        sentMessege();
    }

    public static void sentMessege(){
        try {
            // 创建组播套接字
            InetAddress group = InetAddress.getByName(MULTICAST_GROUP);
            MulticastSocket multicastSocket = new MulticastSocket();

            // 服务器状态信息
            String statusMessage = "{\n" +
                    "  \"RadarType\": 1,\n" +
                    "  \"RadarState\": 1,\n" +
                    "  \"PRFCode\": 2,\n" +
                    "  \"AntennaSpeed\": null,\n" +
                    "  \"Temperature\": null\n" +
                    "}";

            // 发送状态信息
            DatagramPacket packet = new DatagramPacket(statusMessage.getBytes(), statusMessage.length(), group, PORT);
            multicastSocket.send(packet);

            // 关闭套接字
            multicastSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值