WebSocket主动通过线程推动TCP请求

业务需求备注:建立websocket连接时,开启定时任务,线程通过tcp请求获取返回内容,将返回内容定时推送,断开websocket连接时,终止定时任务,同时停止推送,释放资源。

1. WebSocket请求

package com.nwpusct.csal.socket;

import com.nwpusct.csal.common.util.RestResult;
import com.nwpusct.csal.common.util.RestResultUtil;
import com.nwpusct.csal.controller.tcpconnect.MyRunnable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;

/**
 * @ProjectName DatagoSecurityTest-204
 * @Package com.nwpusct.csal.controller.image
 * @Name NdeImageCollectController
 * @Author HB
 * @Date 2021/6/30 17:14
 * @Version 1.0
 */

@ServerEndpoint("/webSocket/{param}")
@Component
@Slf4j
public class WebSocket {

	//定时推送时间
    private static String taskCorn;

    @Value("${taskCorn}")
    public void setTaskCorn(String taskCorn) {
        WebSocket.taskCorn = taskCorn;
    }

    //线程池
    private static ThreadPoolTaskScheduler threadPoolTaskScheduler;
    
    //定时任务
    private ScheduledFuture<?> future1;

    @Autowired
    public void setThreadPoolTaskScheduler(ThreadPoolTaskScheduler threadPoolTaskScheduler) {
        this.threadPoolTaskScheduler = threadPoolTaskScheduler;
    }

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    // 保存连接已连接对象(map键值对的保存方式)
    private static Map<String, Session> sessionPool = new HashMap<String, Session>();

    // 与某一客户端的会话,需要通过客户标识获取对应的session
    private String param;


    //静态变量,记录当前连接数量
    private static int onlineCount = 0;

    @OnOpen
    public void onOpen(Session session, @PathParam(value = "param") String param) {
        this.session = session;
        this.param = param;
        addOnlineCount();  // 在线数加1
        sessionPool.put(param, session);
        log.info("有新连接加入!当前在线人数为" + getOnlineCount());
        startCron();
    }

    @OnClose
    public void onClose(@PathParam(value = "param") String param) {
        sessionPool.remove(param);  //从set中删除
        subOnlineCount();           //在线数减1
        log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
        stopCron();
    }

    // 此为广播消息
    public static void sendAllMessage(String message) {
        for (Map.Entry<String, Session> entry : sessionPool.entrySet()) {
            Session session = entry.getValue();
            String user = entry.getKey();
            if (session != null) {
                try {
                    session.getAsyncRemote().sendText( message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // 消息发送
    public void sendMessage(String message) {
        try {
            this.session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
            log.info("发送错误信息", param, message);
        }
    }


    // synchronized :使用该关键字的作用是为了防止多个线程同时访问
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocket.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocket.onlineCount--;
    }

    public Session getSession() {
        return session;
    }

    // 获取当前所有连接的客户端对象
    public Map<String, Session> getWebSocketSet() {
        return sessionPool;
    }

    //开启定时任务
    public RestResult startCron() {
        future1 = threadPoolTaskScheduler.schedule(new MyRunnable(), new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                return new CronTrigger(taskCorn).nextExecutionTime(triggerContext);
            }
        });
        System.out.println("DynamicTask.startCron()");
        return RestResultUtil.ok("开启定时任务");
    }

    //停止定时任务
    public RestResult stopCron() {
        if (future1 != null) {
            future1.cancel(true);
        }
        System.out.println("DynamicTask.stopCron()");
        return RestResultUtil.ok("关闭定时任务");
    }
}

2 .线程

package com.nwpusct.csal.controller.tcpconnect;

import com.nwpusct.csal.socket.WebSocket;
import net.sf.json.JSONObject;

public class MyRunnable implements Runnable {


    @Override
    public void run() {
        //定时推送内容----根据实际场景推送
        String tcpIP = ToolUtlis.tcpIP;
        Integer tcpPort = ToolUtlis.tcpPort;
        JSONObject jOb = new JSONObject();
        JSONObject jsonObject = new JSONObject();
        //查看开机状态:0(开启),1(关闭)
        String m = TcpToSerialUtil.sendAndReceive(tcpIP, tcpPort, "?M");
        if (m.equals("-1")) {
            jOb.put("code", -1);
            jOb.put("message", "射线机连接失败,请检查后在连接");
        } else {
        //查看开机状态:0(开启),1(关闭)
            if (m.equals("?M000")) {
                jsonObject.put("M", "1");
            } else if (m.equals("?M004")) {
                jsonObject.put("M", "0");
            }
            //查看故障状态:0(无故障),其他(故障码)
            String e = TcpToSerialUtil.sendAndReceive(tcpIP, tcpPort, "?E");
            if (e.equals("?E")) {
                jsonObject.put("E", "0");
            } else {
                jsonObject.put("E", e);
            }
            //查看电压
            String v = TcpToSerialUtil.sendAndReceive(tcpIP, tcpPort, "?V");
            jsonObject.put("V", "?M030");

            //查看电流
            String I = TcpToSerialUtil.sendAndReceive(tcpIP, tcpPort, "?I");
            jsonObject.put("I", "?I000");

            jOb.put("code", 10000);
            jOb.put("message", "成功");
        }
        jOb.put("data", jsonObject);
        WebSocket.sendAllMessage(String.valueOf(jOb));
    }

}

3. TCP连接

package com.nwpusct.csal.controller.tcpconnect;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

@Slf4j
public class TcpToSerialUtil {

    public static String sendAndReceive(String ip, int port, String msg) {
        //这里比较重要,需要给请求信息添加终止符,否则服务端会在解析数据时,一直等待
        msg = msg + "\r";
        //开启一个链接,需要指定地址和端口
        OutputStream out = null;      //写
        InputStream in = null;        //读
        String respData = null;       //响应报文
        Socket socket = null; //客户机
        try {
            socket = new Socket(ip, port);
            socket.setSoTimeout(100000);
            socket.setSoLinger(true, 5);
            socket.setSendBufferSize(1024);
            socket.setReceiveBufferSize(1024);
            socket.setKeepAlive(true);
            //向输出流中写入数据,传向服务端
            out = socket.getOutputStream();
            out.write(msg.getBytes());

            //从输入流中解析数据,输入流来自服务端的响应
            in = socket.getInputStream();
            byte[] buffer = null;
            Thread.sleep(500);
            int bufflenth = in.available();
            while (bufflenth != 0) {
                buffer = new byte[bufflenth];
                in.read(buffer);
                bufflenth = in.available();
            }
            respData = new String(buffer, StandardCharsets.UTF_8);
            log.info("++++++++++++++ " + respData);
        } catch (Exception e) {
            // log.error("TCP连接失败:", e);
            // e.printStackTrace();
            return "-1";
        } finally {
            if (null != socket && socket.isConnected() && !socket.isClosed()) {
                try {
                    socket.close();
                } catch (IOException e) {
                    log.error("关闭客户机Socket时发生异常,堆栈信息如下");
                    e.printStackTrace();
                }
            }
        }
        return respData;
    }
}

4.yml文件配置

#TCP通讯
tcpIP: 192.168.0.1
tcpPort: 10111

5.TCP(ip端口注入)

package com.nwpusct.csal.controller.tcpconnect;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Data
@Component
public class ToolUtlis {

    public static String tcpIP;
    public static Integer tcpPort;

    @Value("${tcpIP}")
    public void setTcpIP(String tcpIP) {
        this.tcpIP = tcpIP;
    }
    @Value("${tcpPort}")
    public void setTcpPort(Integer tcpPort) {
        this.tcpPort = tcpPort;
    }
}

6.验证

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/sinat_37239798/article/details/127262736