【概述】

聊天室主要由两块组成:聊天服务器端(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. 2018青岛网络赛G - Couleur 区间上的启发式合并

    题意:给出\(a[1...n]\),共\(n\)次操作,每次删除一个位置\(p_i\)(强制在线),此时区间会变为两个分离的区间,求每次操作的最大区间逆序对 首先要知道必要的工具,按权值建立的主席树可 ...

  2. linux ln 命令,相当于windows快捷方式

    ln -s 源文件 目标文件. ln -s  **  **,它只会在你选定的位置上生成一个文件的镜像,不会占用磁盘空间, 硬链接ln ** **,没有参数-s, 它会在你选定的位置上生成一个和源文件大 ...

  3. mono 的System.Data.SqlClient小记录

    厦门-JuzzPig()  15:33:36System.Data.SqlClient 不科学的广州-PC286()  15:33:42webservice 返回的是 xml厦门-JuzzPig()  ...

  4. React 的几个需要注意的地方

    1.写组件时,最好将一个大的组件分解成多个小的组件. 通过React写组件时,应当尽可能地将组件分为更小的更多的组件,然后再复合组件. 比如下面的评论组件就是一个组件,一个庞大的组件,这时我们还没有将 ...

  5. DIV盒子介绍

    1.盒子模型=网页布局的基石,由四部分组成: 边框(border).外边距(margin).内边距(padding).盒子中的内容(content) 2.设置顺序是顺时针:上.右.下.左. 三个值(上 ...

  6. centos6 vps部署rails

    centos 6 vps初始化部署rails应用1 ssh登录 vpsssh -p port root@server_ip_address 2 添加用户 adduser usernamepasswd ...

  7. 关于Input输入框蓝色外框的操作

    1.去掉input外框的css: input { outline : none; //去掉外框 //outline:medium; } input:focus { outline : none; } ...

  8. 关系型数据库MySQL多实例

    简介 MySQL数据库是一个中小型关系型数据库管理系统,软件开发者为瑞典MySQL AB公司.在2008年1月16号被Sun公司收购后Sun公司又被oracle公司收购.目前MySQL被广泛地应用在I ...

  9. Spring声明式事务为何不回滚

  10. 多线程编程(四)-CyclicBarrier的使用

    CyclicBarrier的介绍 类CyclicBarrier不仅有CountDownLatch所具有的功能,还可以是啊县屏障等待的功能,也就是阶段性同步,它在使用上的意义在与可以循环地实现线程要一起 ...