html 自动连接websocket_Springboot2(20)轻松搞定WebSocket

实现后台向前端推送信息

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

http://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

http://log.info("有一连接关闭!当前在线人数为" + getOnlineCount());

}

/**

* 收到客户端消息后调用的方法

*

* @param message 客户端发送过来的消息*/

@OnMessage

public void onMessage(String message, Session session) {

http://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 {

http://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" οnclick="connect()">连接</button>

<button type="button" id="closeConnect" οnclick="closeConnect()">断开</button>

&nbsp;&nbsp;&nbsp;&nbsp;<div id="status" style="display:inline;">未连接...</div>

<br><br>

发送消息:<input type="text" id="sendMsg" style="width:400px;"></input>

<button type="button" οnclick="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,"远程服务器未连接。。。nr");

}

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,

"远程服务器连接失败,请检查用户或者密码是正确nr");

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,"已断开连接。。。nr");

return "";

}

}

@MessageMapping:指定要接收消息的地址,类似@RequestMapping

@SendTo默认消息将被发送到与传入消息相同的目的地,但是目的地前面附加前缀(默认情况下为“/topic”}

@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 οnclick="connect()">登录</button>

<button οnclick="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.οnkeydοwn=function(){

if (event.keyCode == 13){

term.write("nr");

}

}

</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>

测试

82b98aa98d7d8088d40c0e98cff5c218.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值