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: ...
随机推荐
- Java面试大纲-java面试该做哪些准备,java开发达到这样的水平可以涨工资
Java培训结束,面临的就是毕业找工作.在找工作时,就要针对性地做充分的面试准备.准备不充分的面试,完全是浪费时间,更是对自己的不负责. 上海尚学堂Java培训整理出Java面试大纲,其中大部分都是面 ...
- [Swift]LeetCode26. 删除排序数组中的重复项 | Remove Duplicates from Sorted Array
Given a sorted array nums, remove the duplicates in-place such that each element appear only once an ...
- [Swift]LeetCode273. 整数转换英文表示 | Integer to English Words
Convert a non-negative integer to its english words representation. Given input is guaranteed to be ...
- [Swift]LeetCode538. 把二叉搜索树转换为累加树 | Convert BST to Greater Tree
Given a Binary Search Tree (BST), convert it to a Greater Tree such that every key of the original B ...
- C# 当中 foreach 的原理
在 C# 当中的 foreach 语句实际上就是遍历迭代器的语法糖.例如我们拥有以下代码: public class TestClass { public void TestMethod() { va ...
- Python内置函数(37)——len
英文文档: len(s) Return the length (the number of items) of an object. The argument may be a sequence (s ...
- influxdb使用说明
前言 influxdb是目前比较流行的时间序列数据库. 何谓时间序列数据库?什么是时间序列数据库,最简单的定义就是数据格式里包含Timestamp字段的数据,比如某一时间环境的温度,CPU的使用率等. ...
- 前端笔记之JavaScript(三)关于条件判断语句、循环语句那点事
一.条件分支语句 条件分支语句,也叫作条件判断语句,就是根据某种条件执行某些语句,不执行某些语句. JS中有三种语法是可以表示条件分支的 1.1 if……else…… 条件分支的主力语法,这个主力语法 ...
- .Net Core扩展 SharpPlugs简单上手
SharpPlugs .Net Core 鋒利扩展,这是本人的开源项目 地址是 GitHub地址 大家喜欢 的话可以加个星哦 当前功能 DI AutoMapper ElasticSearch WebA ...
- Asp.Net SignalR 多平台的Client与Server
多平台 SignalR在.Net的大环境下都可以做到即时通讯,也就是说都可以使用,客户端也不仅是js.下面就来一个控制台的Client 我们需要在nuget上下载包 Microsoft.AspNet. ...