摘要:開始聊天發送聊天信息時消息,這樣后端就知道是誰要發給誰,根據用戶名去找到具體的線程去多帶帶推送消息,實現單聊。前端待完善左側聊天列表沒有實現,每搜索一個在線用戶,應該動態顯示在左側,點擊該用戶,動態顯示右側聊天窗口進行消息發送。
上節課講了群聊,這次來說說單聊,單聊要比群聊復雜點,但是代碼也不是很多,主要是前端顯示比較麻煩點。
效果:
首先一個新的用戶,需要先登陸,輸入自己的昵稱,然后點擊登陸。后端服務會把你的用戶名和當前的線程進行邦定,這樣就可以通過你的用戶名找到你的線程。登陸成功,后端返回定義好的消息 success,前端判斷記錄CHAT.me,這樣給別人發消息時就可以攜帶自己的信息。
在輸入框輸入用戶名,就可以返回對應的用戶的線程,這樣你就可以把消息發送給你要聊天的對象。如果不存在,后端回返回消息給前端,該用戶不存在。如果存在,就記錄此用戶名到CHAT.to中,這樣你發送消息的時候就可以發送給對應用戶了。
發送聊天信息時me:to:消息,這樣后端就知道是誰要發給誰,根據用戶名去找到具體的線程去多帶帶推送消息,實現單聊。
前端待完善左側聊天列表沒有實現,每搜索一個在線用戶,應該動態顯示在左側,點擊該用戶,動態顯示右側聊天窗口進行消息發送。現在是你和所有人的單聊消息都會顯示在右側,沒有完成拆分,因為這是一個頁面,處理起來比較麻煩,我一個后端就不花時間搞了,感興趣的可以自己去實現。
前端代碼因為注視比較詳細,就直接復制整個代碼到這里,大家自己看。
后端改造單人聊天
加入一個UserMap,邦定user和Channel
package netty; import java.util.HashMap; import java.util.Map; import io.netty.channel.Channel; /** * The class UserMap */ public class UserMap { private HashMapusers = new HashMap(); private static UserMap instance; public static UserMap getInstance () { if (instance == null) { instance = new UserMap(); } return instance; } private UserMap () { } public void addUser(String userId, Channel ch) { this.users.put(userId, ch); } public Channel getUser (String userId) { return this.users.get(userId); } public void deleteUser (Channel ch) { for (Map.Entry map: users.entrySet()) { if (map.getValue() == ch) { users.remove(map.getKey()); break; } } } }
ChatHandler改造
package netty; import java.time.LocalDateTime; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.group.ChannelGroup; import io.netty.channel.group.DefaultChannelGroup; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketFrame; import io.netty.util.concurrent.GlobalEventExecutor; /** * */ public class ChatHandler extends SimpleChannelInboundHandler{ public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); public static UserMap usermap = UserMap.getInstance(); /** * 每當從服務端收到新的客戶端連接時,客戶端的 Channel 存入ChannelGroup列表中,并通知列表中的其他客戶端 Channel */ @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { Channel incoming = ctx.channel(); for (Channel channel : channels) { channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入 "); } channels.add(ctx.channel()); } /** * 每當從服務端收到客戶端斷開時,客戶端的 Channel 移除 ChannelGroup 列表中,并通知列表中的其他客戶端 Channel */ @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { Channel incoming = ctx.channel(); for (Channel channel : channels) { channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 離開 "); } channels.remove(ctx.channel()); } /** * 會話建立時 */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { // (5) Channel incoming = ctx.channel(); System.out.println("ChatClient:"+incoming.remoteAddress()+"在線"); } /** * 會話結束時 */ @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6) Channel incoming = ctx.channel(); System.out.println("ChatClient:"+incoming.remoteAddress()+"掉線"); //清除離線用戶 this.usermap.deleteUser(incoming); } /** * 出現異常 */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (7) Channel incoming = ctx.channel(); System.out.println("ChatClient:"+incoming.remoteAddress()+"異常"); // 當出現異常就關閉連接 cause.printStackTrace(); ctx.close(); } /** * 讀取客戶端發送的消息,并將信息轉發給其他客戶端的 Channel。 */ @Override protected void channelRead0(ChannelHandlerContext ctx, Object request) throws Exception { if (request instanceof FullHttpRequest) { //是http請求 FullHttpResponse response = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1,HttpResponseStatus.OK , Unpooled.wrappedBuffer("Hello netty" .getBytes())); response.headers().set("Content-Type", "text/plain"); response.headers().set("Content-Length", response.content().readableBytes()); response.headers().set("connection", HttpHeaderValues.KEEP_ALIVE); ctx.channel().writeAndFlush(response); } else if (request instanceof TextWebSocketFrame) { // websocket請求 //此處id為neety自動分配給每個對話線程的id,有兩種,一個長id一個短id,長id唯一,短id可能會重復 String userId = ctx.channel().id().asLongText(); //客戶端發送過來的消息 String msg = ((TextWebSocketFrame)request).text(); System.out.println("收到客戶端"+userId+":"+msg); //發送消息給所有客戶端 群聊 //channels.writeAndFlush(new TextWebSocketFrame(msg)); // 邦定user和channel // 定義每個上線用戶主動發送初始化信息過來,攜帶自己的name,然后完成綁定 模型 init:[usrname] // 實際場景中應該使用user唯一id if (msg.indexOf("init") != -1) { String userNames[] = msg.split(":"); if ("init".equals(userNames[0])) { // 記錄新的用戶 this.usermap.addUser(userNames[1].trim(), ctx.channel()); ctx.channel().writeAndFlush(new TextWebSocketFrame("success")); } } //搜索在線用戶 消息模型 search:[username] if (msg.indexOf("search") != -1) { Channel ch = this.usermap.getUser(msg.split(":")[1].trim()); if (ch != null) { //此用戶存在 ctx.channel().writeAndFlush(new TextWebSocketFrame("search:"+msg.split(":")[1].trim()+":已找到")); } else { // 此用戶不存在 ctx.channel().writeAndFlush(new TextWebSocketFrame("search:"+msg.split(":")[1].trim()+":未找到")); } } //發送消息給指定的用戶 消息模型 me:to:[msg] if (msg.split(":").length == 3) { //判斷是單聊消息 this.usermap.getUser(msg.split(":")[1].trim()).writeAndFlush(new TextWebSocketFrame(msg)); } //ctx.channel().writeAndFlush(new TextWebSocketFrame(((TextWebSocketFrame)request).text())); } } }
注釋很詳細,自己看
總結消息模型應該定義一個多帶帶的類來管理,我目前是用的String字符串來判斷,提前規定了一些模型,通過判斷來響應前端的請求,比較簡單。還有就是沒有使用數據庫,前端不能顯示聊天記錄,不能實現消息的已讀未讀。實際場景中應該對消息進行加密存儲,且不能窺探用戶隱私。
前端可以使用localstorage來存儲聊天記錄,自己可以擴展。
前端的顯示可能有點問題,自己可以調。其實主要是學習netty后端的搭建
別忘了關注我 mike啥都想搞
求關注啊。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/101348.html
摘要:提供異步的事件驅動的網絡應用程序框架和工具,用以快速開發高性能高可靠性的網絡服務器和客戶端程序。總結我們完成了服務端的簡單搭建,模擬了聊天會話場景。 之前一直在搞前端的東西,都快忘了自己是個java開發。其實還有好多java方面的東西沒搞過,突然了解到netty,覺得有必要學一學。 介紹 Netty是由JBOSS提供的一個java開源框架。Netty提供異步的、事件驅動的網絡應用程序框...
摘要:上節課完成了的后端搭建,搞定了簡單的請求響應,今天來結合前端來完成群聊功能。其實后端群聊很簡單,就是把一個用戶的輸入消息,返回給所有在線客戶端,前端去負責篩選顯示。 上節課完成了netty的后端搭建,搞定了簡單的http請求響應,今天來結合前端websocket來完成群聊功能。話不多說先上圖:showImg(https://segmentfault.com/img/bVbnCa8?w=...
摘要:今天來做一個簡單的聊天室支持換房間支持私信的寫的代碼有點渣里面有很多不是很好的地方畢竟我只是一個野生程序猿環境地址樣子差不多是這個樣子的我不想把代碼發到我的服務器上因為這個項目太小了很垃圾而且怕被攻擊這里有錄的一個演示視頻沒有廣告的你們可以 今天來做一個簡單的聊天室,支持換房間,支持私信的. 寫的代碼有點渣,里面有很多不是很好的地方.畢竟我只是一個野生程序猿. 環境: php7.0...
閱讀 1211·2023-04-26 02:20
閱讀 3337·2021-11-22 14:45
閱讀 4111·2021-11-17 09:33
閱讀 972·2021-09-06 15:00
閱讀 1479·2021-09-03 10:30
閱讀 3837·2021-07-26 22:01
閱讀 990·2019-08-30 15:54
閱讀 531·2019-08-30 15:43