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

什么是UDP协议?

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

运行截图

Netty学习——UDP接收与发送的简单使用_第2张图片

Netty学习——UDP接收与发送的简单使用_第3张图片

你可能感兴趣的:(Netty,udp,学习,网络协议)