这是我参与更文挑战的第3天,活动详情查看: 更文挑战前端
本文正在参加「Java主题月 - Java 开发实战」,详情查看 活动连接java
[TOC]web
上一章节咱们说了websocket的优缺点,咱们经过websocket和http的对比分析,总结出用websocket的场景。今天小编带你们经过一个案例使用下升级版的websocket。ajax
SockJs算是一个浏览器库,它提供了一个跨浏览器的api , 他在浏览器和服务端创建了一个低延迟、全双工、跨域通讯通道。spring
向ie这些浏览器可能缺乏对websocket的支持,咱们上一章节也是在谷歌浏览器下开发完成的。这里对ie这些浏览器没有作测试,可是一些低版本的浏览器的确是不支持的websocket的。 sockJs对浏览器兼容性很大。在原声的websocket基础上进行了优化。sockjs在不支持websocket的浏览器上会采用轮询的方式实现双向通讯。api
springboot 对sockjs支持性很良好。只须要在原有的websocket配置上添加已sockjs方式发布就能够了。和以前websocket章节同样咱们须要添加对sockjs的配置。只须要withSockJS就能够开启sockjs的支持。另外咱们还能够添加拦截器进行对访问的鉴权。而后咱们能够将通道发布在指定路径上跨域
@Configuration
//注解开启使用STOMP协议来传输基于代理(message broker)的消息,这时控制器支持使用@MessageMapping,就像使用@RequestMapping同样
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {//注册STOMP协议的节点(endpoint),并映射指定的url
//注册一个STOMP的endpoint,并指定使用SockJS协议
registry.addEndpoint("/endpointAric")
.setAllowedOrigins("*")
.addInterceptors(createSessionInterceptor())
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {//配置消息代理(Message Broker)
//广播式应配置一个/topic消息代理
registry.enableSimpleBroker("/topic", "/queue");
//registry.setApplicationDestinationPrefixes("/app");
//点对点使用的订阅前缀(客户端订阅路径上会体现出来),不设置的话,默认也是/user/
registry.setUserDestinationPrefix("/user/");
}
/** * 配置客户端入站通道拦截器 */
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(createUserInterceptor());
}
@Bean
public HandshakeInterceptor createSessionInterceptor(){
return new SessionAuthHandshakeInterceptor();
}
/*将客户端渠道拦截器加入spring ioc容器*/
@Bean
public UserInterceptor createUserInterceptor() {
return new UserInterceptor();
}
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
registration.setMessageSizeLimit(500 * 1024 * 1024);
registration.setSendBufferSizeLimit(1024 * 1024 * 1024);
registration.setSendTimeLimit(200000);
}
}
复制代码
registry.enableSimpleBroker("/topic", "/queue");
设置的topic,queue就是客户端和服务端的通讯通道。浏览器
本节内容咱们将经过聊天室体验下websocket的使用。springboot
上面咱们已经配置了服务端的环境,这样咱们就能够开发通道内的通讯内容。聊天室使用websocket进行通讯解决了咱们经过ajax调用的实时性。保证了消息的准确性。无延后性。服务器
sockJs的好处就是被springboot封装一层以后!咱们开发websocket通道就像咱们开发接口同样方便。咱们只须要经过messageMapping
注解就能够完成咱们数据的接收。剩下的客户端的工做仍是以前的一套工做逻辑只须要响应的使用sockjs就能够进行通讯了。
/*点对点通讯*/
@MessageMapping(value = "/sendToUser")
public void templateTest1(@Payload String message, @Header("userId") String userId,
@Headers Map<String, Object> headers) {
int i = 1;
for (SimpUser user : userRegistry.getUsers()) {
System.out.println("用户" + i++ + "---" + user);
}
CustomUser userInfo = (CustomUser) ((Map)(headers.get("simpSessionAttributes"))).get(CoreConstants.USERINFO);
String fromUserId = String.valueOf(userInfo.getUserId());
//发送消息给指定用户
messagingTemplate.convertAndSendToUser(userId, "/queue/message",new AricResponse(fromUserId,userId,message));
if (!fromUserId.equals(userId)) {
//给本身发送一条,消息同步
messagingTemplate.convertAndSendToUser(fromUserId, "/queue/message",new AricResponse(fromUserId,userId,message));
}
//消息新增
messageService.insertBackMessage(fromUserId,userId,message);
}
复制代码
@MessageMapping(value = "sendToUser")
该注解实现了接受客户端的请求。对应客户端能够经过 stompClient.send("/sendToUser", {'userId': userId},content);
进行发送至服务端,userId是客户端用户发送给指定用户的id, content是发送的内容 服务端经过@Payload和@Header注解接受前端传送的信息。上述代码中获取发送者用户id的地方为何那么写呢。下面咱们看看客户端链接的方式
// 创建链接对象(还未发起链接)
socket = new SockJS(host+"/endpointAric");
// 获取 STOMP 子协议的客户端对象
stompClient = Stomp.over(socket);
// 向服务器发起websocket链接并发送CONNECT帧
stompClient.connect(
{
userId: currentUser.userId // 携带客户端信息
},
function connectCallback(frame) {
// 链接成功时(服务器响应 CONNECTED 帧)的回调方法
subscribe();
console.log("链接成功");
},
function errorCallBack(error) {
// 链接失败时(服务器响应 ERROR 帧)的回调方法
console.log("链接失败"+error);
if (errorTimes < 10) {
errorTimes++;
setTimeout("connect();",8000);
}
}
);
复制代码
在链接的时候客户端会将当前用户的id传递进来,这里也解释了为何服务端那样获取用户信息。而后就经过/queue/message
发送给指定的用户。由于咱们在配置websocket的时候指定了 registry.setUserDestinationPrefix("/user/");
,因此服务端发送给/queue/message
了,客户端订阅的时候须要加上user 即 /user/queue/message
function subscribe() {
stompClient.subscribe('/user/queue/message', function (response) {
var returnData = JSON.parse(response.body);
if (returnData.fromUserId == returnData.toUserId) {
//本身发送的消息须要本身渲染到聊天框中
setMessageInnerHTML(currentUser.userId, returnData, 0);
} else if (returnData.fromUserId == currentUser.userId) {
//本身发送信息给别,本身收到的信息
setMessageInnerHTML(returnData.toUserId, returnData, 1);
} else {
//别人发送的信息
setMessageInnerHTML(returnData.fromUserId, returnData, 0);
}
});
}
复制代码
/**
* 多房间聊天室
* @param chatRoomId
* @param message
* @return
*/
@MessageMapping("/welcome/{chatRoomId}") //当浏览器向服务端发送请求时,经过@MessageMapping映射/welcome这个地址,相似于@ResponseMapping
@SendTo("/topic/getResponse/{chatRoomId}")//当服务器有消息时,会对订阅了@SendTo中的路径的浏览器发送消息
public AricResponse say(@Header("userId") String userId,@DestinationVariable("chatRoomId") String chatRoomId, AricMessage message) {
try {
//睡眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new AricResponse(userId,null,"welcome," + message.getName()+chatRoomId + "!");
}
复制代码
群聊相对与单聊简单不少,咱们发送信息的时候只须要发送到房间通道内就行。而在次群聊内的人只须要订阅房号的信息就好了。这里代码不在赘述。有疑问的小伙伴能够经过下方加入战队找到我。
sockjs在websocket基础上进行各个浏览器的兼容,让咱们的开发变得友好起来。 若是你使用Java作服务端,同时又刚好使用Spring Framework做为框架,那么推荐使用SockJS,由于Spring Framework自己就是SockJS推荐的Java Server实现,同时也提供了Java 的client实现。
若是你使用Node.js作服务端,那么毫无疑问你该选择Socket.IO,它本省就是从Node.js开始的,固然服务端也提供了engine.io-server-java实现。甚至你可使用