websocket与http协议
HTTP 协议有一个缺陷:通信只能由客户端发起。只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。
WebSocket最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种
1,pom.xml依赖注入
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
2,yml配置
server:
port: 9900
spring:
application:
name: mywebsk
3,模拟两个客户端
package com.example.mywebsk.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 监控用户页面
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 注册两个客户端资源名和路径 代表张三和李四分别登录了聊天页面
* @param registry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/zs").setViewName("zs");
registry.addViewController("/ls").setViewName("ls");
}
}
4,WebSocket核心
package com.example.mywebsk.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* WebSocket核心 存放在spring容器中
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
5,service层
package com.example.mywebsk.services;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* websocket服务器
*/
@Component
@ServerEndpoint("/socket/{name}")
public class WebSocketServer {
/**
* 原子类 在多线程情况 原子类会自动上锁 记录在线人数
*/
private static AtomicInteger online = new AtomicInteger();
/**
* 存放每个客户端对当前服务器WebSocket会话对象
*/
private static Map<String, Session> sessionPools = new HashMap<String,Session>();
/**
* 从服务器给某个session发送数据 此方法名(推荐)
* @param session
* @param msg
* @throws Exception
*/
public void sendMessage(Session session,String msg) throws Exception{
if (session != null){
session.getBasicRemote().sendText(msg);
}
}
/**
* 用户和服务器建立连接
* @param session
* @param username
*/
@OnOpen
public void onOpen(Session session, @PathParam(value = "name") String username){
//向session集合中添加一个用户session
sessionPools.put(username,session);
//在线人数加1
addOnline();
System.out.println(username+",加入服务器!当前在线人数:"+online);
try {
//发送一段信息给登录的用户
sendMessage(session,username+"欢迎加入聊天室");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 用户退出关闭连接
* @param username
*/
@OnClose
public void onClose(@PathParam("name")String username){
//按照用户的姓名移出session
sessionPools.remove(username);
//在线人数-1
subOnline();
System.out.println(username+"断开服务器连接!当前人数:"+online);
}
/**
* 向所有的session对象发送消息 群发
* @param msg
* @throws Exception
*/
@OnMessage
public void onMessage(String msg){
for (Session session:sessionPools.values()){
try {
sendMessage(session,msg);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 某session发生错误 出异常
* @param session
* @param throwable
*/
@OnError
public void onError(Session session,Throwable throwable){
System.out.println("发生错误");
throwable.printStackTrace();
}
/**
* 指定向某人发消息
* @param name
* @param msg
*/
public void sendInfo(String name, String msg){
Session session = sessionPools.get(name);
try {
sendMessage(session,msg);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 对在线人数的操作 增加
*/
public static void addOnline(){
online.incrementAndGet();
}
/**
* 对在线人数的操作 减少
*/
public static void subOnline(){
online.decrementAndGet();
}
}
6,controller层
package com.example.mywebsk.controller;
import com.example.mywebsk.services.WebSocketServer;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class AfterCtrl {
@Resource
private WebSocketServer webSocketServer;
//服务器给某人发送信息
@RequestMapping(value = "/socket",method = RequestMethod.GET)
public void singleSocket(@RequestParam("name")String name,@RequestParam("msg")String msg){
webSocketServer.sendInfo(name,msg);
}
//服务器群发
@RequestMapping(value = "/socket/all",method = RequestMethod.GET)
public void collectiveSocket(@RequestParam("msg")String msg){
webSocketServer.onMessage(msg);
}
}
7,页面1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>李四的聊天页面</title>
</head>
<body>
<!-- input[id='text' type='text']+button{发送}+button{关闭}+br+div[id='message']-->
<input type="text" id="text">
<button onclick="collective()">发送</button>
<button onclick="exitTalk()">关闭</button>
<br>
<div id="message"></div>
<script type="text/javascript">
var webSocket = null;
if ('WebSocket' in window){
webSocket = new WebSocket("ws://localhost:9900/socket/ls")
}else {
alert("当前浏览器不支持html5的webSocket")
}
//在div中写信息的方法
function setMsg(msg){
document.getElementById("message").innerHTML += msg +"<br/>"
}
//前端webSocket错误处理
webSocket.onerror = function (){
setMsg("连接服务器错误");
}
//开启websocket服务器连接时获取后端服务器返回的数据
webSocket.onopen=function (event){
setMsg("登录");
}
// 接受服务器群发或单发的信息
webSocket.onmessage=function (event){
setMsg(event.data);
}
//服务器关闭后显示的信息
webSocket.onclose=function (){
setMsg("bye-bye");
}
//用户直接关闭浏览器
window.onbeforeunload=function (){
webSocket.close();
}
//用户发送信息到群里
function collective(){
//获取文本框的值
var msg = document.getElementById("text").value;
//发送服务器
webSocket.send(msg);
}
//用户点击按钮退出聊天室
function exitTalk(){
webSocket.close();
}
</script>
</body>
</html>
8,页面2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>张三的聊天页面</title>
</head>
<body>
<!-- input[id='text' type='text']+button{发送}+button{关闭}+br+div[id='message']-->
<input type="text" id="text">
<button onclick="collective()">发送</button>
<button onclick="exitTalk()">关闭</button>
<br>
<div id="message"></div>
<script type="text/javascript">
var webSocket = null;
if ('WebSocket' in window){
webSocket = new WebSocket("ws://localhost:9900/socket/zs")
}else {
alert("当前浏览器不支持html5的webSocket")
}
//在div中写信息的方法
function setMsg(msg){
document.getElementById("message").innerHTML += msg +"<br/>"
}
//前端webSocket错误处理
webSocket.onerror = function (){
setMsg("连接服务器错误");
}
//开启websocket服务器连接时获取后端服务器返回的数据
webSocket.onopen=function (event){
setMsg("登录");
}
// 接受服务器群发或单发的信息
webSocket.onmessage=function (event){
setMsg(event.data);
}
//服务器关闭后显示的信息
webSocket.onclose=function (){
setMsg("bye-bye");
}
//用户直接关闭浏览器
window.onbeforeunload=function (){
webSocket.close();
}
//用户发送信息到群里
function collective(){
//获取文本框的值
var msg = document.getElementById("text").value;
//发送服务器
webSocket.send(msg);
}
//用户点击按钮退出聊天室
function exitTalk(){
webSocket.close();
}
</script>
</body>
</html>