在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广播消息的更多相关文章

  1. rabbitMQ应用,laravel生产广播消息,springboot消费消息

    最近做一个新需求,用户发布了动态,前台需要查询,为了用户读取信息响应速度更快(MySQL很难实现或者说实现起来很慢),所以在用户动态发布成功后,利用消息机制异步构建 redis缓存 和 elastic ...

  2. node的socket.io的广播消息

    在多个客户端与服务器端建立连接后,socket.io()服务器具有一个sockets属性,属性值为所有与客户端建立连接的socket对象.可以利用该对象的send方法或emit方法向所有客户端广播消息 ...

  3. Linux下UDP收/发广播消息简单实现

    发送广播消息 #include<stdio.h> #include<stdlib.h> #include<string.h> #include<sys/typ ...

  4. Linux系统下UDP发送和接收广播消息小例子

    // 发送端 #include <iostream> #include <stdio.h> #include <sys/socket.h> #include < ...

  5. 利用System V消息队列实现回射客户/服务器

    一.介绍 在学习UNIX网络编程 卷1时,我们当时可以利用Socket套接字来实现回射客户/服务器程序,但是Socket编程是存在一些不足的,例如: 1. 服务器必须启动之时,客户端才能连上服务端,并 ...

  6. 关于 OnCloseQuery: 顺序、不能关机等(所有的windows的广播消息都是逐窗口传递的)——如果一个窗体的OnCloseQuery事件中如果写了代码那么WM_QUERYENDSESSION消息就传不过去了msg.result会返回0,关机事件也就停止了

    系统关闭窗体的事件顺序为: OnCloseQuery ----> OnClose ----> OnDestroy 下面的代码说明问题: unit Unit3; interface uses ...

  7. Android中利用Handler实现消息的分发机制(三)

    在第二篇文章<Android中利用Handler实现消息的分发机制(一)>中,我们讲到主线程的Looper是Android系统在启动App的时候,已经帮我们创建好了,而假设在子线程中须要去 ...

  8. 利用rabbit_mq队列消息实现对一组主机进行命令下发

    目的: 利用rabbit_mq队列消息实现对一组主机进行命令下发 server: #!/usr/bin/env python3.5 # -*- coding:utf8 -*- import os,sy ...

  9. Openfire开发广播服务接口,支持离线广播消息

    Openfire开发广播服务接口,支持离线广播消息 概要 最近公司要求做一个web端向所有移动端发送公告,所以考虑到即时性就用openfire做服务.不过为了减轻web端的工作量,我们开发一个简单的插 ...

随机推荐

  1. [解决] python WindowsError: [Error 3]

    python 运行出错 WindowsError: [Error ] : 'D:\\\xe7\x81\xab\xe8\xbd\xa6\xe9\x87\x87\xe9\x9b\x86\xe5\x99\x ...

  2. Crontab 在linux中的非常有用的Schedule Jobs

    crontab 是linux系统集成的定时任务命令,它对于系统运行(数据的备份,调度系统资源或命令,程序的定时触发等非常有用).crontab有些类似于Windows系统的Service的任务计划程序 ...

  3. 【SQL】ORACLE生成临时表

    在日常的SQL查询中,我们需要对要查询的数据进行事先处理,然后再在预先处理好的数据里面进行查询.此时我们就需要用到临时表了,将数据预先处理好放到临时表里面,然后再在临时表里根据我们需要的条件进行查询. ...

  4. 公司gitlab不支持ssh时,用http提交代码免密输入方法

    由于公司内网22端口被封,只能拨vpn 才能用ssh 提交代码.因此记录以下免密码http(https)提交方式. 修改项目下.git/config 将原来的 http://git.xxx.com/x ...

  5. Noip2015提高组解题报告

    Day1 T1神奇的幻方 一道简单异常的小模拟,我们只需要确定数字1的位置,然后根据题意枚举即可,简简单单就A了,什么也不卡. 然而这题,我刚开始学OI的时候,因为当时比较蠢,被这题花式吊打啊.... ...

  6. 01Trie【p4551(poj3764)】 最长异或路径

    题目描述 给定一棵 n 个点的带权树,结点下标从 1 开始到 N .寻找树中找两个结点,求最长的异或路径. 异或路径指的是指两个结点之间唯一路径上的所有边权的异或. 个人: 首先强推一下01字典树(T ...

  7. 叙Windows平台下基于MBR和UEFI的bootkit(一)--以MBR为例

    安全的对抗首先在权限方面,权限高的进程对权限低的权限就是就是降维打击,无往不利.当权限相同时,启动得早便为王.所谓的bootkit也就是基于这个思路设计的一种复杂病毒.它优先于Windows系统启动, ...

  8. 36、Flask实战第36天:客户端权限验证

    编辑cms_base.html <li><a href="#">{{ g.cms_user.username }}<span>[超级管理员]&l ...

  9. [NOIp2016提高组]换教室

    题目大意: 有n节课,第i节课在c[i]上课,同时d[i]也有一节课d[i]. 你有权利向教务处发出m次申请把自己的教室改到d[i],相应的批准概率是k[i]. 教室是图上的一些点,其中每条边都有边权 ...

  10. 记录numpy和c++的混合编程

    准备记录numpy和c++的混合编程 #include <boost/python.hpp> #include <numpy/ndarrayobject.h> namespac ...