【Netty学习】8.UDP协议


1 UDP 是什么?

UDP(User Data Protocol,用户数据报协议),是与 TCP 相对应的协议。它是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发送过去。

2 UDP的特点

  • 无连接
  • 不可靠传输
  • 不会对数据包进行合并:无粘包分包一说。
  • 不会重发
  • 主要用于查询-应答的服务:比如要求高速传输、实时性较高的通信或广播通信NFSIM如视频、音频等多媒体通信限定于LAN 等特定网络中的应用通信广播通信(广播、多播)。

3 与TCP的区别

-TCPUDP
是否连接面向连接面向非连接
传输可靠性可靠不可靠
应用场合传输大量数据少量数据
速度
开销(时间,系统资源)

4 UDP首部结构

UDP 首部字段只有 8 个字节,包括源端口目的端口长度检验和。12 字节的伪首部是为了计算检验和临时添加的。

UDP 首部

当运输层从IP层收到UDP数据报时,就根据首部中的目的端口,把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


文章作者: Kezade
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Kezade !
评论
  目录