实现后台向前端推送信息
pom.xml引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
WebSocketConfig
启用WebSocket的支持也是很简单,几句代码搞定
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* 开启WebSocket支持
* @author zhengkai
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
WebSocketServer
因为WebSocket是类似客户端服务端的形式(采用ws协议),那么这里的WebSocketServer其实就相当于一个ws协议的Controller
直接@ServerEndpoint("/websocket")@Component启用即可,然后在里面实现@OnOpen,@onClose,@onMessage等方法
/**
* @Author: ynz
* @Date: 2018/12/22/022 10:35
*/
@ServerEndpoint("/websocket/{sid}")
@Component
@Slf4j
public class WebSocketServer {
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
private static CopyOnWriteArraySet<WebSocketServer> webSocketSet =
new CopyOnWriteArraySet<WebSocketServer>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
//接收sid
private String sid="";
/**
* 连接建立成功调用的方法*/
@OnOpen
public void onOpen(Session session,@PathParam("sid") String sid) {
this.session = session;
webSocketSet.add(this); //加入set中
addOnlineCount(); //在线数加1
log.info("有新窗口开始监听:"+sid+",当前在线人数为" + getOnlineCount());
this.sid=sid;
try {
sendMessage("连接成功");
} catch (IOException e) {
log.error("websocket IO异常");
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
webSocketSet.remove(this); //从set中删除
subOnlineCount(); //在线数减1
log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("收到来自窗口"+sid+"的信息:"+message);
//群发消息
for (WebSocketServer item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("发生错误");
error.printStackTrace();
}
/**
* 实现服务器主动推送
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 群发自定义消息
* */
public static void sendInfo(String message,@PathParam("sid") String sid)
throws IOException {
log.info("推送消息到窗口"+sid+",推送内容:"+message);
for (WebSocketServer item : webSocketSet) {
try {
//这里可以设定只推送给这个sid的,为null则全部推送
if(sid==null) {
item.sendMessage(message);
}else if(item.sid.equals(sid)){
item.sendMessage(message);
}
} catch (IOException e) {
continue;
}
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
消息推送
至于推送新信息,可以再自己的Controller写个方法调用WebSocketServer.sendInfo();即可
@Controller
public class CheckCenterController {
//推送数据接口
@ResponseBody
@RequestMapping("/socket/push/{cid}")
public String pushToWeb(@PathVariable String cid,@RequestBody String message) {
try {
WebSocketServer.sendInfo(message,cid);
} catch (IOException e) {
e.printStackTrace();
}
return message;
}
}
页面发起socket请求
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script src="js/jquery.min.js"></script>
<script>
var socket = null;
function connect(){
if(typeof(WebSocket) == "undefined") {
console.log("您的浏览器不支持WebSocket");
}else{
console.log("您的浏览器支持WebSocket");
//实现化WebSocket对象,指定要连接的服务器地址与端口 建立连接
socket = new WebSocket($("#url").val());
//打开事件
socket.onopen = function() {
console.log("Socket 已打开");
$("#status").html("已连接...");
//socket.send("这是来自客户端的消息" + location.href + new Date());
};
//获得消息事件
socket.onmessage = function(msg) {
console.log(msg.data);
$("#displayMsg").html( $("#displayMsg").html()+"<br>"+msg.data );
//发现消息进入 开始处理前端触发逻辑
};
//关闭事件
socket.onclose = function() {
console.log("Socket已关闭");
$("#status").html("未连接...");
socket = null;
};
//发生了错误事件
socket.onerror = function() {
alert("Socket发生了错误");
//此时可以尝试刷新页面
}
}
}
function send() {
if(socket == null){
alert("未连接");
return false;
}
socket.send($("#sendMsg").val());
}
function closeConnect(){
$("#status").html("已断开...");
socket.close();
}
</script>
</head>
<body>
连接地址:<input type="text" id="url" style="width:400px;" value="ws://127.0.0.1:8080/websocket/22"></input>
<button type="button" id="connect" onclick="connect()">连接</button>
<button type="button" id="closeConnect" onclick="closeConnect()">断开</button>
<div id="status" style="display:inline;">未连接...</div>
<br><br>
发送消息:<input type="text" id="sendMsg" style="width:400px;"></input>
<button type="button" onclick="send()">发送</button><br><br>
<div>接收到消息:</div>
<div id="displayMsg"></div>
</body>
</html>
访问:http://127.0.0.1:8080/socket.html,可以连接服务,发送消息。
或者:http://127.0.0.1:8080/socket/push/{cid}给相应的服务发消息。
实现SSH WEB客户端
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>
WebSocketMessageBrokerConfigurer
配置消息代理,默认情况下使用内置的消息代理。 类上的注解@EnableWebSocketMessageBroker:此注解表示使用STOMP协议来传输基于消息代理的消息,此时可以在@Controller类中使用@MessageMapping
在方法registerStompEndpoints()里addEndpoint方法:添加STOMP协议的端点。这个HTTP URL是供WebSocket或SockJS客户端访问的地址;withSockJS:指定端点使用SockJS协议
在方法configureMessageBroker()里设置简单消息代理,并配置消息的发送的地址符合配置的前缀的消息才发送到这个broker
@Configuration
// 此注解表示使用STOMP协议来传输基于消息代理的消息,此时可以在@Controller类中使用@MessageMapping
@EnableWebSocketMessageBroker
public class SSHSocketConfig implements WebSocketMessageBrokerConfigurer {
/**
* setAllowedOrigins方法用来设置来自那些域名的请求可访问,默认为localhost
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket")
.setAllowedOrigins("*");
//SockJS客户端访问
/*registry.addEndpoint("/my-websocket").withSockJS();*/
}
/**
* 配置消息代理
* 启动Broker,消息的发送的地址符合配置的前缀来的消息才发送到这个broker
*/
public void configureMessageBroker(MessageBrokerRegistry registry) {
/**
* 配置消息代理
* 启动简单Broker,消息的发送的地址符合配置的前缀来的消息才发送到这个broker
*/
registry.enableSimpleBroker("/topic");
//只接收这前缀发送过来的消息
registry.setApplicationDestinationPrefixes("/send");//应用请求前缀
}
}
@Controller类 ,消息处理
@Controller
public class SSHController {
@Resource
private SimpMessagingTemplate messagingTemplate ;
static Map<String,SSHData> map = new HashMap<>();
/**
* 接收消息
*/
@MessageMapping("/receive/{id}")
// @SendTo("/topic/test")
public String receiver(@DestinationVariable("id") String id, String msg)
throws IOException {
SSHData sshData = map.get(id);
if(sshData != null){
OutputStream outputStream = map.get(id).getOutputStream();
outputStream.write((msg).getBytes());
outputStream.flush();
}else{
messagingTemplate.convertAndSend("/topic/"+id,"远程服务器未连接。。。\n\r");
}
return msg;
}
/**
* 建立SSH连接
*/
@RequestMapping("/connect")
@ResponseBody
public String connect(String user,String host,Integer port,String password,String id)
throws IOException {
SSHData sshData = map.get(id);
if(sshData != null){
sshData.release();
}
ChannelShell channelShell = SshUtils.getShellChannel( user, host, port , password, id);
if(channelShell == null){
messagingTemplate.convertAndSend("/topic/"+id,
"远程服务器连接失败,请检查用户或者密码是正确\n\r");
return "";
}
map.put(id,new SSHData(channelShell,messagingTemplate,id));
return "";
}
/**
* 断开连接
*/
@RequestMapping("/disconnect")
@ResponseBody
public String disConnect(String id) throws IOException {
SSHData sshData = map.get(id);
if(sshData != null){
sshData.release();
map.remove(id);
}
messagingTemplate.convertAndSend("/topic/"+id,"已断开连接。。。\n\r");
return "";
}
}
-
@MessageMapping:指定要接收消息的地址,类似@RequestMapping
-
@SendTo默认消息将被发送到与传入消息相同的目的地,但是目的地前面附加前缀(默认情况下为“/topic”}
扫描二维码关注公众号,回复: 4649555 查看本文章 -
@DestinationVariable接收URL的参数,类似@PathVariable
前端stomp、sockjs的配置
Stomp
websocket使用socket实现双工异步通信能力。但是如果直接使用websocket协议开发程序比较繁琐,我们可以使用它的子协议Stomp
SockJS
sockjs是websocket协议的实现,增加了对浏览器不支持websocket的时候的兼容支持 SockJS的支持的传输的协议有3类: WebSocket, HTTP Streaming, and HTTP Long Polling。默认使用websocket,如果浏览器不支持websocket,则使用后两种的方式。
SockJS使用”Get /info”从服务端获取基本信息。然后客户端会决定使用哪种传输方式。如果浏览器使用websocket,则使用websocket。如果不能,则使用Http Streaming,如果还不行,则最后使用 HTTP Long Polling。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script src="js/jquery.min.js"></script>
<script src="js/xterm/sockjs.min.js"></script>
<script src="js/xterm/stomp.min.js"></script>
<link href="js/xterm/xterm.css" rel="stylesheet"></link>
<script src="js/xterm/xterm.js"></script>
</head>
<body>
<div id="terminal"></div>
<div id="terminal2" style="height: 10%">
<div id="desc"></div><br>
IP:<input id="ip" type="text" value="47.106.106.**"></input>
Port:<input id="port" type="text" value="22"></input>
用户名:<input id="username" type="text" value="root"></input>
密码:<input id="password" type="text" value="*"></input>
<button onclick="connect()">登录</button>
<button onclick="disconnect()">断开</button>
</div>
<script>
var stompClient ;
var term = new Terminal({
cols: 150,
rows: 35,
screenKeys: true,
useStyle: true,
cursorBlink: true
});
term.open(document.getElementById('terminal'));
term.on('data', function($data) {
//term.write($data);
stompClient.send("/send/receive/1",{}, $data );
});
document.onkeydown=function(){
if (event.keyCode == 13){
term.write("\n\r");
}
}
</script>
<script>
$(document).ready(function() {
openSocket();
});
function openSocket() {
if(stompClient==null){
var socketPath ='ws://127.0.0.1:8080/websocket';
var socket = new WebSocket(socketPath);
stompClient = Stomp.over(socket);
var headers={
"Access-Control-Allow-Origin":"*",
"Access-Control-Allow-Credentials":"true",
"token":"kltoen"
};
stompClient.connect(headers, function(frame) {
$("#desc").html("WebSocket已连接");
stompClient.subscribe('/topic/1', function(event) {
term.write(event.body);
},headers);
},function (error) {
$("#desc").html("WebSocket连接失败");
});
window.setInterval(function(){ //每隔5秒钟发送一次心跳,避免websocket连接超时而自动断开
stompClient.send(" ");
},30000);
}
}
function connect() {
$.ajax({
type: "GET",
url: "http://127.0.0.1:8080/connect?user="+$("#username").val()+
"&host="+$("#ip").val()+ "&port="+$("#port").val()+
"&password="+$("#password").val()+"&id=1"
});
}
function disconnect() {
$.ajax({
type: "GET",
url: "http://127.0.0.1:8080/disconnect?&id=1"
});
}
</script>
</body>
</html>