在原項目中,對於WebSocket的長鏈接,聊天系統並無開放接口出來給第三方的系統調用,只有咱們系統內部的人員才知道,確切的說系統內部也沒有實際的查詢接口,那麼咱們今天就來實現這個功能。前端
在Netty下的Websocket長鏈接中,以API形式獲取在線用戶數,與在線用戶列表,並針對某個用戶已API調用的形式進行數據發送,而不須要所謂的前端頁面去建立websocket鏈接。java
首先,咱們須要一個相似ChannelGroup的鏈接池來存放咱們的鏈接實例,這裏我直接在原來本地模擬的一個LikeRedisTemplate中新建了一個ConcurrentHashMap,用於存放對應的用戶名——鏈接實例的鍵值對。git
方便後期API調用時能夠經過這個LikeRedisTemplate中的這個Map進行獲取、刪除及相關信息。github
/**存放連接池實例*/ private Map<Object,Object> ChannelRedisMap = new ConcurrentHashMap<>(); /** * 存儲對應的用戶名與Netty連接實例 * @param name 登陸用戶名 * @param channel Netty連接實例 */ public void saveChannel(Object name,Object channel){ ChannelRedisMap.put(name,channel); } /** * 獲取存儲池中的連接實例 * @param name 登陸用戶名 * @return {@link io.netty.channel.Channel 連接實例} */ public Object getChannel(Object name){ return ChannelRedisMap.get(name); } /** * 刪除存儲池實例 * @param name 登陸用戶名 */ public void deleteChannel(Object name){ ChannelRedisMap.remove(name); } /** * 獲取儲存池連接數 * @return 在線數 */ public Integer getSize(){ return ChannelRedisMap.size(); } /** * 返回在線用戶列表信息 * @return 用戶名列表 */ public Object getOnline() { List<Object> result = new ArrayList<>(); for (Object key:ChannelRedisMap.keySet()){ result.add(key); } return result; }
有了容器,咱們就須要在對應的位置進行鏈接實例的鍵值對存儲,我目前選擇了在聊天消息傳輸過程當中進行存儲,暫時尚未抽象出來。web
並在鏈接斷開時也要作相關的處理。redis
//用戶登陸判斷 if (redisTemplate.check(incoming.id(),rName)){ //臨時存儲聊天數據 cacheTemplate.save(rName,rMsg); //存儲隨機連接ID與對應登陸用戶名 redisTemplate.save(incoming.id(),rName); //存儲登陸用戶名與連接實例,方便API調用連接實例 redisTemplate.saveChannel(rName,incoming); }else{ incoming.writeAndFlush(new TextWebSocketFrame("存在二次登錄,系統已爲你自動斷開本次連接")); channels.remove(ctx.channel()); ctx.close(); return; } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { //刪除存儲池對應實例 String name = (String) redisTemplate.getName(ctx.channel().id()); redisTemplate.deleteChannel(name); //刪除默認存儲對應關係 redisTemplate.delete(ctx.channel().id()); channels.remove(ctx.channel()); }
我直接在SendUtil中寫一個系統發送的方法,輸出也是轉爲TextWebSocketFramewebsocket
/** * 想指定連接發送數據 * @param msg 消息 * @param channel 指定連接 * @return {@link String} */ public static String sendTest(String msg,Channel channel) { try { channel.writeAndFlush(new TextWebSocketFrame( "[系統API]" + msg)); return "success"; }catch (Exception e){ e.printStackTrace(); return "error"; } }
這個就簡單一些了,定義一個統一返回的Bean,還有API的返回工具類,而後寫對應的API接口方法。app
@RestController @RequestMapping("/back") public class NCBackController { @Autowired private LikeRedisTemplate redisTemplate; /** * 獲取在線用戶數 * @return {@link ResultVo} */ @GetMapping("/size") public ResultVo getSize(){ return ResultVOUtil.success(redisTemplate.getSize()); } /** * 獲取在線用戶列表 * @return {@link ResultVo} */ @GetMapping("/online") public ResultVo getOnline(){ return ResultVOUtil.success(redisTemplate.getOnline()); } /** * API調用向在線用戶發送消息 * @param name 用戶名 * @param msg 消息 * @return {@link ResultVo} */ @PostMapping("/send") public ResultVo send(@RequestParam String name,@RequestParam String msg){ Channel channel = (Channel) redisTemplate.getChannel(name); if (channel == null){ return ResultVOUtil.error(555,"當前用戶鏈接已斷開"); } String result = SendUtil.sendTest(msg,channel); return ResultVOUtil.success(result); } }
我在項目中添加Swagger方便查看與簡單測試API,引入對應pom,在啓動類加一個註解便可。異步
啓動項目後登錄界面,發送了一個基本消息。
socket
Swagger這邊的頁面打開後,測試幾個API,都是成功的。
好了,結尾仍是成功的,不過做爲一個好產品是不能僅僅這樣的,後續咱們繼續完善。
本項目是本人近期GitHub的核心發展項目,有興趣的朋友能夠去了解下
項目名:InChat
項目地址:https://github.com/UncleCatMy...
項目介紹:基於Netty4與SpringBoot,聊天室WebSocket(文字圖片)、Iot物聯網-MQTT協議、TCP/IP協議單片機通訊,異步存儲聊天數據
若是本文對你有所幫助,歡迎關注我的技術公衆號