WebSocket actively pushes TCP requests through threads

Remarks for business requirements: When a websocket connection is established, a scheduled task is started, and the thread obtains the returned content through a tcp request, and pushes the returned content regularly. When the websocket connection is disconnected, the scheduled task is terminated, and the push is stopped at the same time to release resources.

1. WebSocket request

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. Thread

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 connection

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 file configuration

#TCP通讯
tcpIP: 192.168.0.1
tcpPort: 10111

5.TCP (ip port injection)

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. Verify

insert image description here

Guess you like

Origin blog.csdn.net/sinat_37239798/article/details/127262736