Netty实战二-实现UDP的单播和广播(1)

public class AnswerHandler extends

SimpleChannelInboundHandler {

/应答的具体内容从常量字符串数组中取得,由nextQuote方法随机获取/

private static final String[] DICTIONARY = {

“一个男生暗恋一个女生很久了。一天自习课上,男生偷偷的传了小纸条给女生,上面写着“其实我注意你很久了”。不一会儿,女生传了另一张纸条,男生心急火燎的打开一看“拜托你不要告诉老师,我保证以后再也不嗑瓜子了”。。。。。。男生一脸懵逼”,

“昨天因为一件事骂儿子,说你妈妈是猪,你也是头猪。儿子却反过来说我:爸爸你怎么这么衰,娶了一头猪,还生了一只猪!你说你这熊孩子,这是不是找打。”,

“火云邪神苦练多年,终于将蛤蟆功练至顶级并成功产下8个小蝌蚪。”,

“老婆永远是对的,这话永远也是对的。但老婆没想到的是,她不一定永远是老婆”,

“人生天地间没有谁是容易的,就算是思聪也得每天犯愁怎么花钱。”,

“今天去理发,洗剪吹68,烫发和染发668。我就做了个洗剪吹,结账的时候发现居然收我66”};

private static Random r = new Random();

private String nextQuote(){

return DICTIONARY[r.nextInt(DICTIONARY.length-1)];

}

@Override

protected void channelRead0(ChannelHandlerContext ctx,

DatagramPacket packet)

throws Exception {

//获得请求

String req = packet.content().toString(CharsetUtil.UTF_8);

System.out.println(“接收到请求:”+req);

if(UdpQuestionSide.QUESTION.equals(req)){

String answer = UdpAnswerSide.ANSWER+nextQuote();

System.out.println(“接收到请求:”+req);

/**

  • 重新 new 一个DatagramPacket对象,我们通过packet.sender()来获取发送者的消息。重新发送出去!

*/

ctx.writeAndFlush(

new DatagramPacket(

Unpooled.copiedBuffer(

answer,

CharsetUtil.UTF_8),

packet.sender()));

}

}

@Override

public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)

throws Exception {

ctx.close();

cause.printStackTrace();

}

}

2、UdpAnswerSide

定义应答服务器

public final static String ANSWER = “笑话来了:”;

public void run(int port) throws Exception{

EventLoopGroup group = new NioEventLoopGroup();

try {

/*和tcp的不同,udp没有接受连接的说法,所以即使是接收端,

也使用Bootstrap*/

Bootstrap b = new Bootstrap();

/由于我们用的是UDP协议,所以要用NioDatagramChannel来创建/

b.group(group)

.channel(NioDatagramChannel.class)

.handler(new AnswerHandler());

//没有接受客户端连接的过程,监听本地端口即可

ChannelFuture f = b.bind(port).sync();

System.out.println(“应答服务已启动…”);

f.channel().closeFuture().sync();

} finally {

group.shutdownGracefully();

}

}

public static void main(String [] args) throws Exception{

int port = 8080;

new UdpAnswerSide().run(port);

}

3、QuestoinHandler

定义应答服务器处理handler

/**

  • 作者:DarkKIng

  • 类说明:订阅handler,读取服务器的应答

*/

public class QuestoinHandler extends

SimpleChannelInboundHandler {

@Override

protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg)

throws Exception {

//获得应答,DatagramPacket提供了content()方法取得报文的实际内容

String response = msg.content().toString(CharsetUtil.UTF_8);

if (response.startsWith(UdpAnswerSide.ANSWER)) {

System.out.println(response);

ctx.close();

}

}

@Override

public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)

throws Exception {

cause.printStackTrace();

ctx.close();

}

}

4、UdpQuestionSide

定义了一个请求客户端

/**

  • 作者:DarkKIng

  • 类说明:订阅服务器

*/

public class UdpQuestionSide {

public final static String QUESTION = “我想听个笑话”;

public void run(int port) throws Exception{

EventLoopGroup group = new NioEventLoopGroup();

try {

Bootstrap b = new Bootstrap();

b.group(group)

/由于我们用的是UDP协议,所以要用NioDatagramChannel来创建/

.channel(NioDatagramChannel.class)

.handler(new QuestoinHandler());

//不需要建立连接

Channel ch = b.bind(0).sync().channel();

//将UDP请求的报文以DatagramPacket打包发送给接受端

ch.writeAndFlush(

new DatagramPacket(

Unpooled.copiedBuffer(QUESTION,

CharsetUtil.UTF_8),

new InetSocketAddress(“127.0.0.1”,

port)))

.sync();

//不知道接收端能否收到报文,也不知道能否收到接收端的应答报文

// 所以等待15秒后,不再等待,关闭通信

if(!ch.closeFuture().await(15000)){

System.out.println(“查询超时!”);

}

} catch (Exception e) {

group.shutdownGracefully();

}

}

public static void main(String [] args) throws Exception{

int answerPort = 8080;

new UdpQuestionSide().run(answerPort);

}

}

5、程序演示

该程序主要实现了客户单向服务器单点请求一个笑话。服务器随机返回一个笑话。

开启应答服务

开启客户端发送请求

在开启一个客户端发送请求

四、Netty实现UDP广播

==============

1、LogConst

定义消息常量类,用来模拟日志

/**

  • 作者:DarkKIng

  • 类说明:日志信息,用String数组代替

*/

public class LogConst {

public final static int MONITOR_SIDE_PORT = 9998;

private static final String[] LOG_INFOS = {

“晨光微好,暖在夕阳。幽幽的巷子里,有着岁月酝酿的酒,愈久愈淳。一笔墨香,一盏明灯,记千帆过浪,数不尽的悲欢离合,待那水莲花开。”,

“未来无期,静在安好。一剪寒梅,纷扰了岁月,抚平了伤痕。摆动的双桨,拨动了心的潭水。陌上花开,落一地秋霜,红枫染了红尘,便许了你十里红装。”,

“离别的风,风干了月的泪。夜里的美”,

“是梦的呢喃低语,挥走一片彩云,段落成珠。拂袖离去,乘鹤而来,古道西风瘦马。斑驳的树影中,眉目如画的眼,轻语告别了往事如烟。”,

“无言的殇,几世沧桑,几生悲凉。一起剪了西窗烛,听了夜来风吹雨。昨日的叹息,今日的迷离,执一伞,存了一世一笔的温情。一曲长歌,唱尽了一世繁华,一世缘……”,

“一世恋书,那便十里花开。一生凄凉,那便霜花十里。” ,

“一抹浓烟,便翻页书,展颜一笑,是时间带来遥远的梦。细数树的年轮,感受昨日惆怅,留一半清醒,梦一半叶落。在指尖流过的沙,海边浪花一朵朵,不相遇,才有不约而同。”,

“这世俗,太多牵挂留在心间,一点朱砂泪,一曲相诗歌。岁月朦胧,梦醒了人生,风雨相容,演绎了一段风情。雪亦梦,雨亦梦,万张红纸从天洒来。惊动了山,撼动了天。” +

“一纸情愁,一指烟凉。一相思,一思量,水漫岸头,我们都有着自己不同的三生故事。迎一夜秋风,送一世暖阳,一切冰雪里的花开,是我一生的柔情。” +

“记忆中的短笛,有着清风须来的气息,那时我们面向大海,海风在耳边述说着大海边缘的温暖故事。安好一轮冷月,静好了一残红日,这便是我的语言,我的情丝。” +

“一漫山水,一段情,留在了岁月,拭去了风,晴雨清风,倒是暖阳拂绿草。” +

“这便,晨光微好,花开静好……”};

private final static Random r = new Random();

public static String getLogInfo(){

return LOG_INFOS[r.nextInt(LOG_INFOS.length-1)];

}

}

2、LogMsg

消息实体类

/**

  • 作者:DarkKIng

  • 类说明:日志实体类

*/

public final class LogMsg {

public static final byte SEPARATOR = (byte) ‘:’;

/源的 InetSocketAddress/

private final InetSocketAddress source;

/消息内容/

private final String msg;

/消息id/

private final long msgId;

/消息发送的时间/

private final long time;

//用于传入消息的构造函数

public LogMsg(String msg) {

this(null, msg,-1,System.currentTimeMillis());

}

//用于传出消息的构造函数

public LogMsg(InetSocketAddress source, long msgId,

String msg) {

this(source,msg,msgId,System.currentTimeMillis());

}

public LogMsg(InetSocketAddress source, String msg, long msgId, long time) {

this.source = source;

this.msg = msg;

this.msgId = msgId;

this.time = time;

}

//返回发送 LogMsg 的源的 InetSocketAddress

public InetSocketAddress getSource() {

return source;

}

//返回消息内容

public String getMsg() {

return msg;

}

//返回消息id

public long getMsgId() {

return msgId;

}

//返回消息中的时间

public long getTime() {

return time;

}

}

3、LogEventEncoder

日志编码类

/**

  • 作者:DarkKIng

  • 类说明:编码,将实际的日志实体类编码为DatagramPacket

*/

public class LogEventEncoder extends MessageToMessageEncoder {

private final InetSocketAddress remoteAddress;

//LogEventEncoder 创建了即将被发送到指定的 InetSocketAddress

// 的 DatagramPacket 消息

public LogEventEncoder(InetSocketAddress remoteAddress) {

this.remoteAddress = remoteAddress;

}

@Override

protected void encode(ChannelHandlerContext channelHandlerContext,

LogMsg logMsg, List out) throws Exception {

byte[] msg = logMsg.getMsg().getBytes(CharsetUtil.UTF_8);

//容量的计算:两个long型+消息的内容+分割符

ByteBuf buf = channelHandlerContext.alloc()

.buffer(8*2 + msg.length + 1);

//将发送时间写入到 ByteBuf中

buf.writeLong(logMsg.getTime());

//将消息id写入到 ByteBuf中

buf.writeLong(logMsg.getMsgId());

//添加一个 SEPARATOR

buf.writeByte(LogMsg.SEPARATOR);

//将日志消息写入 ByteBuf中

buf.writeBytes(msg);

//将一个拥有数据和目的地地址的新 DatagramPacket 添加到出站的消息列表中

out.add(new DatagramPacket(buf, remoteAddress));

}

}

4、LogEventBroadcaster

日志广播端

/**

  • 作者:DarkKIng

  • 类说明:日志的广播端

*/

public class LogEventBroadcaster {

private final EventLoopGroup group;

private final Bootstrap bootstrap;

public LogEventBroadcaster(InetSocketAddress remoteAddress) {

group = new NioEventLoopGroup();

bootstrap = new Bootstrap();

//引导该 NioDatagramChannel(无连接的)

bootstrap.group(group).channel(NioDatagramChannel.class)

//设置 SO_BROADCAST 套接字选项

.option(ChannelOption.SO_BROADCAST, true)

.handler(new LogEventEncoder(remoteAddress));

}

public void run() throws Exception {

//绑定 Channel

Channel ch = bootstrap.bind(0).sync().channel();

long count = 0;

//启动主处理循环,模拟日志发送

for (;😉 {

ch.writeAndFlush(new LogMsg(null, ++count,

LogConst.getLogInfo()));

try {

//休眠 2 秒,如果被中断,则退出循环;

Thread.sleep(2000);

} catch (InterruptedException e) {

Thread.interrupted();

break;

}

}

}

public void stop() {

group.shutdownGracefully();

}

public static void main(String[] args) throws Exception {

//创建并启动一个新的 UdpQuestionSide 的实例

LogEventBroadcaster broadcaster = new LogEventBroadcaster(

//表明本应用发送的报文并没有一个确定的目的地,也就是进行广播

new InetSocketAddress(“255.255.255.255”,

LogConst.MONITOR_SIDE_PORT));

try {

System.out.println(“广播服务启动”);

broadcaster.run();

}

finally {

broadcaster.stop();

}

}

}

5、LogEventDecoder

日志解码类,将DatagramPacket解码为实际的日志实体类

/**

  • 作者:DarkKIng

  • 类说明:解码,将DatagramPacket解码为实际的日志实体类

*/

public class LogEventDecoder extends MessageToMessageDecoder {

@Override

protected void decode(ChannelHandlerContext ctx,

DatagramPacket datagramPacket, List out)

throws Exception {

//获取对 DatagramPacket 中的数据(ByteBuf)的引用

ByteBuf data = datagramPacket.content();

long time = new Date().getTime();

System.out.println(time+" 接受到发送的消息:");

//获得消息的id

long msgId = data.readLong();

//获得分隔符SEPARATOR

byte sepa = data.readByte();

//获取读索引的当前位置,就是分隔符的索引+1

int idx = data.readerIndex();

//提取日志消息,从读索引开始,到最后为日志的信息

String sendMsg = data.slice(idx ,

data.readableBytes()).toString(CharsetUtil.UTF_8);

//构建一个新的 LogMsg 对象,并且将它添加到(已经解码的消息的)列表中

LogMsg event = new LogMsg(datagramPacket.sender(),

msgId, sendMsg);

//作为本handler的处理结果,交给后面的handler进行处理

out.add(event);

}

}

6、LogEventHandler

日志的业务处理类,实际的业务处理,接受日志信息

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
加到(已经解码的消息的)列表中

LogMsg event = new LogMsg(datagramPacket.sender(),

msgId, sendMsg);

//作为本handler的处理结果,交给后面的handler进行处理

out.add(event);

}

}

6、LogEventHandler

日志的业务处理类,实际的业务处理,接受日志信息

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-SvQfOKVd-1715854387031)]

[外链图片转存中…(img-6y6UhwwY-1715854387032)]

[外链图片转存中…(img-0ZA6ai0P-1715854387032)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值