UDP (User Datagram Protocol),全称为——用户数据报协议。UDP提供了一种无需建立连接就可以发送封装的IP数据包的方法。在OSI模型中处于传输层,IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。
以上内容参考与百度百科:UDP
本次Demo参考《Netty权威指南》中的内容,在书中的示例上稍作了一些修改。主要实现了:
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();
}
}
}
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();
}
}
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();
}
}
}
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类型也要改为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<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。