Spring boot+web Socket即時通訊
本文章使用websocket進行通訊,傳輸協議使用的時websocket子協議Stomp。
1、添加jar文件
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> <version>1.3.5.RELEASE</version> </dependency>
2、在spring啟動文件中,如果要調用資料庫方法需要在啟動文件中添加
ConfigurableApplicationContext a=SpringApplication.run(sApplication.class, args);NewsController.setApplicationContext(a);
3、webscoket啟動鏈接
@Configuratio@EnableWebSocketMessageBrokerpublic class WebSocketStompConfig extends AbstractWebSocketMessageBrokerConfigurer{ /** * 註冊stomp的端點 * 設置端點,客戶端通過端點和伺服器進行websocket連接 * */ @Override public void registerStompEndpoints(StompEndpointRegistry registry) { // 允許使用socketJs方式訪問,訪問點為webSocketServer,允許跨域 // 在網頁上我們就可以通過這個鏈接 // http://localhost:8080/webSocketServer // 來和伺服器的WebSocket連接 registry.addEndpoint("/webSocketServer").setAllowedOrigins("*").withSockJS(); } /** * 配置信息代理 * /topic 代表發布廣播,即群發 * /queue 代表點對點,即髮指定用戶 * /app 設置客戶端請求前綴 */ @Override public void configureMessageBroker(MessageBrokerRegistry registry) { // 訂閱Broker名稱 registry.enableSimpleBroker("/topic"); // 全局使用的消息前綴(客戶端訂閱路徑上會體現出來) registry.setApplicationDestinationPrefixes("/app"); // 點對點使用的訂閱前綴(客戶端訂閱路徑上會體現出來),不設置的話,默認也是/user/ // registry.setUserDestinationPrefix("/user/"); } }
4、配置一個scoket的bean
@Componentpublic class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); }}
5、具體實現類
@Component@ServerEndpoint("/webSocket")@Slf4jpublic class NewsController { // 靜態變數,用來記錄當前在線連接數。應該把它設計成線程安全的。 private static int onlineCount = 0; // concurrent包的線程安全Set,用來存放每個客戶端對應的MyWebSocket對象。 private static CopyOnWriteArraySet<NewsController> webSockets = new CopyOnWriteArraySet<>(); private Session session; // websocket注入當前ben不然無法調用資料庫 private static ApplicationContext a; @Autowired private NewsService newsservice; // 實現這個ben ,必寫 public static void setApplicationContext(ApplicationContext a1) { a = a1; } private static int id; /** * 連接成功查詢信息 * * @param session */ @RequestMapping(value = "/getIMessages") @OnOpen public void onOpen(Session session) { this.session = session; webSockets.add(this); newsservice = a.getBean(NewsService.class); List<IMessage> im = newsservice.getIMessages(Integer.valueOf(session.getQueryString()), 1); // 轉換成為json格式,傳回到前台 sutes s1 = new sutes(); for (IMessage iMessage : im) { Map<String, Object> hm = new HashMap<String, Object>(); hm.put("username", iMessage.getUsername()); hm.put("avatar", iMessage.getAvatar()); hm.put("id", iMessage.getIfrom()); hm.put("type", iMessage.getType()); hm.put("content", iMessage.getContent()); if (iMessage.getMine() != 0) { hm.put("mine", false); } else { hm.put("mine", true); } hm.put("fromid", iMessage.getIfrom()); hm.put("timestamp", iMessage.getTimes()); try { s1.setInformation(JSON.toJSONString(hm)); this.sendMessage(JSON.toJSONString(s1)); } catch (IOException e) { // TODO 自動生成的 catch 塊 e.printStackTrace(); } } try { // 上線 newsservice.updatestuse(0, this.session.getQueryString()); String s = this.session.getQueryString() + ",online"; s1.setState(s); sendInfo(null, JSON.toJSONString(s1)); } catch (IOException e) { // TODO 自動生成的 catch 塊 e.printStackTrace(); } addOnlineCount(); System.out.println("連接成功查詢信息"); } /** * 連接關閉調用的方法 */ @OnClose public void onClose() { webSockets.remove(this); // 從set中刪除 subOnlineCount(); // 在線數減 newsservice.updatestuse(1, this.session.getQueryString()); String s = this.session.getQueryString() + ",offline"; sutes s1 = new sutes(); s1.setState(s); try { sendInfo(null, JSON.toJSONString(s1)); } catch (IOException e) { // TODO 自動生成的 catch 塊 e.printStackTrace(); } System.out.println("有一連接關閉!當前在線人數為" + getOnlineCount()); } /** * 發生錯誤時調用 * * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { System.out.println("發生錯誤"); error.printStackTrace(); } /** * 接受客戶端發送過來的消息並返回 * * @param s */ @RequestMapping(value = "/saveImessage") @OnMessage public void onMessage(@RequestParam("res") String s) { String type = ""; int id = 0; IMessage iMessage = new IMessage(); // 轉換成為json格式:下面是獲取前台傳來的消息,可以用大家自己的代碼代替。 JSONObject js = JSONObject.parseObject(s); for (Map.Entry<String, Object> entry : js.entrySet()) { if (entry.getKey() == "mine") { String ll = entry.getValue().toString(); JSONObject js1 = JSONObject.parseObject(ll); for (Map.Entry<String, Object> entry1 : js1.entrySet()) { if (entry1.getKey().equals("content")) { iMessage.setContent((String) entry1.getValue()); } else if (entry1.getKey().equals("id")) { iMessage.setIfrom((Integer) entry1.getValue()); } else if (entry1.getKey().equals("username")) { iMessage.setUsername((String) entry1.getValue()); } else if (entry1.getKey().equals("mine")) { if (entry1.getValue().equals(true)) { iMessage.setMine(0); } iMessage.setMine(1); } } } if (entry.getKey() == "to") { String ll = entry.getValue().toString(); JSONObject js1 = JSONObject.parseObject(ll); for (Map.Entry<String, Object> entry1 : js1.entrySet()) { if (entry1.getKey().equals("type")) { type = (String) entry1.getValue(); iMessage.setType((String) entry1.getValue()); } else if (entry1.getKey().equals("id")) { id = (Integer) entry1.getValue(); iMessage.setUid((Integer) entry1.getValue()); } else if (entry1.getKey().equals("name")) { iMessage.setName((String) entry1.getValue()); } else if (entry1.getKey().equals("avatar")) { iMessage.setAvatar((String) entry1.getValue()); } } } } iMessage.setTimes(new Date()); iMessage.setUnreadnumbers(null); if (type.equals("friend")) { // 單聊 for (NewsController item : webSockets) { if (item.session.getQueryString().equals(String.valueOf(id))) { iMessage.setUnreadpoint(0); break; } else { iMessage.setUnreadpoint(1); } } } else { // 群聊 iMessage.setUnreadpoint(0); } newsservice = a.getBean(NewsService.class); newsservice.saveImessage(iMessage); OpenSendMapper(iMessage); System.out.println("接收方法------【websocket消息】收到客戶端發來的消息:{}" + s); } /** * 個人消息傳遞 * * @param iMessage */ public void OpenSendMapper(IMessage iMessage) { // 轉換成為json格式 sutes st = new sutes(); Map<String, Object> hm = new HashMap<String, Object>(); if (iMessage.getType().equals("friend")) { hm.put("id", iMessage.getIfrom()); } else { hm.put("id", iMessage.getUid()); id = iMessage.getUid(); } hm.put("avatar", iMessage.getAvatar()); hm.put("username", iMessage.getUsername()); hm.put("type", iMessage.getType()); hm.put("content", iMessage.getContent()); if (iMessage.getMine() != 0) { hm.put("mine", false); } else { hm.put("mine", true); } hm.put("fromid", iMessage.getIfrom()); hm.put("timestamp", iMessage.getTimes()); // 如果是單聊 if (iMessage.getType().equals("friend")) { try { st.setInformation(JSON.toJSONString(hm)); sendpoint(iMessage.getUid(), JSON.toJSONString(st)); } catch (IOException e) { // TODO 自動生成的 catch 塊 e.printStackTrace(); } } else { try { st.setInformation(JSON.toJSONString(hm)); sendInfo(String.valueOf(iMessage.getIfrom()), JSON.toJSONString(st)); } catch (IOException e) { // TODO 自動生成的 catch 塊 e.printStackTrace(); } } } /** * 點對點消息傳遞:獲取session裡面的id對應發送給誰的id */ public static void sendpoint(int id, String message) throws IOException { for (NewsController item : webSockets) { if (item.session.getQueryString().equals(String.valueOf(id))) { item.sendMessage(message); } } } /** * 群聊消息 */ public static void sendInfo(String id, String message) throws IOException { for (NewsController item : webSockets) { if (!item.session.getQueryString().equals(id)) { try { item.sendMessage(message); } catch (IOException e) { continue; } } } } /** * 這個方法與上面幾個方法不一樣。沒有用註解,是根據自己需要添加的方法。 * * @param message * @throws IOException */ public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); // this.session.getAsyncRemote().sendText(message); } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { NewsController.onlineCount++; } public static synchronized void subOnlineCount() { NewsController.onlineCount--; } /** * 匿名內部類 * * @Author: Guotl * @Time:下午5:38:15 */ class sutes { private String state = "null"; private String information = "null"; public String getState() { return state; } public void setState(String state) { this.state = state; } public String getInformation() { return information; } public void setInformation(String information) { this.information = information; } }}
6、前台代碼:layim即時通訊,當你購買後導入進來直接可以使用
var websocket = null; if (WebSocket in window) { websocket = new WebSocket("ws://localhost:8888/webSocket?"+[[${user.id}]]); } else { alert(該瀏覽器不支持websocket); } websocket.onopen = function (event) { console.log(websocket建立連接); } websocket.onclose = function (event) { console.log(websocket關閉連接); } websocket.onmessage = function (res) { var scoketutil=JSON.parse(res.data); if(scoketutil.information!="null"){ layim.getMessage(JSON.parse(scoketutil.information)); //res.data即你發送消息傳遞的數據(閱讀:監聽發送的消息) } if(scoketutil.state!="null"){ var skt=scoketutil.state.split(","); layim.setFriendStatus(parseInt(skt[0]),skt[1]); //即使更新好友狀態 } } window.onbeforeunload = function (event) { websocket.close(); } websocket.onerror = function (event) { console.log(websocket通信發生錯誤); } //監聽發送消息 layim.on(sendMessage, function(res){ if (res.to.type == friend) { res.type = friend; websocket.send(JSON.stringify(res)); } else if (res.to.type == group) { res.type = group; websocket.send(JSON.stringify(res)); } });
GitHub會儘快上傳demo,請耐心等待。
作者:Guotl原創,分享時請分享此鏈接
推薦閱讀:
※Spring boot與Spring cloud 是什麼關係?
※springboot怎麼學?
※關於Spring MVC的教程和例子?
※spring-jdbc 目前還是一個主流的廣泛使用的持久化框架嗎?
TAG:SpringBoot | WebSocket | 即時通訊IM |