前面三个章节,我们使用了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学习笔记(四) 简单的聊天室功能之服务端开发的更多相关文章

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

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

  2. Netty学习笔记(四)——实现dubbo的rpc

    1.rpc基本介绍 RPC ( Remote Procedure Call) -远程过程调用,是一个计算机通信协议.该协议允许运行于一台计算机的程序调用另一台计算机的子程序,两个或多个应用程序分布不同 ...

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

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

  4. 玩转Node.js(四)-搭建简单的聊天室

    玩转Node.js(四)-搭建简单的聊天室 Nodejs好久没有跟进了,最近想用它搞一个聊天室,然后便偶遇了socket.io这个东东,说是可以用它来简单的实现实时双向的基于事件的通讯机制.我便看了一 ...

  5. ensorflow学习笔记四:mnist实例--用简单的神经网络来训练和测试

    http://www.cnblogs.com/denny402/p/5852983.html ensorflow学习笔记四:mnist实例--用简单的神经网络来训练和测试   刚开始学习tf时,我们从 ...

  6. ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁

    作者:Grey 原文地址: ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁 前置知识 完成ZooKeeper集群搭建以及熟悉ZooKeeperAPI基本使用 需求 当多个进 ...

  7. Netty 学习笔记(1)通信原理

    前言 本文主要从 select 和 epoll 系统调用入手,来打开 Netty 的大门,从认识 Netty 的基础原理 —— I/O 多路复用模型开始.   Netty 的通信原理 Netty 底层 ...

  8. Netty学习笔记-入门版

    目录 Netty学习笔记 前言 什么是Netty IO基础 概念说明 IO简单介绍 用户空间与内核空间 进程(Process) 线程(thread) 程序和进程 进程切换 进程阻塞 文件描述符 文件句 ...

  9. Netty 学习(四):ChannelHandler 的事件传播和生命周期

    Netty 学习(四):ChannelHandler 的事件传播和生命周期 作者: Grey 原文地址: 博客园:Netty 学习(四):ChannelHandler 的事件传播和生命周期 CSDN: ...

随机推荐

  1. [Swift]LeetCode55. 跳跃游戏 | Jump Game

    Given an array of non-negative integers, you are initially positioned at the first index of the arra ...

  2. [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 ...

  3. [Swift]LeetCode710. 黑名单中的随机数 | Random Pick with Blacklist

    Given a blacklist B containing unique integers from [0, N), write a function to return a uniform ran ...

  4. [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 ...

  5. 虎牙数万主播同时在线直播的秘密,CDN推流日志上行实时监控

    6 月 10 日,又拍云 Open Talk | 2018 音视频技术沙龙·深圳站 顺利落幕,来自虎牙的直播运维研发架构师张波在沙龙上做了<基于CDN推流日志的主播上行实时监控及其自动化解密&g ...

  6. 「造个轮子」——cicada(轻量级 WEB 框架)

    前言 俗话说 「不要重复造轮子」,关于是否有必要不再本次讨论范围. 创建这个项目的主要目的还是提升自己,看看和知名类开源项目的差距以及学习优秀的开源方式. 好了,现在着重来谈谈 cicada 这个项目 ...

  7. nodejs搭建web项目

    如果要使用cnpm可安装淘宝cnpm镜像(事实证明不建议使用,因为cnpm和npm有一些包不同步)npm install -g cnpm --registry=https://registry.npm ...

  8. Java提高班(五)深入理解BIO、NIO、AIO

    导读:本文你将获取到:同/异步 + 阻/非阻塞的性能区别:BIO.NIO.AIO 的区别:理解和实现 NIO 操作 Socket 时的多路复用:同时掌握 IO 最底层最核心的操作技巧. BIO.NIO ...

  9. 痞子衡嵌入式:第一本Git命令教程(6)- 日志(log/reflog/gitk)

    今天是Git系列课程第六课,上一课我们学会了Git本地提交,今天痞子衡要讲的是如何查看Git本地历史提交. 当我们在仓库里做了很多次提交之后,免不了需要回看提交记录,看看自己之前的改动.有三种Git命 ...

  10. mybatis和hibernate的区别

    1. hibernate是全自动,而mybatis是半自动 hibernate完全可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成sql.而mybat ...