resin4.0.44+websocket 實現私信功能服務端訊息推送
最近專案開發中,碰到一個新的開發需求——私信功能。
專案要求:類似微博中傳送私信功能,給對方傳送一條私信訊息,如果對方線上就立馬接受到訊息提示,並顯示到頁面上。如果對方不線上,則下次登入以後,顯示訊息提示。
技術選擇:websocket也是目前比較流行的接收伺服器端訊息的一門HTML5技術,我們伺服器採用的是resin4.0+,所以綜合考慮採用基於resin的websocket形式實現該功能。
軟體版本:resin4.0.44、websocket、SpringMVC、redis
這裡著重強調下,專案架構是SpringMVC結構,這裡就不在贅述Spring相關的配置,主要介紹下resin下的websocket如何實現訊息推送。
第一步:
新建websocket資料封裝Bean,用來儲存websocket+user對應資訊。
package com.gochina.tc.websocket;
import com.caucho.websocket.WebSocketContext;
/**
* websocket封裝bean
* @author hwy
*
*/
public class WebSocketBean {
private String userCode;//使用者的code
private int hashCode;//websocket的hashCode
private WebSocketContext webSocketContext;//websocketContext
public WebSocketBean(String userCode, int hashCode,
WebSocketContext webSocketContext) {
super();
this.userCode = userCode;
this.hashCode = hashCode;
this.webSocketContext = webSocketContext;
}
public String getUserCode() {
return userCode;
}
public void setUserCode(String userCode) {
this.userCode = userCode;
}
public int getHashCode() {
return hashCode;
}
public void setHashCode(int hashCode) {
this.hashCode = hashCode;
}
public WebSocketContext getWebSocketContext() {
return webSocketContext;
}
public void setWebSocketContext(WebSocketContext webSocketContext) {
this.webSocketContext = webSocketContext;
}
}
第二步:
新建MyWebSocketServlet,用來連線前端websocket與下邊的listener建立關係的入口統一配置,將所有接受到的使用者的websocket請求暫存起來。
package com.gochina.tc.websocket;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import com.caucho.websocket.WebSocketContext;
import com.caucho.websocket.WebSocketListener;
import com.caucho.websocket.WebSocketServletRequest;
@Controller
@RequestMapping(value = "/websocket")
@SuppressWarnings("serial")
public class MyWebSocketServlet extends HttpServlet{
private static List<WebSocketBean> socketList = new ArrayList<WebSocketBean>();
@RequestMapping(value = "{userCode}")
public void service(HttpServletRequest req,HttpServletResponse res,@PathVariable("userCode") String userCode)
throws IOException, ServletException{
//當呼叫了下面的startWebSocket函式後,該socket就會和相應的listener建立起對應關係
WebSocketListener listener = new MyWebSocketListener();
WebSocketServletRequest wsReq = (WebSocketServletRequest) req;
WebSocketContext webSocketContext = wsReq.startWebSocket(listener);
WebSocketBean webSocketBean = new WebSocketBean(userCode, webSocketContext.hashCode(), webSocketContext);
socketList.add(webSocketBean);
}
/**
* 獲取連線的websocket列表
* @return
*/
public static List<WebSocketBean> getSockList(){
return socketList;
}
}
注意:這裡的@RequestMapping(value = “{userCode}”)中的userCode是前端建立連線時,傳過來的使用者的唯一標示,根據這個標示確定是哪位使用者發起的websocket請求。
第三步:
新建MyWebSocketListener,這個是用來監聽websocket整個生命週期,包含啟動、關閉、失去連線等等一系列操作。WebSocketListener是resin封裝過一次的,我們只需實現它,然後進行自己的業務邏輯處理即可。
package com.gochina.tc.websocket;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.caucho.websocket.WebSocketContext;
import com.caucho.websocket.WebSocketListener;
import com.gochina.tc.util.redis.RedisUtil;
/**
* websocket訊息監聽
* @author hwy
*
*/
public class MyWebSocketListener implements WebSocketListener{
private Logger log = LoggerFactory.getLogger(MyWebSocketListener.class);
/**
* 移除websocket
* @param webSocketContext
*/
public void remove(WebSocketContext webSocketContext){
List<WebSocketBean> socketList = MyWebSocketServlet.getSockList();
WebSocketBean webSocketBean = null;
for(WebSocketBean socket:socketList){
if(socket.getHashCode() == webSocketContext.hashCode()){
webSocketBean = socket;
break;
}
}
if(webSocketBean != null){
socketList.remove(webSocketBean);
}
}
/**
* 關閉連線
*/
@Override
public void onClose(WebSocketContext webSocketContext) throws IOException {
remove(webSocketContext);
log.info(webSocketContext.hashCode()+" is closed");
}
/**
* 斷開連線
*/
@Override
public void onDisconnect(WebSocketContext webSocketContext) throws IOException {
remove(webSocketContext);
log.info(webSocketContext.hashCode()+" is disconnect");
}
/**
* 接收二進位制訊息
*/
@Override
public void onReadBinary(WebSocketContext arg0, InputStream arg1)
throws IOException {
}
/**
* 接收文字訊息併發送訊息
*/
@Override
public void onReadText(WebSocketContext webSocketContext, Reader reader)
throws IOException {
PrintWriter out = null;
int ch;
String text = "";
while ((ch = reader.read()) >= 0) {
text = text+(char)ch;
}
int hashCode = webSocketContext.hashCode();
List<WebSocketBean> socketList = MyWebSocketServlet.getSockList();
for(WebSocketBean socket:socketList){
try{
if(socket.getHashCode() == hashCode){
int count = 0;
Object o = RedisUtil.get("tc_user_message_"+socket.getUserCode());//從redis中獲取訊息數目
if(o != null){
count = Integer.parseInt(o+"");
}
out = socket.getWebSocketContext().startTextMessage();
out.print(count);
out.close();
break;
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(out != null){
out.close();
}
if(reader != null){
reader.close();
}
}
}
}
/**
* 開始連線
*/
@Override
public void onStart(WebSocketContext webSocketContext) throws IOException {
webSocketContext.setTimeout(43200000);//設定連線關閉時間
log.info(webSocketContext.hashCode()+" is start");
}
/**
* 連線超時
*/
@Override
public void onTimeout(WebSocketContext webSocketContext) throws IOException {
remove(webSocketContext);
log.info(webSocketContext.hashCode()+" is timeOut");
}
/**
* 給所有線上使用者傳送訊息
* @param text
*/
public void sendToOnlingUsers(String text){
List<WebSocketBean> socketList = MyWebSocketServlet.getSockList();
PrintWriter out = null;
for(WebSocketBean socket:socketList){
try{
out = socket.getWebSocketContext().startTextMessage();
out.print(text);
out.close();
}catch(Exception e){
e.printStackTrace();
}finally{
if(out != null){
out.close();
}
}
}
}
/**
* 給某個使用者傳送訊息
* @param text
* @param userCode
*/
public void sendToOneUser(String text,String userCode){
List<WebSocketBean> socketList = MyWebSocketServlet.getSockList();
int i = 0;
PrintWriter out = null;
for(WebSocketBean socket:socketList){
String code = socket.getUserCode();
if(code.equals(userCode)){
try{
out = socket.getWebSocketContext().startTextMessage();
out.print(text);
out.close();
}catch(Exception e){
e.printStackTrace();
}finally{
if(out != null){
out.close();
}
}
}
}
}
}
注意:這裡面有幾個地方需要著重注意下。
首先,在onStart方法是websocket每次建立連線時會觸發,每次關閉連線和斷開連線的時候,會相應觸發onStop()和onDisconnect()方法,需要移除暫存的websocket物件。
其次,webSocketContext.setTimeout(43200000);//設定連線關閉時間,在onStart方法裡面有個設定連線關閉時間的方法,此處有坑。。。最初我的resin版本是4.0.44版本以下的,測試發現,每次連線在200s以後就會莫名的斷開,即使設定了setTimeOut()依舊200s自動關閉。而且官網例項中明確寫了在onStart()中setTimetOut()即可修改連線關閉時間的。。。百思不得姐的時候。google翻看resin更新日誌中無意間看到了這個 resin change log。
Resin Change Log
Resin 3.1 changes
4.0.44 - in progress
jsee: self signed cert should support Firefox and Chrome default cipher-suites(#5884)
jsee: self signed cert should check expire (#5885)
class-loader: excessive reread of jar certificates (#5850, rep by konfetov)
log: add sanity check for log rollover (#5845, rep by Keith F.)
deploy (git): use utf-8 to store path names (#5874, rep by alpor9)
websocket: setTimeout was being overridden by Port keepaliveTimeout (#5841, rep by A. Durairaju)
jni: on windows, skip JNI for File metadata like length (#5865, rep by Mathias Lagerwall)
db: isNull issues with left join (#5853, rep by Thomas Rogan)
websocket: check for socket close on startTextMessage (#5837, rep by samadams)
log: when log rollover fails, log to stderr (#5855, rep by Rock)
filter: allow private instantiation (#5839, rep by V. Selvaggio)
rewrite: added SetRequestCharacterEncoding (#5862, rep by Yoon)
health: change health check timeout to critical instead of fatal to allow for development sleep (#5867)
alarm: timing issue with warnings and alarm extraction (#4854, rep by SHinomiya Nobuaki)
session: orphan deletion throttling needs faster retry time (rep by Thomas Rogan)
mod_caucho: slow PUT/POST uploads with Apache 2.4 (#5846, rep by Stegard)
好吧,果斷將resin版本升至最新版4.0.44。
第四步:
前臺js發起websocket請求。(只複製js程式碼)
//websocket獲取未讀訊息數量
if (userId != null && userId != '') {
var webSocket ;
if(window.WebSocket) {//google & Firefox
webSocket = new WebSocket('ws://127.0.0.1:8080/websocket/'+userId);
}else if('MozWebSocket' in window) {
webSocket = new WebSocket('ws://127.0.0.1:8080/websocket/'+userId);
}else{//不支援websocket瀏覽器
getApiData(API.unReadCountApi+"?userCode="+userId, findUnReadMessageCount);
}
if(webSocket){
webSocket.onerror = function(event) {
console.log("connection error: "+event);
};
webSocket.onopen = function(event) {
console.log("connection established");
webSocket.send('1');
};
//接受到訊息後,顯示到頁面
webSocket.onmessage = function(event) {
console.log("connection receive message: "+event.data);
getUnReadMessageCount(event.data);
};
webSocket.onclose = function(event) {
console.log("connection close");
webSocket.close();
};
}
}
這裡是前端js發起websocket請求程式碼斷,大致寫法都類似,只是注意一點的是 webSocket = new WebSocket(‘ws://127.0.0.1:8080/websocket/’+userId);裡面的請求地址寫法,測試時把127.0.0.1換成線上域名的時候,訊息接收不到,訊息地址不通,無奈換成了ip地址就可以接受到了。不知是我伺服器配置問題還是什麼問題,希望瞭解的同學告訴我一下,感激不盡。
還有一點就是在不支援websocket的瀏覽器的時候,可以使用ajax長輪詢獲取伺服器端訊息,網上有一個socket.js對websocket支援的比較好,包括對不支援的瀏覽器的相容問題,好像Spring4.0+已經支援socket.js了,有時間可以學習下,我這裡是偷懶了,放棄了對不支援websocket的瀏覽器(IE10一下),只是在開啟頁面的時候請求了一次。
經過上面的四步的配置,一個基於resin4.0+websocket實現服務端訊息推送的功能就實現了。
當然現在用的比較多的還是tomcat7.0+ 和websocket整合實現該功能,(弱弱吐槽下,別再resin下使用javaee7+webscoket的方式實現,走過的路就是流過的淚啊! 當初第一版後臺使用的是javaee7的websocket實現方式,在本地tomcat7+以上測試,沒有問題,由於過於自信,直接丟到線上resin伺服器下,然後悲劇就發生了。。。啟動正常,訪問直接導致伺服器CPU 300%,難忘的加班開始了。如果大家需要,我也可以寫一篇基於javaee7+websocket簡版實現服務端訊息推送功能(非整合式Spring4.0+那種),最後強調不要在resin下跑。。。不要在resin下跑。。。不要在resin下跑。。。)
相關推薦
resin4.0.44+websocket 實現私信功能服務端訊息推送
最近專案開發中,碰到一個新的開發需求——私信功能。 專案要求:類似微博中傳送私信功能,給對方傳送一條私信訊息,如果對方線上就立馬接受到訊息提示,並顯示到頁面上。如果對方不線上,則下次登入以後,顯示訊息提示。 技術選擇:websocket也是目前比較流行的接收
使用spring boot +WebSocket實現(後臺主動)訊息推送
前言:使用此webscoket務必確保生產環境能相容/支援!使用此webscoket務必確保生產環境能相容/支援!使用此webscoket務必確保生產環境能相容/支援!主要是tomcat的相容與支援。有個需求:APP使用者產生某個操作,需要讓後臺管理系統部分人員感知(表現為一
spring boot 整合activemq 進行服務端訊息推送(web頁面)
最近公司的專案裡有需要服務端向web端實時推送訊息的需求,網上搜索了一番,有前端頁面通過定時任務向後臺傳送ajax請求重新整理,有使用第三方提供的訊息服務(GoEasy),前者因為會有很多請求是無用的,容易加大伺服器負荷造成宕機,後者現在收費了(免費的也只能用一
webSocket-簡單的服務端定時推送以及重連
本文章是對webSocket的學習,在使用webSocket進行客戶端-服務端的互動。 參考文章: Java基於Socket的簡單推送,在文章在服務端 輸入後回車 ,可進行對客戶端的資訊傳送,同時進行回饋。 以下為自行改進:服務端定時推送資訊到客戶端,可根據自行需要進行調整。
android通過xmpp實現伺服器到客戶端的推送功能
最近專案中要做推送功能,除了自己知道的友盟推送外還不知道其它的實現方式,於是就上網百度了一下要實現推送的基本途徑,發現主要還有以下幾種方式。 1.客戶端建立一個socket,與伺服器端的serversocket連線,其實就是客戶端與伺服器一直保持連線,這個其實本質上
android客戶端訊息推送功能實現方案
1.使用第三方推送平臺 如友盟、極光、百度等現成的訊息推送。好處:訊息及時、穩定,整合簡單。不需要自己搭建支援伺服器,但是可能涉及到收費、保密、服務質量、擴充套件等問題。 2、MQTT協議實現android推送 MQTT是一個輕
WebSocket實現前後端訊息推送
環境 jdk8 tomcat7 谷歌瀏覽器和火狐瀏覽器(瀏覽器得支援websocket) 本文用webSocket建立一個簡單的聊天室,直接上程式碼。。。 websocket 用到jar包: <dependency> &l
Java後端實現安卓/IOS移動端訊息推送(百度雲推送)
本文主要介紹Java伺服器端如何藉助第三方推送平臺(百度雲推送)推送給移動端訊息。 使用案例介紹: 根據客戶的需求,需要做一個類似淘寶訊息推送的功能,客戶下訂單、訂單付款、訂單商品已發貨,以及客戶完成評論,都需要以訊息推送提示的方式告知商家和賣家這麼一個功能,由於之前沒有實現過這方面的功
SpringBoot2.x服務端主動推送SSE
講解SpringBoot2.x服務端主動推送Sever-Send-Events 1、localhost:8080/index.html 2、需要把
Asp.Net Core 2.1 中 利用SignalR 服務端主動推送資料
最近正在學習 SignalR 在Asp.Net Core中的應用(資料推送等等。。。)。以下為個人學習時遇到問題的記錄和解決方法。對於Asp.Net Core我也是剛剛學習,所以不保證完全正確。如果有錯誤還請大家指正,多謝多謝!!!在完整版(傳統版)的 Asp.Net 程式
3、客戶端給服務端發訊息和服務端主動推送訊息
index.html <!-- 客戶端 --> <!DOCTYPE html> <html lang="en"> <head>
使用pushplus+python實現亞馬遜到貨訊息推送微信
xbox series和ps5發售以來,國內黃牛價格一直居高不下。雖然海外amazon上ps5補貨很少而且基本撐不過一分鐘,但是xbox series系列明顯要好搶很多。 日亞、德亞的xbox series x/s都可以直郵中國大陸,所以我們只需要藉助指令碼,監控相關網頁的動態,在補貨的第一時刻通
OAuth2.0學習(3-1)服務端實現
other cti info ase service packages artifacts ace ews 開源 http://oltu.apache.org/ 其他 http://www.oschina.net/project/tag/307/oauth?lang=19&
在WinCE 6.0系統下實現USB功能定製
USB的廣泛應用就不用多說了,相信目前的各個領域的嵌入式產品中,很少有不用USB的。USB是主從結構的,分為USB Host和USB Slave,從USB1.0,USB1.1到現在的USB2.0,基於USB2.0還有USB OTG,也就是同時支援Host和Slave裝置。目前最新的好像是USB3.0,剛開
在Spring Boot框架下使用WebSocket實現聊天功能
上一篇部落格我們介紹了在Spring Boot框架下使用WebSocket實現訊息推送,訊息推送是一對多,伺服器發訊息傳送給所有的瀏覽器,這次我們來看看如何使用WebSocket實現訊息的一對一發送,模擬的場景就是利用網頁來實現兩個人線上聊天。OK,那我們來看看這個要怎麼
SpringBoot整合WebSocket實現多個服務通訊
import com.test.www.socket.WebSocketServer; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.
【微信小程式遇到的坑】使用webSocket實現聊天功能 (支援圖片預覽 + 語音 + 檢視歷史聊天記錄 附完整程式碼)
在一般web服務中,大多使用短連線來向伺服器請求資源,與伺服器的互動頻率低,次數少。而在一些需要與伺服器互動頻繁,需要及時收到伺服器推送的場景,比如直播、聊天、多人實時遊戲,更適合使用 webSocket 進行通訊。 長連的生命週期介紹 webSocket的生命週期一共有
國標GBT28181協議,註冊功能服務端與客戶端實現程式碼
國標GBT28181協議的使用者註冊時候,需要使用者名稱密碼認證,其本質是使用 http digest的演算法, http digest演算法,在RFC2617 [HTTP Authentication: Basic and Digest Access Authentica
(二)websocket實現訊息推送之基於spring4.0實現
1、新建springBoot專案,新增依賴 &n
CXF+Spring+Hibernate實現RESTful webservice服務端實例
fast anti vax apach sql xsd txadvice component path 1.RESTful API接口定義 /* * Copyright 2016-2017 WitPool.org All Rights Reserved. * *