【基于Netty实现Http通信、实现UDP单播和广播通信、代码案例实战学习】

netty构建udp服务器以及发送报文到客户端客户端详细案例 以上代码中用户判断是window还是linux环境,linux环境默认采用Epoll相关通道,所以显式设置通道。要使 通过服务器端通过要使 通过服务器端通过。 阅读详情

一.知识回顾

【0.Netty相关的知识专栏都帮你整理好了,根据自己的情况,自行选择学习,点击我即可快速跳转】
【1.初识Netty&使用Netty实现简单的客户端与服务端的通信操作&Netty框架中一些重要的类以及方法的解析】

二.基于Netty实现Http通信

2.1 Netty内置的编解码器和ChannelHandler

Netty 为许多通用协议提供了编解码器和处理器,几乎可以开箱即用,减少了我们在繁琐的事务上花费的时间。

2.2 Netty中HTTP请求通信

  1. HTTP 是基于请求/响应模式的:客户端向服务器发送一个HTTP 请求,然后服务器将会返回一个HTTP 响应。Netty 提供了多种编码器和解码器以简化对这个协议的使用。
  2. 一个HTTP 请求/响应可能由多个数据部分组成,DefaultFullHttpRequest和DefaultFullHttpResponse消息是特殊的子类型,分别代表了完整的请求和响应。

2.3 案例演示

客户端

将我们的浏览器作为客户端,发送消息,此处我们就写服务端的代码:

服务端
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpServerExpectContinueHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

/**
 * An HTTP server that sends back the content of the received HTTP request
 * in a pretty plaintext form.
 */
public final class HttpHelloWorldServer {

    public static void main(String[] args) throws Exception {
        //主从多线程Reactor模式
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ChannelInitializer() {
                 @Override
                 protected void initChannel(Channel ch) throws Exception {
                     ChannelPipeline p = ch.pipeline();
                     p.addLast(new HttpServerCodec()); //netty针对http编解码的处理类
                     p.addLast(new HttpServerExpectContinueHandler());//netty针对http编解码的处理类
                     p.addLast(new HttpHelloWorldServerHandler());//自己的业务处理逻辑
                 }
             });

            Channel ch = b.bind(8080).sync().channel();

            System.err.println("Open your web browser and navigate to " + "http://127.0.0.1:8080");

            ch.closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

服务端自己定义的业务处理逻辑
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpRequest;

import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpHeaderValues.TEXT_PLAIN;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;

public class HttpHelloWorldServerHandler extends SimpleChannelInboundHandler<HttpObject> {

    private static final byte[] CONTENT = "Hello Netty!".getBytes();

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) {
        if (msg instanceof HttpRequest) {
            HttpRequest req = (HttpRequest) msg;

            FullHttpResponse response = new DefaultFullHttpResponse(req.protocolVersion(), OK,
                                                                    Unpooled.wrappedBuffer(CONTENT));
            response.headers()
                    .set(CONTENT_TYPE, TEXT_PLAIN)
                    .setInt(CONTENT_LENGTH, response.content().readableBytes());

            ChannelFuture f = ctx.write(response);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

2.4 运行结果

在这里插入图片描述

在这里插入图片描述

三.基于Netty实现UDP单播和广播通信

3.0 补充UDP相关知识

UDP协议
  • 面向无连接的通讯协议。
  • 通讯时不需要接收方确认,属于不可靠的传输。
  • 因为不需要建立连接,所以传输速度快,但是容易丢失数据。
UDP报文组成

UDP是面向无连接的通讯协议,UDP报头由4个域组成,其中每个域各占用2个字节,其中包括目的端口号和源端口号信息,数据报的长度域是指包括报头和数据部分在内的总字节数,校验值域来保证数据的安全。由于通讯不需要连接,所以可以实现广播发送。
在这里插入图片描述

源端口:源端口号,在需要对方回信时选用,不需要时可用全0。

目的端口:目的端口号,这在终点交付报文时必须要使用到。

长度:UDP用户数据包的长度,其最小值是8(仅有首部)。

校验和:检测UDP用户数据报在传输中是否有错,有错就丢弃。

3.1 基于Netty实现UDP单播通信

单播的传输模式:定义为发送消息给一个由唯一的地址所标识的单一的网络目的地

同学端代码
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;

/**
 * 类说明:学生端
 */
public class UdpAnswerSide {

    public final static String ANSWER = "老师,我,我,我,我想!!!";

    public void run(int port) throws Exception{
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            /*和tcp的不同,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("同学们等待老师的提问:。。。。。");
            //System.out.println("请问同学们谁想成为架构师?.....");
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
    public static void main(String [] args) throws Exception{
        int port = 8080;
        new UdpAnswerSide().run(port);
    }
}

同学端自定义的处理逻辑
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.util.Random;

/**
 * 类说明:应答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);
        if(UdpQuestionSide.QUESTION.equals(req)){
            String name=nextQuote();
            String answer = UdpAnswerSide.ANSWER+name;
            System.out.println(name+"同学回答:"+answer);
            /**
             * 重新 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();
    }
}

老师端代码
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;

/**
 * 类说明:老师端
 */
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)
                    /*由于我们用的是UDP协议,所以要用NioDatagramChannel来创建*/
                    .channel(NioDatagramChannel.class)
                    .handler(new QuestoinHandler());
            //不需要建立连接
            Channel ch = b.bind(0).sync().channel();
            //将UDP请求的报文以DatagramPacket打包发送给接受端
            System.out.println(QUESTION);
            ch.writeAndFlush(
                    new DatagramPacket(
                            Unpooled.copiedBuffer(QUESTION,
                                    CharsetUtil.UTF_8),
                            new InetSocketAddress("127.0.0.1",
                                    port)))
                    .sync();
            //不知道接收端能否收到报文,也不知道能否收到接收端的应答报文
            // 所以等待15秒后,不再等待,关闭通信
            if(!ch.closeFuture().await(30000)){
                System.out.println("无人回答!");
            }
        } catch (Exception e) {
            group.shutdownGracefully();
        }finally {
            group.shutdownGracefully();
        }
    }
    public static void main(String [] args) throws Exception{
        int answerPort = 8080;

        new UdpQuestionSide().run(answerPort);
    }
}

服务端业务逻辑处理类
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;

/**
 * 类说明:提问端的Handler,读取服务器的应答
 */
public class QuestoinHandler 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);
            System.out.println("接收到同学回答:"+response);
            ctx.close();
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

运行结果

同学端
在这里插入图片描述
教师端
在这里插入图片描述

3.2 基于Netty实现UDP广播通信

特别说明:案例来自马士兵教育

广播:传输到网络(或者子网)上的所有主机。

日志实体类

import java.net.InetSocketAddress;

/**
 * 类说明:日志实体类
 */
public final class LogMsg {
    public static final byte SEPARATOR = (byte) ':';
    /*源的 InetSocketAddress*/
    private final InetSocketAddress source;
    /*消息内容*/
    private final String msg;
    /*消息id*/
    private final long msgId;
    /*消息发送的时间*/
    private final long time;

    //用于传入消息的构造函数
    public LogMsg(String msg) {
        this(null, msg,-1,System.currentTimeMillis());
    }

    //用于传出消息的构造函数
    public LogMsg(InetSocketAddress source, long msgId,
                  String msg) {
        this(source,msg,msgId,System.currentTimeMillis());
    }

    public LogMsg(InetSocketAddress source, String msg, long msgId, long time) {
        this.source = source;
        this.msg = msg;
        this.msgId = msgId;
        this.time = time;
    }

    //返回发送 LogMsg 的源的 InetSocketAddress
    public InetSocketAddress getSource() {
        return source;
    }

    //返回消息内容
    public String getMsg() {
        return msg;
    }

    //返回消息id
    public long getMsgId() {
        return msgId;
    }

    //返回消息中的时间
    public long getTime() {
        return time;
    }
}

日志信息

import java.util.Random;

/**
 * 类说明:日志信息,用String数组代替
 */
public class LogConst {
    public final static int MONITOR_SIDE_PORT = 9998;
    private static final String[] LOG_INFOS = {
            "20220715:machine-1:Send sms to 10001",
            "20220715:machine-3:Send email to ***@qq.com",
            "20220715:machine-4:Happen Exception",
            "20220715:machine-5:人生不能象做菜,把所有的料都准备好了才下锅",
            "20220715:machine-6:牵着你的手,就象左手牵右手没感觉,但砍下去也会痛!",
            "20220715:machine-7:我听别人说这世界上有一种鸟是没有脚的," +
                    "它只能一直飞呀飞呀,飞累了就在风里面睡觉,这种鸟一辈子只能下地一次," +
                    "那一次就是它死亡的时候.",
            "20220715:machine-2:多年以后我有个绰号叫西毒,任何人都可以变得狠毒," +
                    "只要你尝试过什么叫妒嫉.我不介意其他人怎么看我," +
                    "我只不过不想别人比我更开心.我以为有一些人永远不会妒嫉," +
                    "因为他太骄傲 . 在我出道的时候,我认识了一个人," +
                    "因为他喜欢在东边出没,所以很多年以后,他有个绰号叫东邪.",
            "20220715:machine-3:做人如果没有梦想,那和咸鱼有什么区别",
            "20220715:machine-5:世界上最远的距离不是生和死," +
                    "而是我站在你的面前却不能说:我爱你",
            "20220715:machine-6:成功的含义不在于得到什么," +
                    "而是在于你从那个奋斗的起点走了多远.",
            "20220715:machine-7:一个人杀了一个人,他是杀人犯.是坏人," +
                    "当一个人杀了成千上万人后,他是英雄,是大好人",
            "20220715:machine-2:世界在我掌握中,我却掌握不住对你的感情",
            "20220715:machine-3:我害怕前面的路,但是一想到你,就有能力向前走了。",
            "20220715:machine-5:如果你只做自己能力范围之内的事情,就永远无法进步。" +
                    "昨天已成为历史,明天是未知的,而今天是上天赐予我们的礼物," +
                    "这就是为什么我们把它叫做现在!"};

    private final static Random r = new Random();
    public static String getLogInfo(){
        return LOG_INFOS[r.nextInt(LOG_INFOS.length-1)];
    }
}

日志的广播端

import com.msb.netty.udp.broadcast.LogConst;
import com.msb.netty.udp.broadcast.LogMsg;
import io.netty.bootstrap.Bootstrap;
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.nio.NioDatagramChannel;

import java.net.InetSocketAddress;

/**
 * 类说明:日志的广播端
 */
public class LogEventBroadcaster {
    private final EventLoopGroup group;
    private final Bootstrap bootstrap;

    public LogEventBroadcaster(InetSocketAddress remoteAddress) {
        group = new NioEventLoopGroup();
        bootstrap = new Bootstrap();
        //引导该 NioDatagramChannel(无连接的)
        bootstrap.group(group).channel(NioDatagramChannel.class)
             //设置 SO_BROADCAST 套接字选项
             .option(ChannelOption.SO_BROADCAST, true)
             .handler(new LogEventEncoder(remoteAddress));
    }

    public void run() throws Exception {
        //绑定 Channel
        Channel ch = bootstrap.bind(0).sync().channel();
        long count = 0;
        //启动主处理循环,模拟日志发送
        for (;;) {
            ch.writeAndFlush(new LogMsg(null, ++count,
                    LogConst.getLogInfo()));
            try {
                //休眠 2 秒,如果被中断,则退出循环;
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                Thread.interrupted();
                break;
            }
        }
    }

    public void stop() {
        group.shutdownGracefully();
    }

    public static void main(String[] args) throws Exception {

        //创建并启动一个新的 UdpQuestionSide 的实例
        LogEventBroadcaster broadcaster = new LogEventBroadcaster(
                //表明本应用发送的报文并没有一个确定的目的地,也就是进行广播
                new InetSocketAddress("255.255.255.255",
                        LogConst.MONITOR_SIDE_PORT));
        try {
            broadcaster.run();
        }
        finally {
            broadcaster.stop();
        }
    }
}

实际的日志实体类编码

import com.msb.netty.udp.broadcast.LogMsg;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.util.CharsetUtil;

import java.net.InetSocketAddress;
import java.util.List;

/**
 * 类说明:编码,将实际的日志实体类编码为DatagramPacket
 */
public class LogEventEncoder extends MessageToMessageEncoder<LogMsg> {
    private final InetSocketAddress remoteAddress;

    //LogEventEncoder 创建了即将被发送到指定的 InetSocketAddress
    // 的 DatagramPacket 消息
    public LogEventEncoder(InetSocketAddress remoteAddress) {
        this.remoteAddress = remoteAddress;
    }

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext,
                          LogMsg logMsg, List<Object> out) throws Exception {
        byte[] msg = logMsg.getMsg().getBytes(CharsetUtil.UTF_8);
        //容量的计算:两个long型+消息的内容+分割符
        ByteBuf buf = channelHandlerContext.alloc()
            .buffer(8*2 + msg.length + 1);
        //将发送时间写入到 ByteBuf中
        buf.writeLong(logMsg.getTime());
        //将消息id写入到 ByteBuf中
        buf.writeLong(logMsg.getMsgId());
        //添加一个 SEPARATOR
        buf.writeByte(LogMsg.SEPARATOR);
        //将日志消息写入 ByteBuf中
        buf.writeBytes(msg);
        //将一个拥有数据和目的地地址的新 DatagramPacket 添加到出站的消息列表中
        out.add(new DatagramPacket(buf, remoteAddress));
    }
}

日志的接受端

import com.msb.netty.udp.broadcast.LogConst;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;

import java.net.InetSocketAddress;

/**
 * 类说明:日志的接受端
 */
public class LogEventMonitor {
    private final EventLoopGroup group;
    private final Bootstrap bootstrap;

    public LogEventMonitor(InetSocketAddress address) {
        group = new NioEventLoopGroup();
        bootstrap = new Bootstrap();
        //引导该 NioDatagramChannel
        bootstrap.group(group)
            .channel(NioDatagramChannel.class)
            //设置套接字选项 SO_BROADCAST
            .option(ChannelOption.SO_BROADCAST, true)
            //允许重用
            .option(ChannelOption.SO_REUSEADDR,true)
            .handler( new ChannelInitializer<Channel>() {
                @Override
                protected void initChannel(Channel channel)
                    throws Exception {
                    ChannelPipeline pipeline = channel.pipeline();
                    pipeline.addLast(new LogEventDecoder());
                    pipeline.addLast(new LogEventHandler());
                }
            } )
            .localAddress(address);
    }

    public Channel bind() {
        //绑定 Channel。注意,DatagramChannel 是无连接的
        return bootstrap.bind().syncUninterruptibly().channel();
    }

    public void stop() {
        group.shutdownGracefully();
    }

    public static void main(String[] args) throws Exception {
        //构造一个新的 UdpAnswerSide并指明监听端口
        LogEventMonitor monitor = new LogEventMonitor(
            new InetSocketAddress(LogConst.MONITOR_SIDE_PORT));
        try {
            //绑定本地监听端口
            Channel channel = monitor.bind();
            System.out.println("UdpAnswerSide running");
            channel.closeFuture().sync();
        } finally {
            monitor.stop();
        }
    }
}

日志的业务处理类

import com.msb.netty.udp.broadcast.LogMsg;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * 类说明:日志的业务处理类,实际的业务处理,接受日志信息
 */
public class LogEventHandler
    extends SimpleChannelInboundHandler<LogMsg> {

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,
        Throwable cause) throws Exception {
        //当异常发生时,打印栈跟踪信息,并关闭对应的 Channel
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx,
        LogMsg event) throws Exception {
        //创建 StringBuilder,并且构建输出的字符串
        StringBuilder builder = new StringBuilder();
        builder.append(event.getTime());
        builder.append(" [");
        builder.append(event.getSource().toString());
        builder.append("] :[");
        builder.append(event.getMsgId());
        builder.append("] :");
        builder.append(event.getMsg());
        //打印 LogMsg 的数据
        System.out.println(builder.toString());
    }
}

将DatagramPacket解码为实际的日志实体类

import com.msb.netty.udp.broadcast.LogMsg;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.util.CharsetUtil;

import java.util.List;

/**
 * 类说明:解码,将DatagramPacket解码为实际的日志实体类
 */
public class LogEventDecoder extends MessageToMessageDecoder<DatagramPacket> {

    @Override
    protected void decode(ChannelHandlerContext ctx,
                          DatagramPacket datagramPacket, List<Object> out)
        throws Exception {
        //获取对 DatagramPacket 中的数据(ByteBuf)的引用
        ByteBuf data = datagramPacket.content();
        //获得发送时间
        long sendTime = data.readLong();
        System.out.println("接受到"+sendTime+"发送的消息");
        //获得消息的id
        long msgId = data.readLong();
        //获得分隔符SEPARATOR
        byte sepa = data.readByte();
        //获取读索引的当前位置,就是分隔符的索引+1
        int idx = data.readerIndex();
        //提取日志消息,从读索引开始,到最后为日志的信息
        String sendMsg = data.slice(idx ,
            data.readableBytes()).toString(CharsetUtil.UTF_8);
        //构建一个新的 LogMsg 对象,并且将它添加到(已经解码的消息的)列表中
        LogMsg event = new LogMsg(datagramPacket.sender(),
                msgId, sendMsg);
        //作为本handler的处理结果,交给后面的handler进行处理
        out.add(event);
    }
}

运行结果

发送端接收消息
在这里插入图片描述

接收端接收消息
在这里插入图片描述

好了,到这里【基于Netty实现Http通信、实现UDP单播和广播通信、代码案例实战学习】,下次我们更新学习WebSocket通信的代码案例。

Netty实战(十五)UDP广播事件(一)UDP简介和示例程序 TCP:是面向连接的传输,它管理了两个网络端点之间的连接的建立,在连接的生命周期内的有序和可靠的消息传输,以及最后,连接的有序终止。TCP 的消息是有序的,并且会在两个方向上流动。是无连接协议中,并没有持久化连接这样的概念,并且每个消息(一个 UDP 数据报)都是一个单独的传输单元。同时UDP 也没有 TCP 的纠错机制,其中每个节点都将确认它们所接收到的包,而没有被确认的包将会被发送方重新传输。UDP 消息是无序的,并且无法确定是否能到达目的地。UDP的优势和不足:优势:UDP的传输速度远远高于TCP。 阅读详情

相关推荐

Netty框架实战篇 - 实现UDP单播和广播

文章目录前言一、pandas是什么?总结 前言 一、pandas是什么? 总结

wzljiayou的博客 4090

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

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

胡海龙 8774

Netty实战二-实现UDP的单播和广播

================ Netty里已经帮我们封装好了UDP相关的实现类。使用起来也非常方便 1、AddressedEnvelope 接口 interface AddressedEnvelope<M, A extends SocketAddress> extends ReferenceCounted 定义一个消息,其包装了另一个消息并带有发送者和接收者地址。其中M 是消息类型;A 是地址类型 2、DefaultAddressedEnvelope类 class DefaultA

m0_69526086的博客 1732

【Netty实现udp协议通讯】

使用Netty去实现udp通讯,在netty中支持udp的广播,组播,单播三中模式,本文主要是和硬件进行udp通讯,需要实现一发一收机制,所以使用单播模式进行通讯。

GBK_8的博客 3223

netty5_兼容tcp、websocket小例子 实现信息广播

netty5_兼容tcp、websocket小例子 实现信息广播,socket客户端连接后会受到getid此时socket客户端发送个六位数进行验证

高效网络流量回放:使用tcpreplay实现多目标IP的pcap重放

本文详细介绍了如何使用tcpreplay工具高效回放网络流量pcap文件,并实现多目标IP发送。通过tcprewrite编辑报文和并行回放策略,可满足安全测试、负载测试等场景需求。文章涵盖安装、基础回放、核心技巧及性能优化,帮助读者构建真实流量压力测试环境。

weixin_29297127的博客 479

【基于Netty实现WebSocket通信代码&基于WebSocket通信实现简单的群聊天室案例实战学习】

【基于Netty实现WebSocket通信代码&基于WebSocket通信实现简单的群聊天室案例实战学习】WebSocket ——一种在2011 年被互联网工程任务组(IETF)标准化的协议。WebSocket解决了一个长期存在的问题:既然底层的协议(HTTP)是一个请求/响应模式的交互序列,那么如何实时地发布信息呢?AJAX提供了一定程度上的改善,但是数据流仍然是由客户端所发送的请求驱动的。WebSocket规范以及它的实现代表了对一种更加有效的解决方案的尝试。

Coder_ljw的博客 1287

Remote ID的调制解调FPGA实现

Remote ID源于航空安全需求,例如在无人机密集区域实现设备跟踪和防碰撞。它类似于航空领域的ADS-B系统,但针对低空飞行器。Remote ID系统通常工作在特定频段(如2.4 GHz或5.8 GHz),通过无线链路广播数据包,包含设备ID、经纬度、高度和速度等信息。调制解调过程确保这些数据在噪声环境中可靠传输。例如,标准如ASTM F3411定义了Remote ID的协议框架,其中调制解调是关键物理层组件。

yulongfenghu的博客 1505

Netty实现http请求处理

Netty实现http请求处理

afggg_的博客 1087

从零开始:使用Remix和MetaMask在测试网部署智能合约的完整指南

本文提供了一份完整的指南,教你如何使用Remix和MetaMask在测试网上部署智能合约。从环境搭建、获取测试网代币到编写和部署合约,再到与合约交互和连接Python后端,涵盖了智能合约开发的各个环节。特别适合初学者快速上手区块链开发,同时介绍了安全注意事项和调试技巧。

weixin_29019241的博客 472

4相5线步进电机驱动原理

步进电机驱动

while(1) 3万+

《Netty实战篇》快速搭建UDP服务端

Netty是一个基于NIO的客户、服务端开发框架,使用Netty能够使你快速和简单的开发出一个网络应用,例如实现某种协议的客户、服务端应用。Netty相当于简化和流程化了网络应用的编程过程,例如基于UDP和TCP的socket开发。“快速而简单”并不意味着生成的应用程序将面临可维护性或性能问题。Netty是经过精心设计的,其经验来自FTP、SMTP、HTTP以及各种二进制和基于文本的传统协议的实现。因此,Netty成功地找到了一种不妥协地实现易于开发、性能、稳定性和灵活性的方法。

一个不断前行的程序者 2923

lighttools用透镜旋转手动创建菲尼尔透镜

学过很多软件,都没有留下痕迹,很多年之后只记得学过的软件常用的一些功能,稍微难一些的再次用起需要重新学,但如果留下点痕迹,重新再用就会很快。lighttools无意之中接触了,记一点点笔记吧。...

kingsai2012的博客 4329

【netty系列-10】通过netty实现http请求

通过netty实现http请求

zhenghuishengq的博客 3617

Calibre 3DSTACK Flow Example(5-1)

摘要: 本文详细介绍了使用Calibre 3DSTACK工具验证2.5D中介层IC的设计流程。该设计由控制器、两个RAM芯片和中介层组成,各裸片通过255层的焊盘图形和端口文本实现连接。流程包括:1)准备通过DRC/LVS验证的裸片版图;2)创建3DSTACK规则文件定义装配操作;3)设置全局选项并定义各裸片的图层属性;4)指定图层间的电气连接性;5)通过坐标精确放置裸片形成3D堆叠。文中提供了完整的规则文件示例,并说明了如何生成3D装配视图进行物理验证,为后续连接性检查奠定基础。

u013620135的博客 2164

【Android 11】使用Android Studio调试系统应用之Settings移植(五):编译Settings 模块、解决编译错误

基于Andorid 11,系列第5篇文章,本章开始,终于轮到解决正主Settings的编译问题了,使用android studio编译出Settings.apk,能看到这一章的同学,也着实不容易了,再接再厉

上周的博客 3919

netty编程之广播消息

本文看下使用netty如何实现广播消息,主要依赖于类。

wang0907的博客 793

HBV模型

据说mHM模型也是基于HBV模型原理。 网上不同版本的HBV模型稍有差别 def hbv_main(n_days, params, air_temp, prec, dpem): #n_days 模拟天数 #air_temp 每月温度 #prec 降水 #dpem 每月蒸发 Tsnow_thresh = 0.0 ca = 410. # Initialize arrays for the simiulation snow = np.zeros(air_temp.size

m0_46268055的博客 2323

netty实现http客户端请求远程http服务

一般http请求,我们会使用httpclient来实现连接池方式的连接,根据请求的类型,封装get,post等请求,设置参数,设置请求头,调用方法,发送请求之后等待请求返回结果,根据结果解析出我们需要的数据。netty也可以实现httpclient类似的功能,只不过,很多时候,我们使用netty构建tcp的连接,要么使用netty构建http服务端,很少用来构建http客户端,其实和tcp...

feinifi的博客 1万+

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Java字符串转16 进制工具类Hex.java

Java 字符串转 16 进制工具类 Hex.java 实现 16进制 0xfecd .. 和 java 字符串之间的互转换! 如果做开发,通常用户登陆,网络请求可能会用 mad5(salt + pwd) 然后再将 md 之后的数据 hex 一下。 这个工具类,就是实现此效果的。 public class Hex { /** * 用于建立十六进制字符的输出的小写字符数组

jia635的专栏 1万+

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Netty客户端发送HTTP2请求通过Nginx负载均衡转发到服务端

Netty发送HTTP2请求到NGINX,Nginx负载均衡转发到后端应用。

You_are_my_zing的博客 1459

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Java netty发送接收(TCP、UDP)

Java netty发送接收(TCP、UDP)含项目地址

weixin_50272584的博客 3341

05.netty实现简单的消息的广播

思路: 1、使用netty提供的ChannelGroup来保存连接到服务端的客户端连接 ChannelGroup的作用(好处)是:通过操作ChannelGroup,实现对加入到ChannelGroup中的所有的Channel进行统一操作 2、在Server端的客户端与服务端建立好连接时的回调函数handlerAdded方法中,将新的连接加入到ChannelGroup channelGrou...

mazhongjia的博客 2475

Netty网络编程(五):使用UDP协议

本文讲解了netty中UDP协议的实现,UDP相较于Socket连接而言更加简单。

Java+GO+JS全栈工程师-鹤冲天的博客:编程辅导~商务合作~技术交流 7413

Netty实现udp服务器

网络传输层协议有两种,一种是TCP,另外一种是UDP。Tcp的应用非常广泛,Udp相对来说比较少。本文演示如何使用netty开发udp服务,并使用游戏简易功能进行演示说明。

littleschemer的博客 5021

Netty 实现 UDP 通讯

Netty 实现 UDP 通讯 本文包括 UDP 一些基础知识和使用 Netty 完成 UDP 单播、组播和广播等多多种方式通讯。

大鱼的博客 1万+
上一篇: 【RocketMQ中的顺序消息、生产者顺序生产消息、消费者顺序消费消息、顺序包括全局有序和分块有序、代码样例实战】
下一篇: 【LeetCode每日一题:1678.Goal解析器~~~模拟/正则表达式】
硕风和炜
硕风和炜 优质创作者: Java技术领域 优质创作者: Java技术领域
博客等级 码龄7年 2万+粉丝 1024原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

硕风和炜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值