netty构建udp服务器以及发送报文到客户端客户端详细案例

目录

一、基于netty创建udp服务端以及对应通道设置关键

二、发送数据

三、netty中的ChannelOption常用参数说明

1、ChannelOption.SO_BACKLOG

2、ChannelOption.SO_REUSEADDR

3、ChannelOption.SO_KEEPALIVE

4、ChannelOption.SO_SNDBUF和ChannelOption.SO_RCVBUF

5、ChannelOption.SO_LINGER

6、ChannelOption.TCP_NODELAY


一、基于netty创建udp服务端以及对应通道设置关键

@Configuration
@RefreshScope
public class NettyUdpServer {

    @Value("${netty.server.udpPort}")
    private int port;

    private EventLoopGroup bossGroup;//主线程

    private Channel channel;//通道

    private ChannelFuture future; //回调

    @Autowired
    private DataCollector dataCollector;;

    public Channel start() throws InterruptedException {
        //判断是否支持Epoll模式,从而创建不同的线程组
        bossGroup = Epoll.isAvailable()? new EpollEventLoopGroup() : new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            //linux平台下增加SO_REUSEPORT特性提高性能,支持多个进程或者线程绑定到同一个端口,提高服务器程序的吞吐性能
            if(Epoll.isAvailable()) {
                //设置反应器线程组
                b.group(bossGroup)
                 .handler(new EpollUdpServerInitializer(dataCollector))
                //设置nio类型的通道
                 .channel(EpollDatagramChannel.class)
                 .option(ChannelOption.SO_BROADCAST, true)
                 .option(ChannelOption.SO_REUSEADDR, true)
                 .option(ChannelOption.SO_RCVBUF, 1024 * 1024)
                 .option(EpollChannelOption.SO_REUSEPORT, true);
            }else{
                //设置反应器线程组
                b.group(bossGroup)
                 .handler(new UdpServerInitializer(dataCollector))
                 //设置nio类型的通道
                 .channel(NioDatagramChannel.class)
                 //设置通道的参数
                 .option(ChannelOption.SO_BROADCAST, true)
                 .option(ChannelOption.SO_REUSEADDR, true);
            }
            //Channel channel = server.bind(port).sync().channel();
            //开始绑定服务器,通过调用sync()同步方法阻塞直到绑定成功
            //ChannelFuture channelFuture = b.bind(port).sync();
            //等待通道关闭的异步任务结束
            //ChannelFuture closeFuture = channelFuture.channel().closeFuture();
            //closeFuture.sync();

            ChannelFuture f = b.bind(port).sync();
            channel = f.channel();
            if(f.isSuccess()){
                //MasterSelector registry = new MasterSelector("","netty-services",  port);
                System.out.println("UDP服务器启动,监听在端口:" + port);
            }else {
                channel.closeFuture().sync();
            }

        } finally {
            //bossGroup.shutdownGracefully().sync();
        }
        System.out.println("Udp服务器启动,监听在端口:"+port);
        return channel;
    }
}

以上代码中Epoll.isAvailable()用户判断是window还是linux环境,linux环境默认采用Epoll相关通道,所以显式设置EpollDatagramChannel通道。在处理(handler)的设置中要根据不同的通道设置初始化的通道类型:

linux环境下EpollDatagramChannel通道设置 .handler(new EpollUdpServerInitializer(dataCollector))具体代码

public class EpollUdpServerInitializer extends ChannelInitializer<EpollDatagramChannel> {
    private final DataCollector dataCollector;

    public EpollUdpServerInitializer(DataCollector dataCollector) {
        this.dataCollector = dataCollector;
    }

    @Override
    protected void initChannel(EpollDatagramChannel epollDatagramChannel) throws Exception {
        epollDatagramChannel.pipeline()
                //添加netty空闲超时检查的支持
                .addLast(new UdpServerHandler(dataCollector));
    }

要使 通过服务器端通过EpollDatagramChannel通道发送数据,客户端能够正常接收到数据,下图中标红的泛型通道类要与服务器端设置的通道类一致

同意要支持Nio类型通道为NioDatagramChannel.class时,通道初始化为:

public class UdpServerInitializer extends ChannelInitializer<NioDatagramChannel> {
    private final DataCollector dataCollector;

    public UdpServerInitializer(DataCollector dataCollector) {
        this.dataCollector = dataCollector;
    }

    @Override
    protected void initChannel(NioDatagramChannel nioDatagramChannel) throws Exception {
        nioDatagramChannel.pipeline()
                //添加netty空闲超时检查的支持
                .addLast(new UdpServerHandler(dataCollector));
    }
}

 要使 通过服务器端通过NioDatagramChannel通道发送数据,客户端能够正常接收到数据,下图中标红的泛型通道类要与服务器端设置的通道类一致

二、发送数据

关键代码,采用writeAndFlush发送数据,注意:要发送udp数据报,

public class UdpServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {

    /**设置最大消息大小*/
    private static final int MAX_MESSAGE_SIZE = 2048;
    /**线程池*/
    private ExecutorService executorService;

    private final DataCollector dataCollector;

    public UdpServerHandler(DataCollector dataCollector) {
        this.dataCollector = dataCollector;
        //根据当前系统可用的处理器数量创建一个固定长度的线程池
        executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket datagramPacket) throws Exception {
        ByteBuf buffer = datagramPacket.content();
        //确保不会超出最大消息大小
        if(buffer.readableBytes() > MAX_MESSAGE_SIZE) {
            buffer.release();
            return;
        }
        UdpDatagram udpDatagram = parseUdpDatagram(buffer);
        UdpDatagram respUdpDatagram = dataCollector.processUdpDatagram(udpDatagram);
        if (null != respUdpDatagram) {
             handleReceivedData(ctx, respUdpDatagram, datagramPacket);
        }
    }

    /**
     * 处理接收到的数据
     * @param ctx
     * @param udpDatagram
     */
    public void handleReceivedData(ChannelHandlerContext ctx, UdpDatagram udpDatagram, DatagramPacket datagramPacket) throws ExecutionException, InterruptedException {
        Channel channel = ctx.channel();
        if (log.isInfoEnabled()) {
            log.info("received udp message: sessionId: {}, opCode: {}, short messageId: {}",
                    ctx.channel().id(), udpDatagram.getMessageTypeEnum(), udpDatagram.getShortMessageId());
        }
        byte[] payloadBytes = udpDatagram.getPayloadBytes();
        ByteBuf copiedBuffer = Unpooled.copiedBuffer(payloadBytes);
        ChannelFuture channelFuture = channel.writeAndFlush(new DatagramPacket(copiedBuffer.retain(), datagramPacket.sender()));
        channelFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                if (channelFuture.isSuccess()) {
                    // 数据发送成功
                    log.info("数据发送成功:sender host: {}, sender port:{}, sender address:{}",datagramPacket.sender().getHostName(),datagramPacket.sender().getPort(), datagramPacket.sender().getAddress());
                } else {
                    // 数据发送失败
                    log.error("数据发送失败: {}",channelFuture.cause().getStackTrace());
                    channelFuture.cause().printStackTrace();
                }
            }
        });
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        dataCollector.tcpConnect(ctx.channel());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        if (log.isWarnEnabled()) {
            log.warn("udp session throw an exception, sessionId:{} exception message: {}",
                    ctx.channel().id().asLongText(), cause.getMessage());
        }
    }

    //当客户端关闭链接时关闭通道
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        dataCollector.tcpChannelDisconnect(ctx.channel());

    }
}

处理类继承SimpleChannelInboundHandler类泛型类为DatagramPacket 

 writeAndFlush方法中发送的数据类型要是DatagramPacket

三、netty中的ChannelOption常用参数说明

1、ChannelOption.SO_BACKLOG

ChannelOption.SO_BACKLOG对应的是tcp/ip协议listen函数中的backlog参数。函数listen(int socketfd, int backlog)用来初始化服务端可连接队列。

服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接,多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定了队列的大小。

2、ChannelOption.SO_REUSEADDR

ChanneOption.SO_REUSEADDR对应于套接字选项中的SO_REUSEADDR,这个参数表示允许重复使用本地地址和端口。

比如,某个服务器进程占用了TCP的80端口进行监听,此时再次监听该端口就会返回错误,使用该参数就可以解决问题,该参数允许共用该端口,这个在服务器程序中比较常使用。

比如某个进程非正常退出,该程序占用的端口可能要被占用一段时间才能允许其他进程使用,而且程序死掉以后,内核一需要一定的时间才能够释放此端口,不设置SO_REUSEADDR就无法正常使用该端口。

3、ChannelOption.SO_KEEPALIVE

Channeloption.SO_KEEPALIVE参数对应于套接字选项中的SO_KEEPALIVE,该参数用于设置TCP连接,当设置该选项以后,连接会测试链接的状态,这个选项用于可能长时间没有数据交流的连接。

当设置该选项以后,如果在两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文。

4、ChannelOption.SO_SNDBUF和ChannelOption.SO_RCVBUF

ChannelOption.SO_SNDBUF参数对应于套接字选项中的SO_SNDBUF,ChannelOption.SO_RCVBUF参数对应于套接字选项中的SO_RCVBUF这两个参数用于操作发送缓冲区大小和接受缓冲区大小。

接收缓冲区用于保存网络协议站内收到的数据,直到应用程序读取成功,发送缓冲区用于保存发送数据,直到发送成功。

5、ChannelOption.SO_LINGER

ChannelOption.SO_LINGER参数对应于套接字选项中的SO_LINGER,Linux内核默认的处理方式是当用户调用close()方法的时候,函数返回,在可能的情况下,尽量发送数据,不一定保证会发送剩余的数据,造成了数据的不确定性,使用SO_LINGER可以阻塞close()的调用时间,直到数据完全发送。

6、ChannelOption.TCP_NODELAY

ChannelOption.TCP_NODELAY参数对应于套接字选项中的TCP_NODELAY,该参数的使用与Nagle算法有关。

Nagle算法是将小的数据包组装为更大的帧然后进行发送,而不是输入一次发送一次,因此在数据包不足的时候会等待其他数据的到来,组装成大的数据包进行发送,虽然该算法有效提高了网络的有效负载,但是却造成了延时。

而该参数的作用就是禁止使用Nagle算法,使用于小数据即时传输。和TCP_NODELAY相对应的是TCP_CORK,该选项是需要等到发送的数据量最大的时候,一次性发送数据,适用于文件传输。

ChannelOption属性
SO_BROADCAST对应套接字层的套接字:SO_BROADCAST,将消息发送到广播地址。
如果目标中指定的接口支持广播数据包,则启用此选项可让应用程序发送广播消息。
SO_KEEPALIVE对应套接字层的套接字:SO_KEEPALIVE,保持连接。
在空闲套接字上发送探测,以验证套接字是否仍处于活动状态。
SO_SNDBUF对应套接字层的套接字:SO_SNDBUF,设置发送缓冲区的大小。
SO_RCVBUF对应套接字层的套接字:SO_RCVBUF,获取接收缓冲区的大小。
SO_REUSEADDR对应套接字层的套接字:SO_REUSEADDR,本地地址复用。
启用此选项允许绑定已使用的本地地址。
SO_LINGER对应套接字层的套接字:SO_LINGER,延迟关闭连接。
启用此选项,在调用close时如果存在未发送的数据时,在close期间将阻止调用应用程序,直到数据被传输或连接超时。
SO_BACKLOG

对应TCP/IP协议中<font color=red>backlog</font>参数,<font color=red>backlog</font>即连接队列,设置TCP中的连接队列大小。如果队列满了,会发送一个ECONNREFUSED错误信息给C端,即“ Connection refused”。

SO_TIMEOUT等待客户连接的超时时间。
IP_TOS对应套接字层的套接字:IP_TOS,在IP标头中设置服务类型(TOS)和优先级。
IP_MULTICAST_ADDR对应IP层的套接字选项:IP_MULTICAST_IF,设置应发送多播数据报的传出接口。
IP_MULTICAST_IF对应IP层的套接字选项:IP_MULTICAST_IF2,设置应发送多播数据报的IPV6传出接口。
IP_MULTICAST_TTL对应IP层的套接字选项:IP_MULTICAST_TTL,在传出的 多播数据报的IP头中设置生存时间(TTL)。
IP_MULTICAST_LOOP_DISABLED取消 指定应将 传出的多播数据报的副本 回传到发送主机,只要它是多播组的成员即可。
TCP_NODELAY

对应TCP层的套接字选项:TCP_NODELAY,指定TCP是否遵循<font color=#35b998>Nagle算法</font> 决定何时发送数据。Nagle算法代表通过减少必须发送包的个数来增加网络软件系统的效率。即尽可能发送大块数据避免网络中充斥着大量的小数据块。如果要追求高实时性,需要设置关闭Nagle算法;如果需要追求减少网络交互次数,则设置开启Nagle算法。

  

 ChannelOption通用配置

参数说明
ALLOCATORByteBuf的分配器,默认值为ByteBufAllocator.DEFAULT。
RCVBUF_ALLOCATOR

用于Channel分配接受Buffer的分配器,默认值为AdaptiveRecvByteBufAllocator.DEFAULT,是一个自适应的接受缓冲区分配器,能根据接受到的数据自动调节大小。可选值为FixedRecvByteBufAllocator,固定大小的接受缓冲区分配器。

MESSAGE_SIZE_ESTIMATOR

消息大小估算器,默认为DefaultMessageSizeEstimator.DEFAULT。估算ByteBuf、ByteBufHolder和FileRegion的大小,其中ByteBuf和ByteBufHolder为实际大小,FileRegion估算值为0。该值估算的字节数在计算水位时使用,FileRegion为0可知FileRegion不影响高低水位。

CONNECT_TIMEOUT_MILLIS连接超时毫秒数,默认值30000毫秒即30秒。
WRITE_SPIN_COUNT一个Loop写操作执行的最大次数,默认值为16。也就是说,对于大数据量的写操作至多进行16次,如果16次仍没有全部写完数据,此时会提交一个新的写任务给EventLoop,任务将在下次调度继续执行。这样,其他的写请求才能被响应不会因为单个大数据量写请求而耽误。
WRITE_BUFFER_WATER_MARK
ALLOW_HALF_CLOSURE一个连接的远端关闭时本地端是否关闭,默认值为False。值为False时,连接自动关闭;为True时,触发ChannelInboundHandler的userEventTriggered()方法,事件为ChannelInputShutdownEvent。
AUTO_READ自动读取,默认值为True。Netty只在必要的时候才设置关心相应的I/O事件。对于读操作,需要调用channel.read()设置关心的I/O事件为OP_READ,这样若有数据到达才能读取以供用户处理。该值为True时,每次读操作完毕后会自动调用channel.read(),从而有数据到达便能读取;否则,需要用户手动调用channel.read()。需要注意的是:当调用config.setAutoRead(boolean)方法时,如果状态由false变为true,将会调用channel.read()方法读取数据;由true变为false,将调用config.autoReadCleared()方法终止数据读取。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/493961.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

任何程序员错过这些宝藏接单平台,都会难过好吗?

财富自由何时才能实现&#xff1f;首先&#xff0c;可以很负责地告诉你&#xff1a;安安心心地当一头牛马肯定是不能&#xff01;兢兢业业十几年&#xff0c;归来仍是马喽。 说实在的&#xff0c;这两年经济下行&#xff0c;互联网红利又日渐褪去。前者既争又抢&#xff0c;后…

【前端学习——js篇】5.事件循环

详细&#xff1a;https://github.com/febobo/web-interview 5.事件循环 js是一种单线程语言&#xff0c;同一时间内只能做一件事情&#xff0c;为了避免单线程阻塞的方法就是事件循环。 在javascript当中&#xff0c;所有的任务都可以分为&#xff1a; 同步任务&#xff1a;按…

【Linux】详解进程终止进程等待

一、页表&&写时拷贝的进一步理解 页表中不仅仅只有虚拟地址到物理地址的映射&#xff0c;还包括了很多选项&#xff0c;其中就包括了映射条目的权限。当我们进程的代码和数据加载到内存并和进程地址空间建立映射关系时&#xff0c;如果数据的内容不允许被修改&#xff…

阿里云服务器多少钱一个月?低至5元1个月

阿里云服务器一个月多少钱&#xff1f;最便宜5元1个月。阿里云轻量应用服务器2核2G3M配置61元一年&#xff0c;折合5元一个月&#xff0c;2核4G服务器30元3个月&#xff0c;2核2G3M带宽服务器99元12个月&#xff0c;轻量应用服务器2核4G4M带宽165元12个月&#xff0c;4核16G服务…

elementui日期时间选择框自定义组件

1.需求场景 业务中需要&#xff0c;日期选择框方便客户对日期的选择&#xff08;比如近5天&#xff0c;本周&#xff0c;本月&#xff0c;本年等等&#xff09;&#xff0c;并按小时展示。 2.组件代码MyDateTimeChange.vue <template><el-date-pickerv-model"…

Rust使用原始字符串字面量实现Regex双引号嵌套双引号正则匹配

rust使用Regex实现正则匹配的时候&#xff0c;如果想实现匹配双引号&#xff0c;就需要使用原始字符串字面量&#xff0c;不然无法使用双引号嵌套的。r#"..."# 就表示原始字符串字面量。 比如使用双引号匹配&#xff1a; use regex::Regex;fn main() {println!(&qu…

c# RichTextbox添加行号

使用另一个RichTextBox放在要添加行号的左边 使用以下代码 //uiRichTextBox1为右侧文本框&#xff0c;uiRichTextBox2为左侧文本框int lineIndex 0;private void uiRichTextBox1_TextChanged(object sender, EventArgs e){if (lineIndex > uiRichTextBox1.Lines.Length){L…

全国地质灾害点shp崩塌滑坡泥石流空间分布地质灾害详查等数据集

地质灾害是指在自然或者人为因素的作用下形成的&#xff0c;对人类生命财产造成的损失、对环境造成破坏的地质作用或地质现象。地质灾害在时间和空间上的分布变化规律&#xff0c;既受制于自然环境&#xff0c;又与人类活动有关&#xff0c;往往是人类与自然界相互作用的结果。…

【项目技术介绍篇】如何在本地运行若依项目

作者介绍&#xff1a;本人笔名姑苏老陈&#xff0c;从事JAVA开发工作十多年了&#xff0c;带过大学刚毕业的实习生&#xff0c;也带过技术团队。最近有个朋友的表弟&#xff0c;马上要大学毕业了&#xff0c;想从事JAVA开发工作&#xff0c;但不知道从何处入手。于是&#xff0…

Java——注解和注释

0 注解和注释的区别 在博主刚开始学习java语言的时候&#xff0c;经常把注释和注解搞混淆&#xff0c;误认为这两个是类似的东西&#xff0c;其实它们完全不是一个东西。 注释&#xff1a; 注释是程序员在代码中添加的说明性文字&#xff0c;用于解释代码的功能、目的或实现…

在ubuntu上编译prometheus

大纲 系统环境编译环境默认的版本GolangNodejsNPM 更新Nodejs和NPMNodejsNPM 编译运行 prometheus的编译并不难&#xff0c;核心是要将编译环境配置到符合要求的地步&#xff0c;否则就会出现各种错误&#xff0c;而且难以排查。 我们主要需要关心go、npm和nodejs的版本。 以下…

前端Web移动端学习day05

移动 Web 第五天 响应式布局方案 媒体查询Bootstrap框架 响应式网页指的是一套代码适配多端&#xff0c;一套代码适配各种大小的屏幕。 共有两种方案可以实现响应式网页&#xff0c;一种是媒体查询&#xff0c;另一种是使用bootstrap框架。 01-媒体查询 基本写法 max-wid…

饼图渲染的关键

1) 创建一个DOM对象,有自定义的高和宽. 2) 引入Echarts软件包并导入到对应文件内 npm i Echarts import 文件.js script src.../文件 3) 初始化一个对象 4) 对象的方法实现饼图渲染 data内的数据,且当一个对象已经渲染一遍,再执行这个,会对setOption的参数进行更新,其…

定制化区块链交易所开发:Dapp、DeFi和IDO的全方位解决方案

随着区块链技术的不断发展&#xff0c;区块链交易所已经成为数字资产交易的主要场所之一。然而&#xff0c;由于不同项目的需求和特点各不相同&#xff0c;通用的交易所往往无法满足所有的需求。因此&#xff0c;定制化区块链交易所的需求逐渐增加&#xff0c;以满足不同项目的…

JVM(六)——内存模型与高效并发

内存模型与高效并发 一、java 内存模型 【java 内存模型】是 Java Memory Model&#xff08;JMM&#xff09; 简单的说&#xff0c;JMM 定义了一套在多线程读写共享数据时&#xff08;成员变量、数组&#xff09;时&#xff0c;对数据的可见性、有序 性、和原子性的规则和保障…

MySQL修改root用户的密码

在Windows上重置MySQL的root用户密码可以通过以下步骤进行&#xff1a; 停止MySQL服务&#xff1a;使用快捷键WINR打开运行窗口&#xff0c;输入cmd进入命令行。在命令行中输入net stop mysql来关闭MySQL服务。启动MySQL跳过授权表&#xff1a;将目录切换到MySQL安装目录下的b…

CSMM软件过程能力成熟度模型

软件过程能力成熟度模型旨在通过提升组织的软件开发能力帮助顾客提升软件的业务价值。 本模型借鉴吸收了软件工程、项目管理、产品管理、组织治理、质量管理、卓越绩效管理、精益软件开发等领域的优秀实践&#xff0c;为组织提供改进和评估软件过程能力的一个成熟度模型。 总体…

腾讯云4核8G12M云服务器一年646元,送3个月时长

2024年腾讯云4核8G服务器租用优惠价格&#xff1a;轻量应用服务器4核8G12M带宽646元15个月&#xff0c;CVM云服务器S5实例优惠价格1437.24元买一年送3个月&#xff0c;腾讯云4核8G服务器活动页面 txybk.com/go/txy 活动链接打开如下图&#xff1a; 腾讯云4核8G服务器优惠价格 轻…

C语言程序编译与链接(拓宽视野的不二之选)

文章目录 翻译环境和运行环境翻译环境预处理编译汇编链接 运行环境 翻译环境和运行环境 1&#xff0c;在ANSI C的任何⼀种实现中&#xff0c;存在两个不同的环境。 第1种是翻译环境&#xff0c;在这个环境中源代码被转换为可执⾏的机器指 令&#xff08;⼆进制指令&#…

文生图大模型Stable Diffusion的前世今生!

1、引言 跨模态大模型是指能够在不同感官模态(如视觉、语言、音频等)之间进行信息转换的大规模语言模型。当前图文跨模态大模型主要有&#xff1a; 文生图大模型&#xff1a;如 Stable Diffusion系列、DALL-E系列、Imagen等 图文匹配大模型&#xff1a;如CLIP、Chinese CLIP、…