WebSocket是什么?
Websocket是html5开始提供的一种在单个tcp连接上进行全双工通讯的协议,属于应用层协议.
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。
当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。
使用springboot整合websocket
需要javaee 7 版本及以上(对websocket的支持)
1.导入websocket maven坐标
<dependencies>
<!-- web启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- websocket坐标 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>lombok</groupId>
<artifactId>lombok</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
2.注入所需依赖
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
3.编写websocket服务
@ServerEndpoint("/websocket/{sid}")
@Service
@Slf4j
public class WebSocketServer {
//记录当前在线连接数
private static AtomicInteger onlineCount = new AtomicInteger(0);
//创建集合,存放每个客户端对应的websocket对象
private static CopyOnWriteArraySet<WebSocketServer> webSocketServers = new CopyOnWriteArraySet<>();
//与某个客户端的连接会话,需要通过他给客户端发送数据
private Session session;
//接收id
private String sid = "";
/**
* 连接成功调用的方法
*/
@OnOpen
public void open(Session session,EndpointConfig config, @PathParam("sid") String sid) throws IOException {
this.session = session;
webSocketServers.add(this);
addOnlineCount();
log.info("有新窗口开始监听,sid="+sid+",当前在线人数为:"+onlineCount.get());
this.sid = sid;
sendInfo("连接成功,当前人数为:"+onlineCount.get(),sid);
}
/**
* 连接关闭时调用的方法
* @throws IOException
*/
@OnClose
public void close(Session session,CloseReason closeReason) throws IOException {
webSocketServers.remove(this);
subOnlineCount();
for (WebSocketServer item : webSocketServers) {
item.session.getBasicRemote().sendText(this.sid+"窗口下线了,当前人数为:"+onlineCount.get());
}
}
/**
* 接收到来自客户端消息的方法
* @param message
* @param session
* @throws IOException
*/
@OnMessage
public void onMessage(String message,Session session) throws IOException {
log.info("收到来自"+sid+"窗口的信息:"+message);
for (WebSocketServer item : webSocketServers) {
item.sendMessage(message);
}
}
/**
* websocket出错时调用的方法
* @param session
* @param throwable
*/
@OnError
public void onError(Session session,Throwable throwable){
log.error("发生错误");
throwable.printStackTrace();
}
/** 添加在线人数*/
public void addOnlineCount(){
onlineCount.addAndGet(1);
}
/** 减少在线人数*/
public void subOnlineCount(){
onlineCount.decrementAndGet();
}
/** 服务端主动推送消息*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/** 遍历websocketServer列表,全体推送*/
public static void sendInfo(String message,@PathParam("sid") String sid) throws IOException {
log.info("推送消息到窗口"+sid+",推送内容:"+message);
for (WebSocketServer item : webSocketServers) {
item.sendMessage(message);
}
}
}
参数说明 :
注解名 | 方法参数 | 说明 |
---|---|---|
@ServerEndpoint("/uri") | / | 类注解,标注在类上,标明此类为websocket的服务类,uri为请求到此websocket服务的路径 |
@OnOpen | Session,EndpointConfig | 当客户端webSocket连接到服务端成功时会调用此注解标注的方法 |
@onClose | Session,CloseReason | 当客户端有websocket关闭时,会调用此注解标注的方法 |
@onMessage | Session,String message | 当客户端向websocket发送消息时会调用此注解标注的方法 |
@onError | Sessionsession,Throwable | 当socket发生错误时会调用此注解标注的方法 |
至此,服务端代码编写完成.
3.编写客户端代码进行测试
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
消息区: <br>
<div id="box">
</div>
<hr>
<input type="text" id="message">
<button id="send">发送</button>
<script src="jquery.js"></script>
<script type="text/javascript">
var socket;
if (typeof (WebSocket) == 'undefined') {
alert('您的浏览器暂不支持webSocket')
} else {
console.log('连接websocket');
socket = new WebSocket("ws://localhost:8081/websocket/1");
//socket连接
socket.onopen = () => {
console.log("websocket已打开")
};
//获得消息事件
socket.onmessage = (msg)=>{
console.log(`接收到消息:${JSON.stringify(msg.data)}`);
$("#box").append(`<div><span>${msg.data}</span></div>`)
};
//关闭事件
socket.onclose = () => {
};
socket.onerror = () => {
console.log("socket发生错误")
};
//发送消息
$('#send').click(() => {
socket.send($('#message').val());
console.log('发送成功')
})
}
</script>
</body>
</html>
展示效果:
控制台打印:
至此,整合基本完成,