Netty学习——UDP接收与发送的简单使用

Netty学习——UDP接收与发送的简单使用

什么是UDP协议?

UDP (User Datagram Protocol),全称为——用户数据报协议。UDP提供了一种无需建立连接就可以发送封装的IP数据包的方法。在OSI模型中处于传输层,IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。 以上内容参考与百度百科:UDP

Netty实现UDP服务端与客户端

本次Demo参考《Netty权威指南》中的内容,在书中的示例上稍作了一些修改。主要实现了:

客户端向服务端发送“成语”或“谚语”时,服务端会随机生成对应的成语和谚语返回给客户端。服务端如果接收到除“谚语”和“成语”的其他字符串则发送“请发送‘谚语’或‘成语’”的提示语。客户端使用控制台可多次输入发送的内容

服务端代码

UdpServer.java

package server;

import io.netty.bootstrap.Bootstrap;

import io.netty.channel.ChannelOption;

import io.netty.channel.EventLoopGroup;

import io.netty.channel.nio.NioEventLoopGroup;

import io.netty.channel.socket.nio.NioDatagramChannel;

/**

* UDP Server

*

* @author 胡海龙

*

*/

public class UdpServer {

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

EventLoopGroup group = new NioEventLoopGroup();

try {

Bootstrap b = new Bootstrap();

b.group(group).channel(NioDatagramChannel.class).option(ChannelOption.SO_BROADCAST, true)

.handler(new UdpServerHandler());

b.bind(8080).sync().channel().closeFuture().sync();

} finally {

group.shutdownGracefully();

}

}

}

UdpServerHandler.java

package server;

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;

import io.netty.util.internal.ThreadLocalRandom;

/**

* UDP Server Handler Class

*

* @author 胡海龙

*

*/

public class UdpServerHandler extends SimpleChannelInboundHandler {

private static final String[] proverbs = { "只要功夫深,铁棒磨成针。", "旧时王谢堂前燕,飞入寻常百姓家。", "洛阳亲友如相问,一片冰心在玉壶。",

"一寸光阴一寸金,寸金难买寸光阴。", "老骥伏枥,志在千里。烈士暮年,壮心不已!" };

private static final String[] idioms = { "马到成功", "狐假虎威", "虎头虎脑", "生龙活虎", "如雷贯耳", "持之以恒" };

/**

* 随机返回谚语

*/

private String nextProverb() {

int nextInt = ThreadLocalRandom.current().nextInt(proverbs.length);

return proverbs[nextInt];

}

/**

* 随机返回成语

*/

private String nextIdiom() {

int nextInt = ThreadLocalRandom.current().nextInt(idioms.length);

return idioms[nextInt];

}

@Override

protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {

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

System.out.println("服务端接收到的消息:" + message);

String sendMessage;

if ("谚语".equals(message)) {

sendMessage = nextProverb();

} else if ("成语".equals(message)) {

sendMessage = nextIdiom();

} else {

sendMessage = "请发送:“谚语”或者“成语”";

}

ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(sendMessage, CharsetUtil.UTF_8), msg.sender()));

}

@Override

public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

cause.printStackTrace();

ctx.close();

}

}

客户端代码

UdpClient.java

package server;

import java.net.InetSocketAddress;

import java.util.Scanner;

import io.netty.bootstrap.Bootstrap;

import io.netty.buffer.Unpooled;

import io.netty.channel.Channel;

import io.netty.channel.ChannelOption;

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;

/**

* UDP Client

*

* @author 胡海龙

*

*/

public class UdpClient {

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

EventLoopGroup group = new NioEventLoopGroup();

try {

Bootstrap b = new Bootstrap();

b.group(group).channel(NioDatagramChannel.class).option(ChannelOption.SO_BROADCAST, true)

.handler(new UdpClientHandler());

Channel channel = b.bind(8081).sync().channel();

Scanner sc = new Scanner(System.in);

while (sc.hasNext()) {

String sendMessage = sc.next();

if ("quit".equals(sendMessage)) {

break;

}

// 像网段内的所有广播机广播UDP消息

channel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(sendMessage, CharsetUtil.UTF_8),

new InetSocketAddress("255.255.255.255", 8080)));

}

sc.close();

channel.close();

} finally {

group.shutdownGracefully();

}

}

}

UdpClientHandler.java

package server;

import io.netty.channel.ChannelHandlerContext;

import io.netty.channel.SimpleChannelInboundHandler;

import io.netty.channel.socket.DatagramPacket;

import io.netty.util.CharsetUtil;

/**

* UDP Client Handler Class

*

* @author 胡海龙

*

*/

public class UdpClientHandler extends SimpleChannelInboundHandler {

@Override

protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {

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

System.out.println(receiveMessage);

}

@Override

public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

cause.printStackTrace();

ctx.close();

}

}

代码解析

由于UDP不需要建立连接,所以只需要一个EventLoopGroup就可以,对应的channel类型也要改为NioDatagramChannel,SO_BROADCAST设置选项表示允许发送广播消息。在处理类中使用了SimpleChannelInboundHandler这个类,它时继承于ChannelInboundHandlerAdapter,它必须要实现的时channelRead0这个方法。源码如下;

package io.netty.channel;

import io.netty.util.ReferenceCountUtil;

import io.netty.util.internal.TypeParameterMatcher;

public abstract class SimpleChannelInboundHandler extends ChannelInboundHandlerAdapter {

private final TypeParameterMatcher matcher;

private final boolean autoRelease;

protected SimpleChannelInboundHandler() {

this(true);

}

protected SimpleChannelInboundHandler(boolean autoRelease) {

matcher = TypeParameterMatcher.find(this, SimpleChannelInboundHandler.class, "I");

this.autoRelease = autoRelease;

}

protected SimpleChannelInboundHandler(Class inboundMessageType) {

this(inboundMessageType, true);

}

protected SimpleChannelInboundHandler(Class inboundMessageType, boolean autoRelease) {

matcher = TypeParameterMatcher.get(inboundMessageType);

this.autoRelease = autoRelease;

}

/**

* 如果应该处理给定的消息则返回true,如果为false则会传递给到下一个处理类

* */

public boolean acceptInboundMessage(Object msg) throws Exception {

return matcher.match(msg);

}

/**

* 覆盖ChannelInboundHandlerAdapter 类的channelRead方法,

* */

@Override

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

boolean release = true;

try {

if (acceptInboundMessage(msg)) {

@SuppressWarnings("unchecked")

I imsg = (I) msg;

channelRead0(ctx, imsg);

} else { //被释放

release = false;

ctx.fireChannelRead(msg);

}

} finally {

if (autoRelease && release) {

ReferenceCountUtil.release(msg);

}

}

}

protected abstract void channelRead0(ChannelHandlerContext ctx, I msg) throws Exception;

}

UDP服务端和客户端的代码大致相同,只有在客户端有一点区别。客户端中使用广播地址对该网段的所有机器发送消息,当然也可以改为指定机器的IP。

运行截图

🎨 相关创意作品

王李丹妮的电影 王李丹妮演过哪些电影(30部)
哪个才是365官网

王李丹妮的电影 王李丹妮演过哪些电影(30部)

📅 07-10 👁️ 2170
PPT放映中如何取消?如何退出放映模式?
365bet线路检测

PPT放映中如何取消?如何退出放映模式?

📅 07-28 👁️ 7974
马竞出征世俱杯30人名单:阿尔瓦雷斯、格列兹曼、朗格莱在列