日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

java基于netty NIO的簡單聊天室的實現

瀏覽:82日期:2022-08-28 18:21:56

一、為何要使用netty開發

由于之前已經用Java中的socket寫過一版簡單的聊天室,這里就不再對聊天室的具體架構進行細致的介紹了,主要關注于使用netty框架重構后帶來的改變。對聊天室不了解的同學可以先看下我的博客(《JAVA簡單聊天室的實現》)

本篇博客所使用的netty版本為4.1.36,完整工程已上傳到Github(https://github.com/Alexlingl/Chatroom),其中lib文件夾下有相應的netty jar包和source包,自行導入即可。

1、為何要重構

之前的聊天室是基于Java原生socket實現的,socket的處理機制屬于BIO模型,也就是阻塞IO模型。對于每一個客戶端的連接我們都需要啟動一個線程來處理,并且該線程會一直阻塞在讀取用戶數據上面。如此一來,一旦有大量的客戶端并發連接我們的服務器,服務器將難以承受。之前用JMeter測試過,單純使用Java原生socket開發的服務器所能支持的最大并發在2300左右。雖然采用線程池的策略可以在一定程度上提升最大并發數,但也無法超過1W。因此我們需要對其進行重構,使其能夠具有更高的性能。

2、為何使用netty框架

使用netty框架主要還是為了提升代碼的開發速度,并且減少代碼維護成本。使用netty框架開發的程序在復雜度上比使用Java原生NIO類庫開發的要小很多。具體可以看下我之前關于解決C10k問題的系列文章,里面有具體的代碼。

3、為何netty框架只實現了NIO而沒有AIO

前面在解決C10問題時,探究過NIO和AIO的區別,并且使用Java所提供的類庫實現了兩個小程序,理論上來說AIO性能明顯要比NIO高,那為什么netty使用了NIO而不是AIO呢?

官方說法如下:

We obviously did not consider Windows as a serious platform so far, and that’s why we were neglecting NIO.2 AIO API which was implemented using IOCP on Windows. (On Linux, it wasn’t any faster because it was using the same OS facility - epoll.)

大意就是,windows上面有IOCP來支持AIO的實現,因此AIO的性能會比NIO好。而Linux上面不管是NIO還是AIO,底層都是用epoll實現的,性能差距不大。然而當下絕大部分的服務器還是建立在Linux上,因此沒必要使用AIO(使用AIO反而會增加代碼的復雜度,增大維護成本)。

二、基于netty NIO的處理模型

1、服務器的類關系

(1)、SubreqServer:創建兩個NIO線程組,一個用來監聽處理客戶端的連接請求,一個用來監聽客戶端的消息。同時實例化一個ServerBootStrap啟動類的對象來啟動兩個NIO線程組,并且配置必要的參數。(2)、ChannelInitializer:初始化SocketChannel管道的各項參數,主要有指定解碼器和編碼器,并指明管道的處理類(3)、SubreqServerHandler:SocketChannel管道的處理類,負責處理來自客戶端的消息

2、客戶端的類關系

(1)、SubreqClient:創建一個NIO線程組,用來監聽客戶端的消息。同時實例化一個ServerBootStrap啟動類的對象來啟動這個NIO線程組,并且配置必要的參數。最后讓主程序阻塞在監聽客戶端的鍵盤輸入。(2)、ChannelInitializer:初始化SocketChannel管道的各項參數,主要有指定解碼器和編碼器,并指明管道的處理類(3)、SubreqClientHandler:SocketChannel管道的處理類,負責處理來自服務器的消息

三、所涉及類庫的源碼解讀

1、ChannelHandlerAdapter(用來對channel的注冊和注銷做出反應的類):

(1)、功能

用來實現當用戶上線或下線時,通知其他在線的用戶。

(2)、類定義

它是ChannelHandler的框架實現

java基于netty NIO的簡單聊天室的實現

(3)、HandlerAdded()和HandlerRemoved()方法

java基于netty NIO的簡單聊天室的實現

當有一個channel被注冊后將會調用HandlerAdded(),而當有一個channel被注銷后將調用HandlerRemoved()方法。并且根據注釋我們知道,這兩個方法默認不做任何處理,它希望由繼承的子類自己去寫相應的處理實現。

2、SimpleChannelInboundHandler(用來對接收的消息做出反應)

(1)、功能

用來實現當接收到消息時做出相應反應,如果是服務端,那么將當前消息轉發給其他在線的客戶端;如果是客戶端,就將消息簡單地打印出來。

(2)、類定義

java基于netty NIO的簡單聊天室的實現

java基于netty NIO的簡單聊天室的實現

這個類是一個泛型類,它只能用于處理一種具體類型的消息。注釋中給出一種使用方法,StringHandler繼承了SimpleChannelInboundHandler,并且指定泛型變量為String,因此這個繼承類只能用于String類型消息的處理。

(2)、channelRead(ChannelHandlerContext ctx, Object msg)

java基于netty NIO的簡單聊天室的實現

這個方法主要用來處理類型為Object的消息,也就是所有消息。

首先當接收到msg時,先使用accpetInboundMessage()方法來判斷該消息是否可以處理。我們來看下該方法的實現。

java基于netty NIO的簡單聊天室的實現

如果接收到的這個消息應該被處理就返回true,如果它應當被傳到ChannelPipeLine的下一個ChannelInboundHandler就返回false。

我們繼續來看下ChannelRead()方法。如果接受到的消息可以處理時。它將對消息進行強制轉化,將其轉為I,并且調用channelRead0()。它是一個抽象類,因此我們在繼承SimpleChannelInboundHandler類時,需要根據自己的實際去實現這個方法。

java基于netty NIO的簡單聊天室的實現

四、關鍵的代碼實現

1、server端

(1)、SubreqServer類

package nettyserverv1; import java.util.ArrayList; import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.*;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.codec.serialization.ClassResolvers;import io.netty.handler.codec.serialization.ObjectDecoder;import io.netty.handler.codec.serialization.ObjectEncoder; /** * Created by linguolong on 2019/05/08. * Chatroom server built using netty framework */ public class SubreqServer {//保存已注冊的用戶信息public static ArrayList<UserInfo> userlist = new ArrayList<UserInfo>();//自動生成注冊用戶static{for(int i=0;i<10;i++){UserInfo user=new UserInfo();user.setUserID('123'+i);user.setUserName('user'+i);user.setPassword('pwd'+i);userlist.add(user);}} public void bind(int port) throws Exception{ //配置服務端NIO 線程組 EventLoopGroup boss = new NioEventLoopGroup(); EventLoopGroup worker = new NioEventLoopGroup(); ServerBootstrap server = new ServerBootstrap(); try { server.group(boss, worker) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 100) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { /*** 解碼器: 構造器傳入了兩個參數: * #1 單個對象序列化后最大字節長度,這是設置是1M;* #2 類解析器: weakCachingConcurrentResolver創建線程安全的WeakReferenceMa對類加載器進行緩存,* 支持多線程并發訪問,當虛擬機內存不足時,會釋放緩存中的內存,防止內存泄漏.*/ ch.pipeline().addLast(new ObjectDecoder(1024*1024, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())) ); ch.pipeline().addLast(new ObjectEncoder()); ch.pipeline().addLast(new SubreqServerHandler()); } }); System.out.println('Start the server success'); //綁定端口, 同步等待成功 ChannelFuture future = server.bind(port).sync(); //等待服務端監聽端口關閉 future.channel().closeFuture().sync(); } finally { //優雅關閉 線程組 boss.shutdownGracefully(); worker.shutdownGracefully(); } } public static void main(String[] args) { SubreqServer server = new SubreqServer(); try { server.bind(18888); } catch (Exception e) { e.printStackTrace(); } }}

(2)、SubreqServerHandler類

package nettyserverv1; import io.netty.channel.Channel;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.SimpleChannelInboundHandler;import io.netty.channel.group.DefaultChannelGroup;import io.netty.channel.group.ChannelGroup;import io.netty.util.concurrent.GlobalEventExecutor; /** * Created by linguolong on 2019/05/08. * Chatroom client built using netty framework */ public class SubreqServerHandler extends SimpleChannelInboundHandler<String>{//新建一個channelGroup,用于存放連接的channelpublic static ChannelGroup online_channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);@Overridepublic void handlerRemoved(ChannelHandlerContext ctx){ Channel leave_channel = ctx.channel(); for (Channel channel : online_channels) { if (channel != leave_channel){ channel.writeAndFlush('[用戶 ' + leave_channel.remoteAddress() + ']下線了!n'); } } System.out.println(ctx.channel().id()+'下線了');//把剛下線的channel移除出在線用戶隊列online_channels.remove(leave_channel);} @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //判斷用戶是否已經登錄 if(!check_login(ctx)){ //未登錄則進行登錄驗證 check_identity(ctx,msg); }else{ //已登錄則進行消息轉發 Channel coming_channel = ctx.channel(); for (Channel channel : online_channels) { if (channel != coming_channel){ channel.writeAndFlush('[用戶 ' + coming_channel.remoteAddress() + ']: ' + msg ); } else { channel.writeAndFlush('[我]: ' + msg); } } } } //用戶信息驗證,檢查用戶ID和密碼是否正確 public void check_identity(ChannelHandlerContext ctx, Object msg){ UserInfo req = (UserInfo) msg; System.out.println('service receive client login req :{'+ req.toString() +'}'); boolean login_flag = false; for(int i=0;i<SubreqServer.userlist.size();i++){ if( SubreqServer.userlist.get(i).getUserID().equalsIgnoreCase(req.getUserID())&&(SubreqServer.userlist.get(i).getPassword().equals(req.getPassword()))){ login_flag=true; } } if(login_flag){ System.out.println('賬號'+req.getUserID()+'登錄成功'); ctx.writeAndFlush('您已登錄成功~n'); //將當前的通道加入在線隊列中 online_channels.add(ctx.channel()); } else{ System.out.println('賬號'+req.getUserID()+'登錄失敗'); ctx.writeAndFlush('登錄失敗!'); //關閉連接 ctx.close(); online_channels.remove(ctx.channel()); } } //判斷用戶是否已經在線 public boolean check_login(ChannelHandlerContext ctx){ boolean online_flag = false; for(int i=0;i<online_channels.size();i++){ if(online_channels.contains(ctx.channel())){ online_flag = true; } }return online_flag; } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { //釋放資源 ctx.close(); }@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {} //ChannelRead0()只能處理類型為String的消息,因此我們這里不能用ChannelRead0()這個方法,這里的第二個參數類型使用了泛型@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {}}

(3)、用戶信息UserInfo類

package nettyserverv1; import java.io.Serializable; /** * Created by linguolong on 2019/05/08. * Chatroom User Infomation */ public class UserInfo implements Serializable{ private String userID; private String userName; private String password; public String getUserID() { return userID; } public void setUserID(String userID) { this.userID = userID; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } @Override public String toString() { return 'SubscribeReq: [messageID]:'+ userID + ' [userName]:' +userName + ' [password]:' +password; } }

2、Client端

(1)、SubreqClient類

package nettyclientv1; import java.io.BufferedReader;import java.io.InputStreamReader; import io.netty.bootstrap.Bootstrap;import io.netty.channel.Channel;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelOption;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioSocketChannel;import io.netty.handler.codec.serialization.ClassResolvers;import io.netty.handler.codec.serialization.ObjectDecoder;import io.netty.handler.codec.serialization.ObjectEncoder; /** * Created by linguolong on 2019/05/08. * Chatroom client built using netty framework */ public class SubreqClient { public void connect(int port, String host) throws Exception{ //配置客戶端NIO 線程組 EventLoopGroup group = new NioEventLoopGroup(); Bootstrap client = new Bootstrap(); try { client.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { //添加解碼器 ch.pipeline().addLast(new ObjectDecoder(1024, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())) ); //添加編碼器 ch.pipeline().addLast(new ObjectEncoder()); ch.pipeline().addLast(new SubreqClientHandler()); } }); //異步獲取當前已連接的channel Channel now_channel = client.connect(host,port).sync().channel(); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); //異步等待客戶端連接端口關閉// now_channel.closeFuture().sync(); //使客戶端一直處于輸入狀態,直到讀取到'bye' String message = ' '; while (true) { //讀到bye時退出 if(message.equals('bye')) break; message = reader.readLine(); now_channel.writeAndFlush(message+'n'); } //讀到了'bye'字符串,主動斷開連接 now_channel.close(); } finally { //優雅關閉 線程組 group.shutdownGracefully(); } } public static void main(String[] args) { SubreqClient client = new SubreqClient(); try { client.connect(18888, '127.0.0.1'); } catch (Exception e) { e.printStackTrace(); } }}

(2)、SubreqClientHandler類

package nettyclientv1; import nettyserverv1.UserInfo;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter; /** * Created by linguolong on 2019/05/08. * Chatroom client built using netty framework */ public class SubreqClientHandler extends ChannelInboundHandlerAdapter{ public SubreqClientHandler() { } /** * @param ctx * @throws Exception */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush(subReq('1231','user1','pwd1')); ctx.flush(); } private UserInfo subReq(String id,String userName,String password){ UserInfo req = new UserInfo(); req.setUserID(id); req.setUserName(userName); req.setPassword(password); return req; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.print(msg.toString()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); }}

五、說明

1、登錄功能的實現:當客戶端剛連接上服務器時,便構造一個UserInfo對象,對對象進行編碼后發送給服務器。服務器接收到后對其進行解碼,驗證相應的賬戶ID和密碼是否正確。

到此這篇關于java基于netty NIO的簡單聊天室的實現的文章就介紹到這了,更多相關java簡單聊天室內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Java
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
免费一区二区三区在线视频| 国产亚洲亚洲| 青青在线精品| 久久精品97| 久久国际精品| 欧美激情视频一区二区三区免费 | 激情综合网站| 极品日韩av| 国产一区二区精品| 免费人成精品欧美精品| 日韩手机在线| 国产精品一区二区三区av| 麻豆视频久久| 久久久久久久欧美精品| 欧美一区二区三区免费看| 国产乱码精品一区二区三区亚洲人| 日本国产一区| 美女免费视频一区| 在线手机中文字幕| 中文字幕系列一区| 国产日韩综合| 欧美日韩 国产精品| 久久伊人久久| 一区二区三区四区在线看| 亚洲一区二区免费看| 只有精品亚洲| 精品国产91| 怡红院精品视频在线观看极品| 亚洲精品福利| 精品入口麻豆88视频| 久久狠狠婷婷| 色狠狠一区二区三区| 久久精品资源| 午夜av一区| 欧美日一区二区三区在线观看国产免| 欧美在线黄色| 欧美天堂视频| 中文字幕中文字幕精品| 国产激情久久| 亚洲午夜黄色| 91在线成人| 日本综合字幕| 亚洲人妖在线| 国产精品久久久一区二区| 精品日韩视频| 日韩久久99| 91亚洲一区| 天堂av在线一区| 国产欧美日韩视频在线| 欧美久久天堂| 亚洲欧美高清| 久久一区精品| 亚洲免费中文| 精品久久久中文字幕| 亚洲激情中文在线| 国产精品欧美三级在线观看| 神马午夜久久| 国产精品一级| 国产免费成人| 国产一区二区三区四区五区传媒| 国产亚洲精品v| 精品久久中文| 日韩一区二区三区免费视频 | 中文字幕乱码亚洲无线精品一区| 久久精品色播| 久久福利精品| 国产福利片在线观看| 亚洲精品极品| 久久精品青草| 国产美女久久| 日韩视频二区| 欧产日产国产精品视频| 欧美日韩1区2区3区| 日韩一级欧洲| 成人在线视频免费| 亚洲精品日本| 999国产精品999久久久久久| 欧美亚洲一级| 日本大胆欧美人术艺术动态| 日本不卡免费高清视频在线| 久久精品97| 亚洲人成亚洲精品| 欧美一级精品| 中文字幕成在线观看| 国产欧美一区二区三区国产幕精品 | 欧美午夜精品一区二区三区电影| 欧美精品自拍| 91亚洲国产高清| 国产欧美综合一区二区三区| 免费在线看一区| 亚洲成人三区| 久久久国产亚洲精品| 精品三级av在线导航| 日韩av中文在线观看| 日韩专区在线视频| 免费黄色成人| 欧美成人基地 | 中文视频一区| 亚洲国产一区二区三区在线播放| 国产福利片在线观看| 国产精品二区影院| 97久久超碰| 日韩精品电影一区亚洲| 在线一区二区三区视频| 久久国产精品久久久久久电车 | 高清一区二区三区av| 国产精品日本一区二区不卡视频 | 蜜臀av一区二区三区| 欧美福利在线| 久久一区二区三区电影| 久久免费黄色| 日韩精品久久久久久久电影99爱| 精品一区二区三区在线观看视频| 国产精品成人一区二区网站软件| 国产情侣一区在线| 国产剧情在线观看一区| 久久国产免费看| 国产区精品区| 欧美激情网址| 国产一区二区三区四区| 国产一区精品福利| 久久精品国产999大香线蕉| 青青草91久久久久久久久| 欧美中文一区| 国产福利一区二区三区在线播放| 国产欧美一区二区精品久久久 | 911精品国产| 国产精品一区亚洲| 久久超碰99| 狂野欧美性猛交xxxx| 久久中文在线| 亚洲va在线| 国产精品天堂蜜av在线播放| 日韩国产欧美三级| 国产福利一区二区精品秒拍 | 国产一区不卡| 久久永久免费| 精品一区二区三区四区五区| 国产福利亚洲| 麻豆精品蜜桃视频网站| 国产一区一一区高清不卡| 青青青免费在线视频| 麻豆视频在线观看免费网站黄 | 久久精品网址| 香蕉成人av| 99日韩精品| 日本在线不卡视频| 国产日本精品| 日产精品一区| 久久福利精品| 国产精品一卡| 成人影视亚洲图片在线| 欧美一级精品| 亚洲欧美日本国产| 麻豆精品视频在线| 日本在线精品| 亚洲欧美网站| 国产精品黄色片| 99热精品久久| 一区二区国产精品| 国产精品**亚洲精品| 日韩在线欧美| 美国三级日本三级久久99| 国产伦精品一区二区三区在线播放 | 青青草国产精品亚洲专区无| 久久性天堂网| 国产午夜一区| 日韩综合精品| 一二三区精品| 麻豆成人91精品二区三区| av在线日韩| 日韩区一区二| 日韩欧美1区| 亚洲精品一区二区妖精| 日韩1区2区3区| sm捆绑调教国产免费网站在线观看 | 日本欧美一区二区在线观看| 国产精品午夜av| 日本在线精品| 日韩三级视频| 九九色在线视频| 综合欧美精品| 久久青青视频| 亚洲精品美女91| 国内精品伊人| 中文一区一区三区免费在线观| 亚洲精品伊人| 日韩成人亚洲| 亚洲三级在线| 日本а中文在线天堂| 亚洲免费网址| 伊人久久在线| 69精品国产久热在线观看| 日本在线精品| 国产精品嫩模av在线| 三级在线观看一区二区| 久久久久免费| 国产日韩欧美一区二区三区 | 日韩毛片在线| 青青在线精品|