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

什么是UDP协议?

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

	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<DatagramPacket> {

	@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类型也要改为NioDatagramChannelSO_BROADCAST设置选项表示允许发送广播消息。在处理类中使用了SimpleChannelInboundHandler这个类,它时继承于ChannelInboundHandlerAdapter,它必须要实现的时channelRead0这个方法。源码如下;

package io.netty.channel;

import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.TypeParameterMatcher;
public abstract class SimpleChannelInboundHandler<I> 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<? extends I> inboundMessageType) {
        this(inboundMessageType, true);
    }
    
    protected SimpleChannelInboundHandler(Class<? extends I> 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。

运行截图

在这里插入图片描述

在这里插入图片描述

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Netty可以很方便地用于UDP转发。UDP是一种无连接的协议,它不像TCP那样有连接状态和可靠性保证。但是,Netty提供了一些功能来帮助我们处理UDP数据报的转发。 首先,我们需要创建一个`Bootstrap`对象来配置和启动我们的Netty应用程序。然后,我们需要指定一个`ChannelInitializer`,用于设置管道的处理程序。在这个初始化器中,我们可以添加一个自定义的处理程序来处理UDP数据报。 下面是一个简单的示例代码,演示了如何使用Netty进行UDP转发: ```java import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.nio.NioDatagramChannel; public class UdpForwarder { public static void main(String[] args) { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioDatagramChannel.class) .option(ChannelOption.SO_BROADCAST, true) .handler(new ChannelInitializer<DatagramChannel>() { @Override protected void initChannel(DatagramChannel channel) throws Exception { channel.pipeline().addLast(new UdpForwardHandler()); } }); ChannelFuture future = bootstrap.bind(8888).sync(); future.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { group.shutdownGracefully(); } } } ``` 在上面的示例中,我们创建了一个`NioEventLoopGroup`来处理事件,然后创建了一个`Bootstrap`对象并配置了一些选项,如频道类型和广播选项。在初始化器中,我们添加了一个自定义的处理程序`UdpForwardHandler`,用于处理UDP数据报。 你可以根据自己的需求在`UdpForwardHandler`中编写自定义的转发逻辑。这个处理程序会接收到传入的UDP数据报,并根据你的逻辑进行转发或处理。 需要注意的是,UDP是一种无连接的协议,所以你需要自己处理数据包的可靠性和顺序问题。Netty只提供了一些基本的UDP支持,具体的转发逻辑需要根据你的业务需求进行实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@胡海龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值