使用WebSocket实现聊天室功能

1、搭建项目框架,添加依赖

1、添加相关依赖


org.springframework.boot
spring-boot-starter-websocket
1.3.5.RELEASE

1)、编写通信握手拦截器

/**
* WebSocket 通信握手拦截器
* 它只拦截握手
*/
@Component
public class SpringBootHandshakeInterceptor implements HandshakeInterceptor{
/**
* 握手之前
* ServerHttpRequest : 请求对象
* ServerHttpResponse : 响应对象
* WebSocketHandler : WebSocket 服务处理类,在这里指的是 SpringBootWebSocketHandler * attributes : WebSocketSession.getAttributes() */
@Override
public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse
serverHttpResponse, WebSocketHandler wsHandler,Map attributes) throws Exception {
ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) serverHttpRequest;
//得到 Http 协议的请求对象
HttpServletRequest request = servletServerHttpRequest.getServletRequest();
HttpSession session = request.getSession(true);
System.out.println("握手拦截器^^^^^^^
request.getParameter(\"param\")=>"+request.getParameter("param"));
System.out.println("握手拦截器^^^^^^^^^^^^
request.getParameter(\"token\")=>"+request.getParameter("token"));
System.out.println("握手拦截器^^^^^^^^^^^^ HttpSession.getAttribute(\"user\")=>"+session.getAttribute("user"));
//数据中转 可以把 http 协议的会话对象数据中转到 ws 协议的会话对象中
attributes.put("param", request.getParameter("param"));
//非前后端分离架构:把 HttpSession 中的数据中转到 WebSocketSession 中
if(session.getAttribute("user") != null)attributes.put("user", session.getAttribute("user"));
//如果是前后端分离架构:把 Http 协议中的 token 令牌中转到 ws 协议的 WebSocketSession 中
attributes.put("token", request.getParameter("token"));
return true;
}
/**
* 握手之后
*/
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler
wsHandler,Exception exception) {
System.out.println(“握手之后”);
}
}

2)、编写 WebSocket 服务配置类

/**
* WebSocket 服务配置类
*
*/
@Configuration //单例模式 bean
@EnableWebSocket //启动 WebSocket 服务器
public class SpringBootWebSocketConfigurer implements WebMvcConfigurer , WebSocketConfigurer{
@Autowired
private SpringBootWebSocketHandler handler;
@Autowired
private SpringBootHandshakeInterceptor handshakeInterceptor;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
//-------------------- 允许跨域访问 WebSocket ------------------------

String[] allowsOrigins = {"*"};//允许连接的域,只能以 http 或 https 开头
/**
* http://localhost:8080
* http://localhost:8080/index.html */
//7. 设置 websocket 服务器地址 ws://localhost:8080/SpringBootWebSocket
registry.addHandler(handler, "/SpringBootWebSocket").addInterceptors(handshakeInterceptor).setAllowedOrigins(allowsOrigins);
}
}

3)、自定义实现 WebSocket 服务处理类

/**
    * WebSocket 服务处理类
    */
    @Component
    public class SpringBootWebSocketHandler implements WebSocketHandler{
    //存储所有客户端的会话 WebSocketSession,key 使用客户端的唯一标识方便存取
    private static Map allWebSocketSession = new HashMap();
    /**
    * 1. 客户端成功建立连接时触发
    */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    System.out.println("客户端成功建立连接=>"+session.getId());
    //在 ws 服务类中任何位置都可以使用以下取值
    System.out.println("WebSocketSession.getAttributes().get(\"param\")"+session.getAttributes().get("param"));
    System.out.println("WebSocketSession.getAttributes().get(\"user\")"+session.getAttributes().get("user"));
    System.out.println("WebSocketSession.getAttributes().get(\"token\")"+session.getAttributes().get("token"));
    //存储所有客户端的 WebSocketSession
    allWebSocketSession.put((String)session.getAttributes().get("param"), session);
    }
    /**
    * 2. 接收客户端数据时触发
    */
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage message) throws Exception {
    System.out.println("接收客户端数据=>"+message.getPayload().toString());
    //给客户端回一句话意思意思,随便给客户端发数据
    this.send(session, "&&&&&&服务器回复的&&&&&&"+message.getPayload().toString());
    }
    /**
    * 3. 通信异常时触发
    */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
    System.out.println("通信异常=>"+session.getId());
    }
    /**
    * 4.客户端关闭连接时触发
    */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
    System.out.println("客户端关闭连接=>"+session.getId());
    //移除通行关闭的客户端
    allWebSocketSession.remove((String)session.getAttributes().get("param"));
    }
    /**
    * 是否支持分段传输
    */
    @Override
    public boolean supportsPartialMessages() {
    return false; //一次输出完毕
    }
    /**
    * 5.服务器主动发送数据
    * @param webSocketSession
    * @param msg
    */
    public void send(WebSocketSession webSocketSession,String msg) {
    try {
    webSocketSession.sendMessage(new TextMessage(msg));
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    /**
    * 6. 服务器主动关闭通信
    * @param webSocketSession
    * @param msg
    */
    public void close(WebSocketSession webSocketSession,String msg) {
    try {
    webSocketSession.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }

2)、自定义服务器端点,创建 ChatRoomServerEndpoint 类。代码如下:

/**
* 聊天室服务端
* 标注为端点:@ServerEndpoint,其中"/chat-room/{username}"为访问路径
*/
@ServerEndpoint("/chat-room/{username}")
public class ChatRoomServerEndpoint {
/**
* 存储所有存活的用户
* *我们需要周期性的去检查用户是否还处于活跃状态,不活跃的,移除该用户的 session
*/
private static Map livingSessions = new ConcurrentHashMap<>();
/**
* 前端一旦启用 WebSocket,就会调用@OnOpen 注解标注的方法
* @param username 路径参数 *
* @param session 会话,每个访问对象都会有一个单独的会话
*/
@OnOpen
public void openSession(@PathParam("username") String username, Session session){
livingSessions.put(session.getId(), session);
sendTextAll("欢迎用户【" + username +"】来到聊天室!");
}
/**
* 服务端发送消息给前端时调用 *
* @param username 路径参数 *
* @param session 会话,每个访问对象都会有一个单独的会话
* @param message 待发送的消息
*/
@OnMessage
public void onMessage(@PathParam("username") String username, Session session, String message){
sendTextAll("用户【" + username + "】:" + message);
}
/** *
* 客户端关闭 WebSocket 连接时,调用标注@OnClose 的方法 *
* @param username 路径参数
* @param session 会话,每个访问对象都会有一个单独的会话
* */
@OnClose
public void onClose(@PathParam("username") String username, Session session){
//将当前用户移除
livingSessions.remove(session.getId());
//给所有存活的用户发送消息
sendTextAll("用户【" + username +"】离开聊天室!");
}
/** *
* 向指定 Session(用户)发送 message * */
private void sendText(Session session, String message){
//发送消息对象
RemoteEndpoint.Basic basicRemote = session.getBasicRemote();
try {
//发送消息
basicRemote.sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
/** *
* 遍历所有存活的用户,并发送消息(PS:就是广播消息)
* */
private void sendTextAll(String message){
Iterator sids=livingSessions.keySet().iterator();
while(sids.hasNext()) {
String sid=sids.next();
Session session=livingSessions.get(sid);
this.sendText(session, message);
}
}
}

3)、改造启动接口,激活 WebSocket,并注册WebSocket 相关类和端点,参考代码如下:

/**
* * 激活 WebSocket:@EnableWebSocket */
@SpringBootApplication
@EnableWebSocket
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
/**
* * 注册 ServerEndpointExporter Bean 对象(因为 Springboot 没有自动注册,所以得手动注册)
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
/**
* * 注册 端点对象
*/
@Bean
public ChatRoomServerEndpoint chatRoomServerEndpoint() {
return new ChatRoomServerEndpoint();
}
}

4)、前端 chat.html 页面,参考代码如下:





聊天室





聊天消息内容:




用户:




输入框:



需要在引入两个js文件

使用WebSocket实现聊天室功能_第1张图片

你可能感兴趣的:(websocket,java,spring,boot)