Netty利用ChannelGroup广播消息
在Netty中提供了ChannelGroup接口,该接口继承Set接口,因此可以通过ChannelGroup可管理服务器端所有的连接的Channel,然后对所有的连接Channel广播消息。
Server端:
public class BroadCastServer {
public static void run(int port) {
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, worker)
.channel(NioServerSocketChannel.class) // 设置Channel Type
.option(ChannelOption.SO_BACKLOG, 1024) // 设置Channel属性
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast(new BroadCastChannelHandler());
}
});
ChannelFuture channelFuture = bootstrap.bind(port).sync();
if (channelFuture.isDone()) {
System.out.println(String.format("server bind port %s sucess", port));
}
Channel channel = channelFuture.channel();
/**CloseFuture异步方式关闭*/
channel.closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
public static void main(String []args) {
BroadCastServer.run(8080);
}
}
public class BroadCastChannelHandler extends ChannelInboundHandlerAdapter {
private static final Gson GSON = new GsonBuilder().create();
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static final AtomicInteger response = new AtomicInteger();
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel ch = ctx.channel();
if (ChannelGroups.size() > 0) {
Message msg = new Message(ch.remoteAddress().toString().substring(1), SDF.format(new Date()));
ChannelGroups.broadcast(GSON.toJson(msg), new ChannelMatchers());
}
ChannelGroups.add(ch);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Channel ch = ctx.channel();
if (ChannelGroups.contains(ch) && String.valueOf(msg).equals("welcome")) {
System.out.println(String.format("receive [%s] from [%s] at [%s]", String.valueOf(msg) ,
ch.remoteAddress().toString().substring(1), SDF.format(new Date())));
response.incrementAndGet();
}
synchronized (response) {
System.out.println(response.get() + "\t" + ChannelGroups.size());
if (response.get() == ChannelGroups.size() - 1) {
System.out.println("server close all connected channel");
ChannelGroups.disconnect();
response.set(0);
}
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
ctx.close();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ChannelGroups.discard(ctx.channel());
response.decrementAndGet();
}
public static class ChannelMatchers implements ChannelMatcher {
@Override
public boolean matches(Channel channel) {
return true;
}
}
}
服务器端收到所有连接客户端对广播消息的响应后,服务器端主动关闭已连接的Channel
客户端:
public class Client {
private static final String host = "127.0.0.1";
private static final int port = 8080;
private static final ExecutorService es = Executors.newFixedThreadPool(5);
public static void start() {
for (int i = 0; i < 5; i++) {
es.execute(new Task());
}
es.shutdown();
}
public static class Task implements Runnable {
@Override
public void run() {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast(new SimpleClientChannelHandler());
}
});
ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
if (channelFuture.isSuccess()) {
System.out.println(String.format("connect server(%s:%s) sucess", host, port));
}
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
}
public static void main(String []args) {
Client.start();
}
}
public class SimpleClientChannelHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Channel channel = ctx.channel();
System.out.println(String.format("client(%s) receive message [%s]", channel.localAddress().toString().substring(1),
String.valueOf(msg)));
System.out.println();
ctx.writeAndFlush(String.valueOf("welcome"));
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
ctx.disconnect(ctx.newPromise());
ctx.close();
System.out.println(String.format("client(%s) close sucess", ctx.channel().localAddress().toString().substring(1)));
}
}
本文使用ChannelGroups辅助类管理服务器端已连接的Channel,代码实现如下:
public class ChannelGroups {
private static final ChannelGroup CHANNEL_GROUP = new DefaultChannelGroup("ChannelGroups", GlobalEventExecutor.INSTANCE);
public static void add(Channel channel) {
CHANNEL_GROUP.add(channel);
}
public static ChannelGroupFuture broadcast(Object msg) {
return CHANNEL_GROUP.writeAndFlush(msg);
}
public static ChannelGroupFuture broadcast(Object msg, ChannelMatcher matcher) {
return CHANNEL_GROUP.writeAndFlush(msg, matcher);
}
public static ChannelGroup flush() {
return CHANNEL_GROUP.flush();
}
public static boolean discard(Channel channel) {
return CHANNEL_GROUP.remove(channel);
}
public static ChannelGroupFuture disconnect() {
return CHANNEL_GROUP.disconnect();
}
public static ChannelGroupFuture disconnect(ChannelMatcher matcher) {
return CHANNEL_GROUP.disconnect(matcher);
}
public static boolean contains(Channel channel) {
return CHANNEL_GROUP.contains(channel);
}
public static int size() {
return CHANNEL_GROUP.size();
}
}
Netty利用ChannelGroup广播消息的更多相关文章
- rabbitMQ应用,laravel生产广播消息,springboot消费消息
最近做一个新需求,用户发布了动态,前台需要查询,为了用户读取信息响应速度更快(MySQL很难实现或者说实现起来很慢),所以在用户动态发布成功后,利用消息机制异步构建 redis缓存 和 elastic ...
- node的socket.io的广播消息
在多个客户端与服务器端建立连接后,socket.io()服务器具有一个sockets属性,属性值为所有与客户端建立连接的socket对象.可以利用该对象的send方法或emit方法向所有客户端广播消息 ...
- Linux下UDP收/发广播消息简单实现
发送广播消息 #include<stdio.h> #include<stdlib.h> #include<string.h> #include<sys/typ ...
- Linux系统下UDP发送和接收广播消息小例子
// 发送端 #include <iostream> #include <stdio.h> #include <sys/socket.h> #include < ...
- 利用System V消息队列实现回射客户/服务器
一.介绍 在学习UNIX网络编程 卷1时,我们当时可以利用Socket套接字来实现回射客户/服务器程序,但是Socket编程是存在一些不足的,例如: 1. 服务器必须启动之时,客户端才能连上服务端,并 ...
- 关于 OnCloseQuery: 顺序、不能关机等(所有的windows的广播消息都是逐窗口传递的)——如果一个窗体的OnCloseQuery事件中如果写了代码那么WM_QUERYENDSESSION消息就传不过去了msg.result会返回0,关机事件也就停止了
系统关闭窗体的事件顺序为: OnCloseQuery ----> OnClose ----> OnDestroy 下面的代码说明问题: unit Unit3; interface uses ...
- Android中利用Handler实现消息的分发机制(三)
在第二篇文章<Android中利用Handler实现消息的分发机制(一)>中,我们讲到主线程的Looper是Android系统在启动App的时候,已经帮我们创建好了,而假设在子线程中须要去 ...
- 利用rabbit_mq队列消息实现对一组主机进行命令下发
目的: 利用rabbit_mq队列消息实现对一组主机进行命令下发 server: #!/usr/bin/env python3.5 # -*- coding:utf8 -*- import os,sy ...
- Openfire开发广播服务接口,支持离线广播消息
Openfire开发广播服务接口,支持离线广播消息 概要 最近公司要求做一个web端向所有移动端发送公告,所以考虑到即时性就用openfire做服务.不过为了减轻web端的工作量,我们开发一个简单的插 ...
随机推荐
- 6.Spark streaming技术内幕 : Job动态生成原理与源码解析
原创文章,转载请注明:转载自 周岳飞博客(http://www.cnblogs.com/zhouyf/) Spark streaming 程序的运行过程是将DStream的操作转化成RDD的操作, ...
- mongo connect BI 连接至Power BI
第一步:安装mongodb服务 官网地址:https://www.mongodb.com/download-center?jmp=nav#community mongodb的安装请参考:http:// ...
- 如何访问mvc 默认的错误页
在ActionResult 中: public ActionResult Error() { return View("~/Views/Shared/Error.cshtml"); ...
- Python3 文件操作基本语法
对文件操作流程 打开文件,得到文件句柄并赋值给一个变量 通过句柄对文件进行操作 关闭文件 f = open('lyrics') #打开文件 first_line = f.readline() prin ...
- js相关数组迭代方法图解
- Junit3.8源码--核心类
好久没画图了,看下这个序列图,还算比较清晰的: 以textui来分析: Test 顶层接口.TestSuite和TestCase均实现此接口,在具体执行的时候面向此接口编程,弱化类型,实现各自的执行流 ...
- Flask实战第54天:cms删除轮播图功能完成
后台逻辑 编辑cms.views.py @bp.route('/dbanner/',methods=['POST']) @login_required def dbanner(): banner_id ...
- ansible学习-playbook的YAML语法
[一篇非常好的ansible参考博文] 初识Ansible http://liumissyou.blog.51cto.com/4828343/1616462 --------------------- ...
- [Nescafé41]编码病毒(循环卷积)
题意看起来好麻烦实际上很简单,首先4s可以先bitset暴力一下,听说卡卡就能过:$O(2^{22}+n^2/32)$ #include<cstdio> #include<bitse ...
- 【欧拉回路】【欧拉路径】【Fleury算法】CDOJ1634 记得小苹初见,两重心字罗衣
Fleury算法看这里 http://hihocoder.com/problemset/problem/1181 把每个点看成边,每个横纵坐标看成一个点,得到一个无向图. 如果新图中每个点的度都是偶数 ...