03_netty實現聊天室功能
阿新 • • 發佈:2019-01-27
class ktr 連接 bind line 服務 RKE 客戶端 chatroom
【概述】
聊天室主要由兩塊組成:聊天服務器端(ChatRoomServer)和聊天客戶端(ChatClient)。
[ 聊天服務器(ChatRoomServer)功能概述 ]
1.監聽所有客戶端的接入、斷線
2.有客戶端A接入聊天室時,將接入消息發給除了客戶端A的其他客戶端
3.當客戶端A退出聊天室時,將退出消息發給除了客戶端A的其他客戶端
4.當客戶端A發送消息到聊天室時,將消息轉發給除了客戶端A的其他客戶端
[ 聊天客戶端(ChatClient)功能概述 ]
1.發送消息至聊天服務器
2.接收聊天服務器發送過來的所有消息
【聊天服務端 ChatRoomServer】
/** * 聊天室服務端 */ public class ChatRoomServer { private final int port ; public ChatRoomServer(int port) { this.port = port; } public void start(){ EventLoopGroup boss = new NioEventLoopGroup(); EventLoopGroup worker = new NioEventLoopGroup();try{ ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(boss,worker) .channel(NioServerSocketChannel.class) .childHandler(new ChatServerInitialize()) .option(ChannelOption.SO_BACKLOG, 128) .option(ChannelOption.SO_KEEPALIVE,true); ChannelFuture future = bootstrap.bind(port).sync(); future.channel().closeFuture().sync(); }catch (Exception e){ e.printStackTrace(); }finally { boss.shutdownGracefully(); worker.shutdownGracefully(); } } public static void main(String[] args) { new ChatRoomServer(9999).start(); //服務端監聽本地的9999端口 } }
【ChatServerInitialize】
public class ChatServerInitialize extends ChannelInitializer<SocketChannel>{ @Override protected void initChannel(SocketChannel channel) throws Exception { System.out.println("用戶【"+channel.remoteAddress()+"】接入聊天室......"); ChannelPipeline pipeline = channel.pipeline(); pipeline.addLast("framer",new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); pipeline.addLast("decoder",new StringDecoder()); pipeline.addLast("encoder",new StringEncoder()); pipeline.addLast("handler",new ChatServerHandler()); } }
【ChatServerHandler】
/** * 聊天服務器對各種情況的處理 */ public class ChatServerHandler extends SimpleChannelInboundHandler<String> { public static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); /** * 當從服務端收到新的客戶端連接時 * 客戶端的 Channel 存入 channels 列表中,並通知列表中的其他客戶端 Channel */ @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { Channel clientChannel = ctx.channel(); channels.add(clientChannel); for (Channel ch : channels) { if (ch != clientChannel) { //通知除了自己以外的其他用戶 ch.writeAndFlush("【提示】:用戶【" + clientChannel.remoteAddress() + "】進入聊天室...\n"); } } } /** * 每當從服務端收到客戶端斷開時 * 客戶端的 Channel 自動從 channels 列表中移除了,並通知列表中的其他客戶端 Channel */ @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { Channel clientChannel = ctx.channel(); channels.remove(clientChannel); for (Channel ch : channels) { if (ch != clientChannel) { //通知除了自己以外的其他用戶 ch.writeAndFlush("【提示】:用戶【" + clientChannel.remoteAddress() + "】退出聊天室...\n"); } } } /** * 接受到客戶端發出的消息 * 判斷channel是否是 */ @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { Channel clientChannel = ctx.channel(); for (Channel ch : channels) { if (ch != clientChannel) { ch.writeAndFlush("用戶【" + clientChannel.remoteAddress() + "】說:" + msg + "\n"); } else { ch.writeAndFlush("【我】說:" + msg + "\n"); } } } /** * 服務端監聽到客戶端活動 */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { Channel clientChannel = ctx.channel(); System.out.println("用戶【"+clientChannel.remoteAddress()+"】在線中..."); } /** * 服務端監聽到客戶端 不活動 */ @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { Channel clientChannel = ctx.channel(); System.out.println("用戶【 " +clientChannel.remoteAddress()+"】:離線了"); } }
【ChatClient 聊天客戶端】
/** * 聊天客戶端 */ public class ChatClient { private final String host; private final int port; public ChatClient(String host, int port) { this.host = host; this.port = port; } public void start() { EventLoopGroup worker = new NioEventLoopGroup(); Bootstrap bootstrap = new Bootstrap(); try{ bootstrap.group(worker) .channel(NioSocketChannel.class) .handler(new ClientInitializer()); Channel channel = bootstrap.connect(host,port).sync().channel(); //客戶端從鍵盤輸入數據 BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); while(true){ channel.writeAndFlush(input.readLine()+"\n"); } }catch (Exception e){ e.printStackTrace(); }finally { worker.shutdownGracefully(); } } public static void main(String[] args) { new ChatClient("127.0.0.1",9999).start(); //連接服務器端 } }
【ChatClientInitializer 】
public class ChatClientInitializer extends ChannelInitializer<SocketChannel>{ @Override protected void initChannel(SocketChannel socketChannel) throws Exception { //當有客戶端連接服務器時,netty會調用這個初始化器的 initChannel方法 System.out.println("客戶端開始初始化......"); ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast("framer",new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); pipeline.addLast("decoder",new StringDecoder()); pipeline.addLast("encoder",new StringEncoder()); pipeline.addLast("handler",new ChatClientHandler()); } }
【ChatClientHandler】
public class ChatClientHandler extends SimpleChannelInboundHandler<String> { /** * 打印服務端發送過來的數據 */ @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception { System.out.println(s); } }
【運行結果】
[1.啟動聊天服務器]
[2.啟動一個客戶端A]
[3.再啟動一個客戶端B]
[4.客戶端A發送消息]
[5.客戶端A關閉]
03_netty實現聊天室功能