1 UDP 是什么?
UDP
(User Data Protocol,用户数据报协议),是与 TCP
相对应的协议。它是面向非连接
的协议,它不与对方建立连接,而是直接就把数据包发送过去。
2 UDP的特点
- 无连接
- 不可靠传输
- 不会对数据包进行合并:无粘包分包一说。
- 不会重发
- 主要用于查询-应答的服务:比如
要求高速传输、实时性较高的通信或广播通信
,NFS
,IM
,如视频、音频等多媒体通信
,限定于LAN 等特定网络中的应用通信
,广播通信
(广播、多播)。
3 与TCP的区别
- | TCP | UDP |
---|---|---|
是否连接 | 面向连接 | 面向非连接 |
传输可靠性 | 可靠 | 不可靠 |
应用场合 | 传输大量数据 | 少量数据 |
速度 | 慢 | 快 |
开销(时间,系统资源) | 大 | 小 |
4 UDP首部结构
UDP 首部字段只有 8 个字节,包括源端口
、目的端口
、长度
、检验和
。12 字节的伪首部
是为了计算检验和
临时添加的。
当运输层从IP层
收到UDP数据报
时,就根据首部
中的目的端口
,把UDP数据报通过相应的端口,上交最后的终点——应用进程
。
如果接收方UDP
发现收到的报文中的目的端口号不正确(即不存在对应于该端口号的应用进程),就丢弃
该报文,并由网际控制报文协议 ICMP 发送端口不可达
差错报文给发送方。
请注意,虽然在 UDP 之间的通信要用到其端口号,但由于 UDP 的通信是无连接的,因此不需要使用套接字(TCP 之间的通信必须要在两个套接字之间建立连接)。
4 基于Netty写一个UDP实例
场景很简单,A提问,“告诉我一句古诗”,B回应,“洛阳亲友如相问,一片冰心在玉壶。”
4.1 提问端
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
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;
import java.net.InetSocketAddress;
/**
* @author zyxelva
* 类说明:提问端
*/
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)
.channel(NioDatagramChannel.class)/*UDP通信*/
.handler(new QuestoinHandler());
Channel channel = b.bind(0).sync().channel();
channel.writeAndFlush(
new DatagramPacket(
Unpooled.copiedBuffer(QUESTION, CharsetUtil.UTF_8),
new InetSocketAddress("127.0.0.1", port)
)
).sync();
if (!channel.closeFuture().await(15000)) {
System.out.println("等待超时");
}
} catch (Exception e) {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new UdpQuestionSide().run(UdpAnswerSide.ANSWER_PORT);
}
}
4.2 提问端Handler
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;
/**
* @author zyxelva
* 类说明:提问端的Handler,读取服务器的应答
*/
public class QuestionHandler extends SimpleChannelInboundHandler<DatagramPacket> {
@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.3 应答端
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
/**
* @author zyxelva
* 类说明:应答端
*/
public class UdpAnswerSide {
public static final int ANSWER_PORT = 8080;
public final static String ANSWER = "古诗来了:";
public void run(int port) throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
/*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 {
new UdpAnswerSide().run(ANSWER_PORT);
}
}
4.4 应答端Handler
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 java.net.InetSocketAddress;
import java.util.Random;
/**
* @author zyxelva
* 类说明:应答Handler
*/
public class AnswerHandler extends SimpleChannelInboundHandler<DatagramPacket> {
/**
* 应答的具体内容从常量字符串数组中取得,由nextQuote方法随机获取
*/
private static final String[] DICTIONARY = {"只要功夫深,铁棒磨成针。", "旧时王谢堂前燕,飞入寻常百姓家。", "洛阳亲友如相问,一片冰心在玉壶。", "一寸光阴一寸金,寸金难买寸光阴。", "老骥伏枥,志在千里,烈士暮年,壮心不已"};
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();
}
}
4.5 结果
应答端:应答服务已启动…..
接收到请求:告诉我一句古诗
请求端:古诗来了:洛阳亲友如相问,一片冰心在玉壶。
下一章节,学习下服务器推送技术
以及websocket
。