【概述】

聊天室主要由两块组成:聊天服务器端(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实现聊天室功能的更多相关文章

  1. Netty学习笔记(四) 简单的聊天室功能之服务端开发

    前面三个章节,我们使用了Netty实现了DISCARD丢弃服务和回复以及自定义编码解码,这篇博客,我们要用Netty实现简单的聊天室功能. Ps: 突然想起来大学里面有个课程实训,给予UDP还是TCP ...

  2. 使用epoll实现聊天室功能,同时比较epoll和select的异同

    1.首先介绍一下select和epoll的异同,如下(摘抄自https://www.cnblogs.com/Anker/p/3265058.html) select的几大缺点: (1)每次调用sele ...

  3. [Python] socket发送UDP广播实现聊天室功能

    一.说明 本文主要使用socket.socket发送UDP广播来实现聊天室功能. 重点难点:理解UDP通讯流程.多线程.UDP广播收发等. 测试环境:Win10\Python3.5. 程序基本流程:创 ...

  4. SignalR实现在线聊天室功能

    一.在线聊天室 1.新建解决方案 SignalROnlineChatDemo 2.新建MVC项目 SignalROnlineChatDemo.Web (无身份验证) 3.安装SignalR PM> ...

  5. PHP 之websocket实现聊天室功能

    一.功能界面 具体的详细代码:https://github.com/yangsphp/websocket-master/tree/master 二.具体代码实现 1.前端代码如下 <!DOCTY ...

  6. 黑科技!仅需 3 行代码,就能将 Gitter 集成到个人网站中,实现一个 IM 即时通讯聊天室功能?

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 高级架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...

  7. java web利用mvc结构实现简单聊天室功能

    简单聊天室采用各种内部对象不适用数据库实现. 一个聊天室要实现的基本功能是:         1.用户登录进入聊天室, 2.用户发言 3.用户可以看见别人发言 刚才算是简单的需求分析了,现在就应该是进 ...

  8. 通过WebSocket实现一个简单的聊天室功能

    WebSocket WebSocket是一个协议,它是是基于TCP的一种新的网络协议,TCP协议是一种持续性的协议,和HTTP不同的是,它可以在服务器端主动向客户端推送消息.通过这个协议,可以在建立一 ...

  9. Netty学习笔记(六) 简单的聊天室功能之WebSocket客户端开发实例

    在之前的Netty相关学习笔记中,学习了如何去实现聊天室的服务段,这里我们来实现聊天室的客户端,聊天室的客户端使用的是Html5和WebSocket实现,下面我们继续学习. 创建客户端 接着第五个笔记 ...

随机推荐

  1. [BZOJ 5155][Tjoi2014]电源插排

    传送门 网上大部分题解都写得是动态开点线段树,然而像\(MiEcoku\)这么懒惰的显然不会去写线段树... \(\color{green}{solution}\) 我们考虑来点骚操作. 线段树维护的 ...

  2. 北航操作系统实验2019:Lab4-1流程梳理

    北航操作系统实验2019:Lab4-1流程梳理 前言 操作系统的实验课实在令人头秃.我们需要在两周时间内学习相关知识.读懂指导书.读懂代码.补全代码.处理玄学bug和祖传bug,以及回答令人窒息的思考 ...

  3. OS X获取process.env.NODE_ENV出错

    原来项目是其它小组在维护,现在我们需要维护部分功能,把项目带到OS X上运行发现 webpack.config.js获取process.env.NODE_ENV变量出错 解决: 根据电脑操作系统平台类 ...

  4. wap 往下拉自动加载更多数据

    var stop=true; $(window).scroll(function(){ totalheight = parseFloat($(window).height()) + parseFloa ...

  5. Windows下的SASS环境搭建

    虽然眼下 CSS 预编译框架不少,但 SASS 已经逐渐成为主流了,为了更好的适应社会,满足市场需求,获取新技能是必须的. 之前一直想使用 SASS,但都碍于它需要在 ruby 环境下编译而退缩了,这 ...

  6. java外观模式(Facade)

    1.外观模式(Facade [fə'sɑd] n. 正面:表面:外观) 外观模式:可以理解为 中介模式(没错,就是在用户与系统之间,增加了一个类,而这个类就是外观类,所以这个模式就叫外观模式) 如下图 ...

  7. Weblogic Maven

    从weblogic 10.3.4开始支持maven deploy部署   步骤如下: 1.构建weblogic-maven-plugin jar 在D:\oracle\Middleware\wlser ...

  8. cursor : 普通,带参,可更新的游标。使用游标遍历时,强烈建议用for循环!!!

    cursor: 源数据表account中仅有两条记录: 如果输出在判断前,则出错,将最后一条记录输出两次,如下: 所以,一定要先判断notfound再输出结果: exit when (c%notfou ...

  9. python36异步任务 获取ip地址的地理位置

    # encoding: utf-8 import asyncio import requests import functools url = "http://ip.zxinc.org/ap ...

  10. Delphi下OpenGL2d绘图(03)-画线

    一.前言 画线与画点基本上代码是相同.区别在于glBegin()的参数.绘制的框架代码可以使用 Delphi下OpenGL2d绘图(01)-初始化 中的代码.修改的部份为 Draw 函数的内容. 二. ...