业务需求备注:建立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;
}
}