這是我參與更文挑戰的第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實現。甚至你可使用