Netty学习笔记(四) 简单的聊天室功能之服务端开发
前面三个章节,我们使用了Netty实现了DISCARD丢弃服务和回复以及自定义编码解码,这篇博客,我们要用Netty实现简单的聊天室功能。
Ps: 突然想起来大学里面有个课程实训,给予UDP还是TCP实现的聊天程序,简单的分析一下,那个实现和基于Netty的实现是不一样的,基于UDP或者TCP做的聊天室中只能是客户端向服务发送消息(当然基于UDP的也可以建立两个Channel来实现服务器和客户端的双向通道),然后客户端接收到消息,这里的服务器仅仅作为一个接收消息处理之的作用,并不能主动向客户端推送消息。
基于前面几个章节的知识,这里我们做一个简单的聊天室功能,我们简单的说一下需求:
- 进入聊天室,服务器发送欢迎信息到该用户的客户端
- 有新人进入或者退出聊天室,那么聊天室的其他用户都能接收到通知信息
- 某位用户发送消息,其他用户的客户端显示格式为
[时间][发送用户的名称/地址]消息内容,自己的客户端显示[时间][You]消息内容
这篇博文仅仅实现服务端的代码,使用telent测试,客户端的作为下一篇阐述。
工具类代码
工具类就一个时间格式化的工具,如下:
package com.zhoutao123.simpleChat.utils;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DatetimeUtils {
private static final SimpleDateFormat smf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String getNowDatetime(){
return smf.format(new Date());
}
}
服务端代码
服务处理适配器
和之前的代码一样,这里我们继承SimpleChannelInboundHandler,具体的解释在注释里面。
package com.zhoutao123.simpleChat.server;
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.util.concurrent.GlobalEventExecutor;
import static com.zhoutao123.simpleChat.utils.DatetimeUtils.getNowDatetime;
public class ServerHandle extends SimpleChannelInboundHandler<String> {
// 创建ChannelGroup 用于保存连接的Channel
public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
//当有新的Channel增加的时候
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
// 获取当前的Channel
Channel channel = ctx.channel();
//向其他Channel发送上线消息
channelGroup.writeAndFlush(String.format("[%s][服务器]\t用户:%s 加入聊天室!\n", getNowDatetime(), channel.remoteAddress()));
// 添加Channel到Group里面
channelGroup.add(channel);
// 向新用户发送欢迎信息
channel.writeAndFlush(String.format("你好,%s欢迎来到Netty聊天室\n", channel.remoteAddress()));
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
// 用户退出后向全部Channel发送下线消息
channelGroup.writeAndFlush(String.format("[%s][服务器]\t用户:%s 离开聊天室!\n", getNowDatetime(), ctx.channel().remoteAddress()));
// 移除
channelGroup.remove(ctx.channel());
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
// 服务器接收到新的消息后之后执行
// 获取当前的Channel
Channel currentChannel = ctx.channel();
// 遍历
for (Channel channel : channelGroup) {
String sendMessage = "";
// 如果是当前的用户发送You的信息,不是则发送带有发送人的信息
if (channel == currentChannel) {
sendMessage = String.format("[%s][You]\t%s\n", getNowDatetime(), msg);
} else {
sendMessage = String.format("[%s][%s]\t %s\n", getNowDatetime(), currentChannel.remoteAddress(), msg);
}
channel.writeAndFlush(sendMessage);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// 发送异常的时候通知移除
Channel channel = ctx.channel();
channelGroup.writeAndFlush(String.format("[%s][服务器]\t 用户 %s 出现异常掉线!\n", getNowDatetime(), channel.remoteAddress()));
ctx.close();
}
}
处理器初始化
这里主要是配置一些编码器以及解码器以及我们自己定义的ServerHandleAdapter
package com.zhoutao123.simpleChat.server;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
public class SimpleChatServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new ServerHandle());
System.out.println("SimpleChatClient:"+ch.remoteAddress() +"连接上");
}
}
启动服务器
启动的代码和以前一致,没有打的改动.
package com.zhoutao123.simpleChat.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class Server {
private final static int port = 8080;
public static void main(String[] args) throws InterruptedException {
NioEventLoopGroup boss = new NioEventLoopGroup();
NioEventLoopGroup work = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(boss, work)
.channel(NioServerSocketChannel.class)
.childHandler(new SimpleChatServerInitializer())
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
System.out.println("聊天服务已经启动.....");
ChannelFuture sync = serverBootstrap.bind(port).sync();
sync.channel().closeFuture().sync();
} finally {
work.shutdownGracefully();
boss.shutdownGracefully();
System.out.println("聊天服务已经被关闭");
}
}
}
TELNET测试
启动服务之后,我在Linux上使用Telnet命令来简单的测试了下,
这里创建了4个用户,发送了一些信息,可以观察一下:
Netty学习笔记(四) 简单的聊天室功能之服务端开发的更多相关文章
- Netty学习笔记(六) 简单的聊天室功能之WebSocket客户端开发实例
在之前的Netty相关学习笔记中,学习了如何去实现聊天室的服务段,这里我们来实现聊天室的客户端,聊天室的客户端使用的是Html5和WebSocket实现,下面我们继续学习. 创建客户端 接着第五个笔记 ...
- Netty学习笔记(四)——实现dubbo的rpc
1.rpc基本介绍 RPC ( Remote Procedure Call) -远程过程调用,是一个计算机通信协议.该协议允许运行于一台计算机的程序调用另一台计算机的子程序,两个或多个应用程序分布不同 ...
- 通过WebSocket实现一个简单的聊天室功能
WebSocket WebSocket是一个协议,它是是基于TCP的一种新的网络协议,TCP协议是一种持续性的协议,和HTTP不同的是,它可以在服务器端主动向客户端推送消息.通过这个协议,可以在建立一 ...
- 玩转Node.js(四)-搭建简单的聊天室
玩转Node.js(四)-搭建简单的聊天室 Nodejs好久没有跟进了,最近想用它搞一个聊天室,然后便偶遇了socket.io这个东东,说是可以用它来简单的实现实时双向的基于事件的通讯机制.我便看了一 ...
- ensorflow学习笔记四:mnist实例--用简单的神经网络来训练和测试
http://www.cnblogs.com/denny402/p/5852983.html ensorflow学习笔记四:mnist实例--用简单的神经网络来训练和测试 刚开始学习tf时,我们从 ...
- ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁
作者:Grey 原文地址: ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁 前置知识 完成ZooKeeper集群搭建以及熟悉ZooKeeperAPI基本使用 需求 当多个进 ...
- Netty 学习笔记(1)通信原理
前言 本文主要从 select 和 epoll 系统调用入手,来打开 Netty 的大门,从认识 Netty 的基础原理 —— I/O 多路复用模型开始. Netty 的通信原理 Netty 底层 ...
- Netty学习笔记-入门版
目录 Netty学习笔记 前言 什么是Netty IO基础 概念说明 IO简单介绍 用户空间与内核空间 进程(Process) 线程(thread) 程序和进程 进程切换 进程阻塞 文件描述符 文件句 ...
- Netty 学习(四):ChannelHandler 的事件传播和生命周期
Netty 学习(四):ChannelHandler 的事件传播和生命周期 作者: Grey 原文地址: 博客园:Netty 学习(四):ChannelHandler 的事件传播和生命周期 CSDN: ...
随机推荐
- [Swift]LeetCode55. 跳跃游戏 | Jump Game
Given an array of non-negative integers, you are initially positioned at the first index of the arra ...
- [Swift]LeetCode277. 寻找名人 $ Find the Celebrity
Suppose you are at a party with n people (labeled from 0 to n - 1) and among them, there may exist o ...
- [Swift]LeetCode710. 黑名单中的随机数 | Random Pick with Blacklist
Given a blacklist B containing unique integers from [0, N), write a function to return a uniform ran ...
- [Swift]LeetCode762. 二进制表示中质数个计算置位 | Prime Number of Set Bits in Binary Representation
Given two integers L and R, find the count of numbers in the range [L, R] (inclusive) having a prime ...
- 虎牙数万主播同时在线直播的秘密,CDN推流日志上行实时监控
6 月 10 日,又拍云 Open Talk | 2018 音视频技术沙龙·深圳站 顺利落幕,来自虎牙的直播运维研发架构师张波在沙龙上做了<基于CDN推流日志的主播上行实时监控及其自动化解密&g ...
- 「造个轮子」——cicada(轻量级 WEB 框架)
前言 俗话说 「不要重复造轮子」,关于是否有必要不再本次讨论范围. 创建这个项目的主要目的还是提升自己,看看和知名类开源项目的差距以及学习优秀的开源方式. 好了,现在着重来谈谈 cicada 这个项目 ...
- nodejs搭建web项目
如果要使用cnpm可安装淘宝cnpm镜像(事实证明不建议使用,因为cnpm和npm有一些包不同步)npm install -g cnpm --registry=https://registry.npm ...
- Java提高班(五)深入理解BIO、NIO、AIO
导读:本文你将获取到:同/异步 + 阻/非阻塞的性能区别:BIO.NIO.AIO 的区别:理解和实现 NIO 操作 Socket 时的多路复用:同时掌握 IO 最底层最核心的操作技巧. BIO.NIO ...
- 痞子衡嵌入式:第一本Git命令教程(6)- 日志(log/reflog/gitk)
今天是Git系列课程第六课,上一课我们学会了Git本地提交,今天痞子衡要讲的是如何查看Git本地历史提交. 当我们在仓库里做了很多次提交之后,免不了需要回看提交记录,看看自己之前的改动.有三种Git命 ...
- mybatis和hibernate的区别
1. hibernate是全自动,而mybatis是半自动 hibernate完全可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成sql.而mybat ...