org.springframework.boot
spring-boot-starter-websocket
org.webjars
sockjs-client
1.0.2
org.webjars
stomp-websocket
2.3.3
org.springframework.boot
spring-boot-starter-thymeleaf
用到websocket只需要第一个spring-boot-starter-websocket依赖就可以了,因为该项目还实现了和前台的交互,所以导入了sockjs-client和stomp-websocket依赖(如果不想在pom.xml中导入,可以直接引用/sockjs.min.js和stomp.min.js链接:http://www.bootcdn.cn/stomp.js/)
2. 后台新建WebSocketConfig.java
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
//添加服务端点,接收客户端的连接
//参数/any-socket要和前台对应一致
registry.addEndpoint("/any-socket")
//开启SockJS支持
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//服务端接收地址的前缀
registry.setApplicationDestinationPrefixes("/app");
//客户端订阅地址的前缀信息
registry.enableSimpleBroker("/topic","/user");
}
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
registration.addDecoratorFactory(new WebSocketHandlerDecoratorFactory() {
@Override
public WebSocketHandler decorate(WebSocketHandler handler) {
return new WebSocketHandlerDecorator(handler){
/* (non-Javadoc)
* 客户端与服务端建立连接后的服务端的操作
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
String username = session.getPrincipal().getName();
System.out.println("online:"+username);
super.afterConnectionEstablished(session);
}
/* (non-Javadoc)
* 客户端与服务端断开连接后服务端的操作
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus)
throws Exception {
String username = session.getPrincipal().getName();
System.out.println("offline:"+username);
super.afterConnectionClosed(session, closeStatus);
}
};
}
});
}
}
@Configuration 说明该java类是一个配置类
@EnableWebSocketMessageBroker 用于开始使用STOMP协议来传输基于MessageBroken的消息,这时controller可以使用@MessageMapping来拦截消息
registry.setApplicationDestinationPrefixes()用来设置服务器接收消息的前缀
3.聊天消息的controller
@Controller
public class ChatController {
private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Autowired
private SimpMessagingTemplate template;
/**
* 群聊模式
* @param principal 当前用户
* @param message 接收到的客户端的消息
* @return 包装后的消息
*/
@MessageMapping("/all")
@SendTo("/topic/all")
public String all(Principal principal,String message){
BaseMessage baseMessage = new BaseMessage();
baseMessage.setType("to_all");
baseMessage.setContent(message);
baseMessage.setSender(principal.getName());
System.out.println(principal.getName()+"++++++++++++++++++++++++++++++++++");
baseMessage.setSendTime(format.format(new Date()));
System.out.println(JSON.toJSONString(baseMessage));
return JSON.toJSONString(baseMessage);
}
/**
* 私聊
*
*/
@MessageMapping("/chat")//接收发送到/app/chat的消息
public void chat(Principal principal,String message){
ChatMessage chatMessage = JSON.parseObject(message,ChatMessage.class);
BaseMessage baseMessage = new BaseMessage();
baseMessage.setType("to_one");
baseMessage.setSender(principal.getName());
System.out.println(principal.getName()+"++++++++++++++++++++++++++++++++++");
baseMessage.setContent(chatMessage.getContent());
baseMessage.setSendTime(format.format(new Date()));
//转发包装后的消息至用户
template.convertAndSendToUser(chatMessage.getReceiver(),"/topic/chat",JSON.toJSONString(baseMessage));
}
}
@MessageMapping("/chat") 拦截发送到"/app/chat"的消息
@SendTo 注解重写了消息代理的目的地,如果不指定@SendTo,帧所发往的目的地会与触发处理器方法的目的地相同,只不过会添加上“/topic”前缀
3.前台js的实现
var target = "to_all";
var stompClient = null;
function connect() {
var socket = new SockJS('/any-socket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/all', function (message) {
showMessage(JSON.parse(message.body));
});
stompClient.subscribe('/user/topic/chat', function (message) {
showMessage(JSON.parse(message.body));
});
});
}
stompClient.connect(header,connectionCallback,errorCallback);第一个参数是header,包含客户端的认证信息,可如下所示;connectionCallback是表示连接成功时(服务器响应 CONNECTED 帧)的回调方法
errorCallback是连接失败时的回调方法,非必需
var headers = {
login: 'mylogin',
passcode: 'mypasscode',
// additional header
'client-id': 'my-client-id'
};
subscribe(destination url, callback[, headers]) 订阅服务器的消息
destination url 为服务器 @SendTo 匹配的 URL,字符串;
callback 为每次收到服务器推送的消息时的回调方法
showMessage为自定义消息用于在客户端展示消息
$(function () {
connect();
$("#send").click(function () {
if (target == "to_all"){
if($("#message").val()){
stompClient.send("/app/all", {}, $("#message").val());
}
}else{
var content = "{'type':'to_one','content':'" + $("#message").val() + "','receiver':'"+target+"'}";
stompClient.send("/app/chat", {}, content);
}
$("#message").val("");
});
$(".friend").click(function () {
target = $(this).find("p.f-name").text();
var username = $(this).find("h4.f-name").text();
$("#target").text(username);
$("#groupBtn").attr("class","btn btn-default");
});
$("#groupBtn").click(function(){
$(this).attr("class","btn btn-default disabled");
target = "to_all";
$("#target").text("所有人");
});
}
仅为部分代码。