- 浏览: 5101612 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
silence19841230:
先拿走看看
SpringBoot2.0开发WebSocket应用完整示例 -
wallimn:
masuweng 写道发下源码下载地址吧!三个相关文件打了个包 ...
SpringBoot2.0开发WebSocket应用完整示例 -
masuweng:
发下源码下载地址吧!
SpringBoot2.0开发WebSocket应用完整示例 -
masuweng:
SpringBoot2.0开发WebSocket应用完整示例 -
wallimn:
水淼火 写道你好,我使用以后,图标不显示,应该怎么引用呢,谢谢 ...
前端框架iviewui使用示例之菜单+多Tab页布局
部分UDP通信场景中,需要客户端定期发送心跳信息,以获取终端的状态,并获取终端IP,以便服务器主动发送控制命令。如移动通信,内网穿越等。
使用TCP方式通信,心跳是比较容易实现的,使用IdleStateHandler监控channel,然后在自定义的Handler中处理几个对应的事件就可以了。但是对于UDP,就不灵了。
学习研究netty,做了一个简单而完善的例子:通过UDP通信,客户端上线,发送一条信息,服务器响应(不在Handler中响应,在其他线程中处理)。服务器主动向客户端发问候消息,监控到无心跳后,踢掉客户端。
程序逻辑比较简单,不多解释,请看注释。
一、辅助类
二、客户端代码
三、服务器端代码
使用TCP方式通信,心跳是比较容易实现的,使用IdleStateHandler监控channel,然后在自定义的Handler中处理几个对应的事件就可以了。但是对于UDP,就不灵了。
学习研究netty,做了一个简单而完善的例子:通过UDP通信,客户端上线,发送一条信息,服务器响应(不在Handler中响应,在其他线程中处理)。服务器主动向客户端发问候消息,监控到无心跳后,踢掉客户端。
程序逻辑比较简单,不多解释,请看注释。
一、辅助类
package com.wallimn.iteye.netty.heart; import java.net.InetSocketAddress; import io.netty.util.AttributeKey; /** * 记录一些常量。真正的应用要从配置文件中读取。 * * <br> * <br>时间:2019年9月14日 下午11:41:26,作者:wallimn */ public class Config { private Config(){}; public static final AttributeKey<InetSocketAddress> SERVER_ADDR_ATTR=AttributeKey.newInstance("SERVER_ADDR_ATTR"); //原来打算将客户端的ID记录在Channel的属性中,后来发现对于UDP不适用。 //public static final AttributeKey<String> CLIENT_ID=AttributeKey.newInstance("CLIENT_ID"); public static final int IDLE_TIME=5;//允许的发呆时间 public static final int SERVER_PORT=8585; public static final String SERVER_IP="localhost"; public static final long CLIENT_VALID_THRESHOLD=5000;//客户端地址有效的时间阀值。单位为毫秒。 }
package com.wallimn.iteye.netty.heart; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; /** * 用来模拟持有数据 * * <br> * <br>时间:2019年9月14日 下午7:58:40,作者:wallimn */ public class DataHolder { private DataHolder(){} /** * 记录客户端的消息 */ public static ConcurrentLinkedQueue<ClientMessage> clientMessageQueue = new ConcurrentLinkedQueue<ClientMessage>(); /** * 记录由心跳获取的客户端地址,用于服务器主动给客户端发消息 */ public static ConcurrentMap<String, ClientInformation> clientInformationMap = new ConcurrentHashMap<String, ClientInformation>(); }
package com.wallimn.iteye.netty.heart; import java.net.InetSocketAddress; import java.util.Date; import lombok.Data; /** * 客户端信息 * * <br> * <br>时间:2019年9月14日 下午8:55:36,作者:wallimn */ @Data public class ClientInformation { /** * 客户端唯一标识 */ private String id; /** * 收到时间 */ private Date recordTime; /** * 客户端地址, */ private InetSocketAddress address; }
package com.wallimn.iteye.netty.heart; import lombok.Data; /** * 客户端发来的消息 * * <br> * <br>时间:2019年9月14日 下午8:55:36,作者:wallimn */ @Data public class ClientMessage { /** * 消息 */ private String message; /** * 客户端信息 */ private ClientInformation client; }
二、客户端代码
package com.wallimn.iteye.netty.heart; import java.net.InetSocketAddress; import java.util.UUID; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.socket.DatagramPacket; import io.netty.util.CharsetUtil; /** * 客户端处理器(handler),并无太多逻辑。仅发了一条访问时间的信息(time)、读取服务器信息并显示。 * * <br> * <br>时间:2019年9月14日 下午9:09:47,作者:wallimn */ public class ClientHandler extends SimpleChannelInboundHandler<DatagramPacket> { @Override protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception { String msg = packet.content().toString(CharsetUtil.UTF_8); System.out.println(msg); //如果收到exit信息,关闭channel if("exit".equals(msg)){ ctx.close(); } } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { InetSocketAddress addr = ctx.channel().attr(Config.SERVER_ADDR_ATTR).get(); String clientId; String message; //发送1条心跳 clientId = UUID.randomUUID().toString().toUpperCase().replace("-", ""); // message = clientId+";"+"heart";//发送的信息 // ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(message,CharsetUtil.UTF_8),addr)); // System.out.println("发送一条心跳");//不用专门发心跳信息,任何发到服务器的信息都可以用于服务器更新心跳记录 message = clientId+";"+"time";//发送的信息 ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(message,CharsetUtil.UTF_8),addr)); System.out.println("发送对时信息"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
package com.wallimn.iteye.netty.heart; import java.net.InetSocketAddress; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioDatagramChannel; /** * 客户端程序 * 启动命令:java -classpath .;netty-all-4.1.38.Final.jar com.wallimn.iteye.netty.heart.ClientApp * * <br> * <br>时间:2019年9月14日 上午8:58:13,作者:wallimn */ public class ClientApp { public static void main(String[] args) { int port = Config.SERVER_PORT; new ClientApp().run(port); } public void run(int port){ EventLoopGroup group = new NioEventLoopGroup(); Bootstrap b = new Bootstrap(); b.group(group) .channel(NioDatagramChannel.class) //.option(ChannelOption.SO_BROADCAST, true) .handler(new ClientHandler()); InetSocketAddress addr = new InetSocketAddress(Config.SERVER_IP,port); b.attr(Config.SERVER_ADDR_ATTR, addr); try { Channel ch = b.bind(0).sync().channel();//使用一个随机端口 //最长运行30秒 if(!ch.closeFuture().await(30000)){ System.out.println("操作超时"); } System.out.println("退出"); } catch (InterruptedException e) { e.printStackTrace(); } finally{ group.shutdownGracefully(); } } }
三、服务器端代码
package com.wallimn.iteye.netty.heart; import java.util.Date; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.socket.DatagramPacket; import io.netty.util.CharsetUtil; /** * 服务器处理器(handler) * * <br> * <br>时间:2019年9月15日 上午8:37:15,作者:wallimn */ public class ServerHandler extends SimpleChannelInboundHandler<DatagramPacket> { @Override protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception { System.out.println("读取信息,channelShortId="+ctx.channel().id().asShortText()); String msg = packet.content().toString(CharsetUtil.UTF_8); System.out.println("host: " + packet.sender().getHostString()); System.out.println("port: " + packet.sender().getPort()); System.out.println("content: " + msg); String[] fields = msg.split(";"); if (fields.length != 2) { return; } ClientInformation client = new ClientInformation(); client.setId(fields[0]); client.setRecordTime(new Date()); client.setAddress(packet.sender()); ClientMessage message = new ClientMessage(); message.setClient(client); message.setMessage(fields[1]); System.out.println("加入待处理数据队列"); //标注客户端的ID // 不对消息进行处理,只是加入队列,由其他线程进行处理 if(!"heart".equals(message.getMessage())){//如果不是心跳消息 DataHolder.clientMessageQueue.add(message); } //不管什么消息,更新客户端的信息 DataHolder.clientInformationMap.put(client.getId(), client); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { super.exceptionCaught(ctx, cause); } //这个方法对于UDP没有什么意义 // @Override // public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { // if (evt instanceof IdleStateEvent) { // IdleStateEvent e = (IdleStateEvent) evt; // switch (e.state()) { // case READER_IDLE: // System.out.println("READER_IDLE"); // break; // case WRITER_IDLE: // System.out.println("WRITER_IDLE"); // break; // case ALL_IDLE: // System.out.println("ALL_IDLE"); // break; // default: // break; // } // } // } }
package com.wallimn.iteye.netty.heart; import java.util.Date; import java.util.Iterator; import java.util.Map.Entry; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.util.CharsetUtil; import io.netty.util.HashedWheelTimer; import io.netty.util.Timeout; import io.netty.util.TimerTask; /** * 服务器应用 * 启动命令:java -classpath .;netty-all-4.1.38.Final.jar com.wallimn.iteye.netty.heart.ServerApp * <br> * <br> * 时间:2019年9月14日 上午9:47:29,作者:wallimn */ public class ServerApp { //public static ConcurrentMap<String, ClientMessage> clientMessageMap = new ConcurrentHashMap<String, ClientMessage>(); // 内部放置多个Task, public static HashedWheelTimer timer = new HashedWheelTimer(Executors.defaultThreadFactory(), 1, // tick一下的时间 TimeUnit.SECONDS, 3);// 放置Timer的数量 public static Channel channel = null; public static final long RESPONSE_TIMEER_DELAY = 1L; public static final long CHECK_TIMEER_DELAY = 5L; public static final long HELLO_TIMEER_DELAY = 2L; /** * 处理客户端发来的消息 */ public static TimerTask responseTasker = new TimerTask() { public void run(Timeout timeout) throws Exception { timer.newTimeout(this, RESPONSE_TIMEER_DELAY, TimeUnit.SECONDS); if (channel == null){ System.out.println("channel is null"); return; } if (channel.isActive() == false) { System.out.println("channel is inactive"); timeout.cancel(); return; } //System.out.println("responseTasker run, size is "+DataHolder.clientMessageQueue.size()); ClientMessage message; for (Iterator<ClientMessage> iterator=DataHolder.clientMessageQueue.iterator();iterator.hasNext();) { message = iterator.next(); System.out.println(message.getMessage()); if("time".equals(message.getMessage())){ channel.writeAndFlush( new DatagramPacket(Unpooled.copiedBuffer("18:18", CharsetUtil.UTF_8), message.getClient().getAddress())); } else{ ; } //处理完后清除 iterator.remove(); } } }; /** * 用于检查客户端是否有效 */ public static TimerTask checkTasker = new TimerTask() { public void run(Timeout timeout) throws Exception { timer.newTimeout(this, CHECK_TIMEER_DELAY, TimeUnit.SECONDS); ClientInformation client = null; long now = new Date().getTime(); for (Entry<String, ClientInformation> entry : DataHolder.clientInformationMap.entrySet()) { client = entry.getValue(); if (now - client.getRecordTime().getTime() > Config.CLIENT_VALID_THRESHOLD) { System.out.println("client kick : " + client.getId()); DataHolder.clientInformationMap.remove(entry.getKey()); } } } }; /** * 用于模拟主动向客户端发送消息 */ public static TimerTask helloTimer = new TimerTask(){ public void run(Timeout timeout) throws Exception { if (channel == null){ System.out.println("channel is null"); return; } if (channel.isActive() == false) { System.out.println("channel is inactive"); timeout.cancel(); return; } timer.newTimeout(this, HELLO_TIMEER_DELAY, TimeUnit.SECONDS); ClientInformation client = null; for (Entry<String, ClientInformation> entry : DataHolder.clientInformationMap.entrySet()) { client = entry.getValue(); System.out.println("helloTimer run. send to "+client.getId()); channel.writeAndFlush( new DatagramPacket(Unpooled.copiedBuffer("hello", CharsetUtil.UTF_8), client.getAddress())); } } }; public static void main(String[] args) throws Exception { int port = Config.SERVER_PORT; timer.newTimeout(responseTasker, RESPONSE_TIMEER_DELAY, TimeUnit.SECONDS); timer.newTimeout(checkTasker, CHECK_TIMEER_DELAY, TimeUnit.SECONDS); timer.newTimeout(helloTimer, HELLO_TIMEER_DELAY, TimeUnit.SECONDS); new ServerApp().run(port); } public void run(int port) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); Bootstrap b = new Bootstrap(); b.group(group).channel(NioDatagramChannel.class) // .option(ChannelOption.SO_BROADCAST, true) .handler(new ChannelInitializer<Channel>(){ @Override protected void initChannel(Channel ch) throws Exception { //ch.pipeline().addLast(new IdleStateHandler(5, 0, 0)); ch.pipeline().addLast(new ServerHandler()); } }); ChannelFuture future = b.bind(port).sync(); channel = future.channel(); System.out.println("服务器准备就绪,channelShortId="+channel.id().asShortText()); channel.closeFuture().await(); group.shutdownGracefully(); } }
发表评论
-
gradle编译错误:Could not find method compile() for arguments
2020-09-19 10:50 18185编译(IDEA+Gradle)一个别人的工程,出现一个 ... -
解决tomcat部署两个SpringBoot应用提示InstanceAlreadyExistsException
2019-06-30 11:49 3168两个SpringBoot应用部署在一个Tomcat中,单独 ... -
Eclipse配置MyBatis代码自动化功能
2019-06-29 10:16 16401.安装插件 Eclipse中,Help->Ecli ... -
vue.js中使用qrcode生成二维码
2019-05-20 00:00 7560一、安装包 npm install qrcodejs2 --s ... -
MySQL插入数据报错: Incorrect string value: '\xFD\xDE'
2019-03-31 23:19 1173我MySQL数据库用的uft-8字符集,插入数据一直很正常 ... -
vue自定义组件并双向绑定属性
2019-03-08 22:46 3209做了两个子组件,原理基本一样,一个是使用原生的select ... -
vue-router简单示例
2019-03-05 00:32 1089写个基本完整、稍有借鉴意义的示例,防止自己忘记。 &l ... -
“联通充值系统繁忙”轻松应对
2019-02-06 11:03 3905大过年的,联通充个值一直报“充值系统繁忙”。昨天晚上试了几 ... -
electron.js数据库应用---导航菜单(element-ui+mysql)
2019-02-05 21:33 2296一、环境搭建 略, ... -
electron.js数据库应用---入门(mysql+element-ui)
2019-01-27 23:19 7304我的机器:Windows10,64 ... -
SpringMVC 在controller层中注入成员变量request,是否线程安全
2018-12-17 21:17 2691@RestController public class ... -
VueJS 组件参数名命名与组件属性转化
2018-12-03 00:00 2005转自:https://www.cnblogs.com/meiy ... -
vue-resource拦截器实现token发送及检验自动化
2018-11-16 22:38 3041用了很长时间vue-resource,最近思考$http发 ... -
element-ui试用手记
2018-10-29 20:25 1684element-ui、iviewui都以vue.js为基础 ... -
iviewui中表格控件中render的使用示例
2018-07-07 16:46 9712示例了如何在表格中显示按钮,如何将代码转化为文字。 i ... -
Tomcat错误“Alias name tomcat does not identify a key entry”解决
2018-07-05 21:39 6179申请到了阿里云的证书后,下载、按照说明生成jks格式证书、 ... -
阿里云免费证书“fileauth.txt内容配置错误”解决
2018-07-05 20:43 5196最近研究微信小程序开发,上阿里云申请了个证书,使用文件验证 ... -
springboot2.0跨域配置
2018-07-04 22:11 5235springboot2.0跨域配置: 一、代码 ... -
微信小程序使用code换openid的方法(JAVA、SpringBoot)
2018-07-01 21:52 10293微信小程序序的代码中提示,使用code换取openid,但 ... -
SpringBoot2.0启用https协议
2018-06-28 23:00 7685SpringBoot2.0之后,启用https协议的方式与 ...
相关推荐
springboot整合 netty做心跳检测 springboot整合 netty做心跳检测 springboot整合 netty做心跳检测 springboot整合 netty做心跳检测 springboot整合 netty做心跳检测 springboot整合 netty做心跳检测 springboot整合...
spring整合netty心跳检测,spring整合netty心跳检测,spring整合netty心跳检测,spring整合netty心跳检测
Java异步NIO框架Netty实现高性能高并发,通过netty搭建TCP、UDP服务,支持物联网设备上行,下行
这个小程序使用netty5进行udp网络通讯,客户端有两种,1:用netty5类库发送DatagramPacket和接收 2:直接使用DatagramSocket发送接收DatagramPacket 先运行netty_server的QuoteOfTheMomentServer, 在运行netty_...
netty案例,netty4.1中级拓展篇八《Netty心跳服务与断线重连》源码 https://mp.weixin.qq.com/s?__biz=MzIxMDAwMDAxMw==&mid=2650724845&idx=1&sn=8631c590ff4876ba0b7af64df16fc54b&scene=19#wechat_redirect
基于netty 的udp字节数据接 收服务,发送服务实例 基于netty 的udp字节数据接收服务,发送服务实例
基于netty的心跳检测技术,测试过可以在java端和android上使用
本源码是《NIO框架入门(三):iOS与MINA2、Netty4的跨平台UDP双向通信实战》一文的服务端源码实现(Netty4版),详见:http://www.52im.net/thread-378-1-1.html
Netty心跳检测机制
非常简单明了的netty心跳检测代码,标准的eclipse工程代码,导入后即可运行。由于代码很精简,可以轻松学会Netty心跳检测的原理
这是更具netty的一个demo自己再修改一下 有问题可以联系我
java实现基于netty 的utp字节数据接收服务,服务具体实现代码。样例java实现基于netty 的utp字节数据接收服务,服务具体实现代码。样例
netty 心跳实现
netty案例,netty4.1基础入门篇十一《netty udp通信方式案例Demo》源码 https://mp.weixin.qq.com/s?__biz=MzIxMDAwMDAxMw==&mid=2650724927&idx=1&sn=a16bc8e98d6a27816da0896adcc83778&scene=19#wechat_redirect
SCANFISH-II 型声呐系统数据接口协议,对接tcp转发app,json封装
断电断网的心跳检测,完美解决了websocket断电断网之后服务端不能收到关闭的通知,倒置客户端不能收到信息
面试官:Netty心跳检测机制是什么,怎么自定义检测间隔时间?.doc
java的netty实现的可靠udp网络库
netty心跳连接代码
本代码利用UDP协议查询谚语的简单功能。