開源IM專案-InChat登入介面設計與實現(基於Netty)
只給你最值得的資訊
小弟正在做的一個開源IM專案,目標是實現一個輕量級、高效率的支援聊天與物聯網的通訊框架。昨天剛剛出的設計稿並再今天做了實現。
專案是基於Netty的二次開發,關於Netty我這裡就不再介紹了,懂的人自然都懂。我的預算是做一個所有企業或組織可以引用的Maven專案,並且是基本上開箱即用,簡單實現對應的配置與重寫方法就可以搭建自己的IM專案(某Q、某信的效果)。
本文著重介紹的是登入介面的設計與實現。
設計資訊
關於InChat統一登入的介面設計,設計針對小程式、APP、Web端的登入作用,所以將作為token的形式登入InChat的Socket/">WebSocket長連線,使用者伺服器做sso的認證登入後得到token後直接傳送login資訊到InChat,使用者伺服器需要重寫InChat中的verifyToken方法校驗自己的的Token資訊是否有效,正常則啟動長連線。考慮到token失效問題,WebSocket長連線的登入僅做初次登入,接下來考慮以心跳形式保持連結狀態(pingpong),使用token認證是為保護InChat連結的常規化(目前暫時這樣設計後面根據使用情況更改設計)
[圖片上傳失敗...(image-29d656-1543988912603)]
由於目前大部分的Web專案或基於IM的專案登入可能存在多端的單點登入,所以我選用了Token的形式,為什麼登入後還需要用token再來websocket這邊校驗一次呢?
因為你登入的是web應用程式端的,而websocket而言,只要別人知道你的地址,那麼就可以連結上,我們不希望存在過多的死連結(無效連結),所以我們需要將token再次發給InChat登入,由InChat來檢驗是否是合法登入連結,如果無效則關閉連結。
程式碼實現
由於是想要做給別人用的,那麼我們自己本身就要封裝的好一點,對於配置我選了足夠多的型別。
[圖片上傳失敗...(image-a24a6f-1543988912603)]
對於netty監聽與初始化,我使用的方式是掃描與AspectJ,你可以在專案的auto包中看到掃描啟動,這種啟動思路我也是參考了其他的開源專案(具體忘記了地址)。
@Bean @ConditionalOnMissingBean(name = "sacnScheduled") public ScanRunnable initRunable(@AutowiredInitNetty serverBean){ long time =(serverBean==null || serverBean.getPeriod()<5)?10:serverBean.getPeriod(); ScanRunnable sacnScheduled = new SacnScheduled(time); Thread scanRunnable = new Thread(sacnScheduled); scanRunnable.setDaemon(true); scanRunnable.start(); return sacnScheduled; } @Bean(initMethod = "open", destroyMethod = "close") @ConditionalOnMissingBean public InitServer initServer(InitNetty serverBean){ if(!ObjectUtils.allNotNull(serverBean.getWebport(),serverBean.getServerName())){ thrownew NullPointerException("not set port"); } if(serverBean.getBacklog()<1){ serverBean.setBacklog(_BLACKLOG); } if(serverBean.getBossThread()<1){ serverBean.setBossThread(CPU); } if(serverBean.getInitalDelay()<0){ serverBean.setInitalDelay(SEDU_DAY); } if(serverBean.getPeriod()<1){ serverBean.setPeriod(SEDU_DAY); } if(serverBean.getHeart()<1){ serverBean.setHeart(TIMEOUT); } if(serverBean.getRevbuf()<1){ serverBean.setRevbuf(BUF_SIZE); } if(serverBean.getWorkerThread()<1){ serverBean.setWorkerThread(CPU*2); } return new InitServer(serverBean); }
在上圖中存在一個DefaultWebSocketHandler,這個是預設的netty啟動處理。
當然在執行它之前,還需要執行到一個抽象的類WebSocketHandler。
它將會為我做一些基本的功能操作。
@Slf4j public abstract class WebSocketHandler extends SimpleChannelInboundHandler<Object> { WebSocketHandlerApi webSocketHandlerApi; public WebSocketHandler(WebSocketHandlerApi webSocketHandlerApi){ this.webSocketHandlerApi = webSocketHandlerApi; } @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof TextWebSocketFrame){ textdoMessage(ctx,(TextWebSocketFrame)msg); }else if (msg instanceof WebSocketFrame){ webdoMessage(ctx,(WebSocketFrame)msg); } } protected abstract void webdoMessage(ChannelHandlerContext ctx, WebSocketFrame msg); protected abstract void textdoMessage(ChannelHandlerContext ctx, TextWebSocketFrame msg); @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { log.info("【DefaultWebSocketHandler:channelInactive】"+ctx.channel().localAddress().toString()+"關閉成功"); webSocketHandlerApi.close(ctx.channel()); } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if(evt instanceof IdleStateEvent){ webSocketHandlerApi.doTimeOut(ctx.channel(),(IdleStateEvent)evt); } super.userEventTriggered(ctx, evt); } }
對於登入介面,他是基於WebSocket的,且是TextWebSocketFrame型別的,WebSocketFrame是後期的圖片聊天功能,所以我們的DefaultWebSocketHandler暫時只需要實現textdoMessage。
對於websocket的傳輸我們推薦使用json形式,這對於前後端都是由好處的。
@Override protected void textdoMessage(ChannelHandlerContext ctx, TextWebSocketFrame msg) { Channel channel = ctx.channel(); ServerWebSocketHandlerService serverWebSocketHandlerService; if (webSocketHandlerApi instanceof ServerWebSocketHandlerService){ serverWebSocketHandlerService = (ServerWebSocketHandlerService)webSocketHandlerApi; }else{ throw new NoFindHandlerException("Server Handler 不匹配"); } Map<String,String> maps = (Map) JSON.parse(msg.text()); switch (maps.get("type")){ case "login": serverWebSocketHandlerService.login(channel,msg); break; default: break; } }
由上面的程式碼,你也許才想到了前端登入的大致json內容,沒錯是這樣的。
{ type: "login", token: value }
ServerWebSocketHandlerService是一個自己定義的後端WebSocket存在的介面實現服務,我們現在使用到他的登入介面,讓我們來看看他的登入實現方法。
@Autowired InChatVerifyService inChatVerifyService; @Override public boolean login(Channel channel, TextWebSocketFrame textWebSocketFrame) { //校驗規則,自定義校驗規則 Map<String,String> maps = (Map<String, String>) JSON.parse(textWebSocketFrame.text()); System.out.println("login-"+textWebSocketFrame.text()); String token = maps.get("token"); Gson gson = new Gson(); Map<String,String> backMap = new HashMap<>(); if (inChatVerifyService.verifyToken(token)){ backMap.put("type","login"); backMap.put("success","true"); channel.writeAndFlush(new TextWebSocketFrame(gson.toJson(backMap))); return true; } backMap.put("type","login"); backMap.put("success","false"); channel.writeAndFlush(new TextWebSocketFrame(gson.toJson(backMap))); close(channel); return false; }
由以上可看出,我們會將token和inChatVerifyService.verifyToken做校驗,這是一個介面,我並沒有寫實現,因為這個是使用者的事情了,它需要實現並重寫我的verifyToken方法,並返回給我一個值,如下是我測試的時候寫的模擬實現。
/** * 不屬於專案程式碼 * Created by MySelf on 2018/11/22. */ @Service public class InChatVerifyServiceImpl implements InChatVerifyService { @Override public boolean verifyToken(String token) { //與Redis中的Token做比較,請使用者自己實現,查詢是否存在該Token值 System.out.println("verify---"+token); if ("3333".equals(token)){ return true; } return false; } }
我僅僅做了普通的校驗,對於使用者可以注入RedisTemplate然後進行校驗等工作。到此我們的登入介面就實現好了!
看看效果

微信圖片_20181122171033.png
GitHub
專案名: ofollow,noindex">InChat
專案地址: https://github.com/UncleCatMySelf/InChat
專案簡介:A lightweight, efficient communication framework that supports chat and the Internet of Things(一個輕量級、高效率的支援聊天與物聯網的通訊框架)
Demo分支
原始專案核心演示, 您可以先運行了解, 模仿微聊天聊天應用程式, 逐步更新, 基於 springbot-web 套接字的一般框架, 結合 netty 聊天社交, 並記錄聊天日誌, 非同步儲存, 前端臨時 sui mobile, 新增實現 tcp/後端通訊埠 (mqtt 協議、實時和微控制器等 tcp 硬體通訊), 新增圖片處理流、聊天實現文字和圖片傳送功能,api 呼叫 netty 長連結執行傳送訊息 (聯機、使用者列表的數量)
[圖片上傳失敗...(image-31008c-1543988912603)]
webrtc分支
基於Netty與web RTC實現語音與視訊通訊的分支專案
im-api分支
騰訊 im (雲通訊) 後端模擬專案, 以 api 的形式對接, 如果有前端想要停靠可以執行此分支, 預計該分支最終將為一個單一的服務併發 300, 000個使用者的 im 後臺專案
paho-mqtt分支
基於 paho. js 和 java mqtt 客戶端訊息訂閱和通訊的小型程式端或移動 web 端, 小程式物聯網演示目前支援 ws 格式
tcp-wechat分支
基於小程式端與微控制器等硬體 tcp/ip 的主要通訊, 將物聯網中心作為中轉, 本演示將充分實現具體功能

1787897575-5b6fb1bf2b46f_articlex.png