一,Websocket是一个基于TCP、对传统HTTP协议(短连接)的升级版,它实现了浏览器与服务器的全双工通信,扩展了浏览器与服务端的通信功能,使服务端也能主动向客户端发送数据。
解决的问题:
1.解决了多次握手的问题(长连接),提高效率
2.服务器可以推送数据给客户端,不需要客户端轮询等low操作
客户端实现方式
JavaScript对WebSocket的支持:
2.1.创建客户端连接的方式:
websocket = new WebSocket(“ws://localhost:9090/websocket”);
2.2.websocket对象常用事件:
onerror: 连接到服务端错误时触发
onmessage: 收到服务器推送的消息时触发
onclose: 连接关闭时触发
onopen: 连接到服务端成功后触发
二.WebSocket示例(实现简单的信息交互)
2.1.新建WebSocket示例(网页版QQ聊天实现)
在pom.xml中添加Jar包依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.version}</version>
</dependency>
2.2.spring整合websocket方法
spring整合,此方式基于spring mvc框架
WebSocketConfig.java
这个类是配置类,所以需要在spring mvc配置文件中加入对这个类的扫描,第一个addHandler是对正常连接的配置,第二个是如果浏览器不支持websocket,使用socketjs模拟websocket的连接。
package com.ssm.websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
/*
@Configuration的作用
声明当前类是一个配置类
@Configuration不可以是final类型;
@Configuration不可以是匿名类;
嵌套的configuration必须是静态类。
@EnableWebSocket 声明该类支持WebSocket
*/
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
//websocket入口,允许访问的域、注册Handler、SockJs支持和拦截器。
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(),"/websocket")//入口路径为websocket
.addInterceptors(new SpringWebSocketHandler())
.setAllowedOrigins("*");
registry.addHandler(myHandler(), "/wbsockjs/webSocketServer")
.addInterceptors(new SpringWebSocketHandler()).withSockJS();
//setAllowedOrigins()方法的支持spring4.1.5之后的版本才支持
//setAllowedOrigins(String... val),允许指定的域名或IP(含端口号)建立长连接,
// 可以只设置允许自家域名访问,如果不限时使用"*"号,如果指定了域名,
// 则必须要以http或https开头。
}
@Bean
public WebSocketHandler myHandler(){
return new MySocketHandler();
}
@Bean
public HttpSessionHandshakeInterceptor myHandlerInterceptor(){
return new SpringWebSocketHandler();
}
}
SpringWebSocketHandler.java
这个类的作用就是在连接成功前和成功后增加一些额外的功能,Constants.java类是一个工具类,两个常量。
package com.ssm.websocket;
import com.lingdian.pojo.User;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.*;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
import javax.servlet.http.HttpSession;
import java.util.Map;
/*
握手拦截器类
*/
public class SpringWebSocketHandler extends HttpSessionHandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request,
ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
System.out.println("Before Handshake");
//在握手之前将HttpSession中的用户,copy放到WebSocket Session中
if (request instanceof ServletServerHttpRequest){
ServletServerHttpRequest servletServerHttpRequest=
(ServletServerHttpRequest) request;
HttpSession session=
servletServerHttpRequest.getServletRequest().getSession(true);
if (null!=session){
User user=(User)session.getAttribute("user");
//WebSocket Session
attributes.put("user",user);
}
}
return super.beforeHandshake(request,response,wsHandler,attributes);
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response
, WebSocketHandler wsHandler, Exception ex) {
super.afterHandshake(request, response, wsHandler, ex);
}
}
MySocketHandler .java
这个类是对消息的一些处理,比如是发给一个人,还是发给所有人,并且前端连接时触发的一些动作
package com.ssm.websocket;
import com.lingdian.pojo.Message;
import com.lingdian.pojo.User;
import com.lingdian.service.MessageService;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CopyOnWriteArraySet;
/***
* 消息处理类
*/
public class MySocketHandler extends TextWebSocketHandler {
//使用CopyOnWriteArraySet,保证线程安全,当一个用户
// 退出时,这边的用户查看用户列表时不会出现安全失败
private static CopyOnWriteArraySet<WebSocketSession> users=new CopyOnWriteArraySet<WebSocketSession>();;
@Resource
private MessageService messageService;
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
//super.handleTextMessage(session, message);
SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = sd.format(new Date());
//从 WebSocket Session中取得用户
User user=(User) session.getAttributes().get("user");
Message message1=new Message();
message1.setUid(user.getId());
message1.setMessage(message.getPayload());
message1.setSendTime(new Date());
//将消息保存到数据库
messageService.insertMessage(message1);
//封装要输出的消息到TextMessage,姓名,时间,消息
TextMessage returnMessage =
new TextMessage(user.getName()+":" +
""+time+"<br/>"+message.getPayload());
for (WebSocketSession session1 : users) {
try{
//使用sendMessage()方法输出消息到客户端
session1.sendMessage(returnMessage);
}catch (Exception e){
e.printStackTrace();
continue;
}
}
}
/**
* 加入一个用户 add进集合
* @param session
* @throws Exception
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
users.add(session);
System.out.println("connect to the websocket success......当前数量:"+users.size());
}
/**
* 当用户退出时,将用户从集合remove
* @param session
* @param status
* @throws Exception
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
users.remove(session);
System.out.println("剩余在线用户"+users.size());
}
public static CopyOnWriteArraySet<WebSocketSession> getUsers() {
return users;
}
public static void setUsers(CopyOnWriteArraySet<WebSocketSession> users) {
MySocketHandler.users = users;
}
public MessageService getMessageService() {
return messageService;
}
public void setMessageService(MessageService messageService) {
this.messageService = messageService;
}
}
spring-mvc.xml
正常的配置文件,同时需要增加对WebSocketConfig.java类的扫描,并且增加
<!--配置握手拦截器-->
<websocket:handlers>
<!--path=websocket-->
<websocket:mapping path="/websocket" handler="websocket"/>
<websocket:handshake-interceptors>
<bean class="com.ssm.websocket.SpringWebSocketHandler"/>
</websocket:handshake-interceptors>
</websocket:handlers>
jsp简单聊天界面
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<html>
<head>
<title>聊天室</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/Css/chat.css" />
<script src="${pageContext.request.contextPath}/Js/jquery-1.8.2.min.js"></script>
<script src="${pageContext.request.contextPath}/Js/chat.js"></script>
<script type="text/javascript">
//获取当前用户
function getUser(){
$("#chat-user-con ul").html("");
$.post("${pageContext.request.contextPath}/user/getAll",{},
function(data){
var temp;
for(temp=0;temp<data.length;temp++){
$("#chat-user-con ul").append("<li>"+data[temp].name+"</li>");
}
},"json");
}
//下线
function downLine(){
$.post("${pageContext.request.contextPath}/user/downLine",{},
function(){});
}
</script>
</head>
<body>
<span id="message"></span>
<div id="chat">
<div id="chat-top">
<div id="chat-dialog">
<div id="chat-dialog-t">聊天室</div>
<div id="chat-dialog-con">
<ul>
</ul>
</div>
</div>
<div id="chat-user">
<div id="chat-user-t">当前在线用户</div>
<div id="chat-user-con">
<ul>
</ul>
</div>
</div>
</div>
<div id="chat-bottom">
<div id="chat-input">
<div id="chat-input-expr">
<!--<img src="Images/1.gif" id="1" /><img src="Images/2.gif" id="2" /><img src="Images/3.gif" id="3" /><img src="Images/4.gif" id="4" /><img src="Images/5.gif" id="5" /><img src="Images/6.gif" id="6" /><img src="Images/7.gif" id="7" /><img src="Images/8.gif" id="8" /><img src="Images/9.gif" id="9" /><img src="Images/10.gif" id="10" />-->
</div>
<div id="chat-input-edit">
<div id="input-field">
<textarea id="txtInput"></textarea>
</div>
<div id="input-btn">
<input id="btnSend" type="button" value="发送" />
</div>
</div>
<div id="chat-input-tip">发送内容不能为空</div>
</div>
</div>
</div>
<div id="chat-msg"></div>
</body>
</html>
List item
注意导入wbsockjs时要使用地址全称,并且连接使用的是http而不是websocket的ws
声明: 整理本文仅供参考,若有疑问可以同时参考原文。