netty源码浅析-客户端启动流程

Netty 详细讲解,附服务端,客户端代码例子 的作用是管理事件循环组,它们负责处理不同类型的事件,如接受传入连接、读取/写入数据等。在这里,我们创建了两个bossGroup和。bossGroup用于接受传入的连接请求,而则用于实际处理连接的流量。是 Netty 用于启动服务器的帮助类。通过group()方法将两个绑定到服务器上。channel()方法指定了服务器使用的通道类型。在这里,我们选择了,这意味着我们将使用基于 NIO 的 Socket 通道。方法设置了一个,当一个新的连接被接受时,这个初始化器将会被调用。 阅读详情

客户端启动流程

我们以下面的demo作为本次netty源码分析的入口

public static void main(String[] args) throws Exception {
        // Configure the client.
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
             .channel(NioSocketChannel.class)
             .option(ChannelOption.TCP_NODELAY, true)
             .handler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline p = ch.pipeline();
                     if (sslCtx != null) {
                         p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT));
                     }
                     p.addLast(new EchoClientHandler());
                 }
             });

            // Start the client.
            ChannelFuture f = b.connect(HOST, PORT).sync();

            // Wait until the connection is closed.
            f.channel().closeFuture().sync();
        } finally {
            // Shut down the event loop to terminate all threads.
            group.shutdownGracefully();
        }
    }
}

客户端启动和服务端启动有很多类似的地方,首先我们看到相比于服务端,客户端只创建了一个NioEventLoopGroup,不太清楚创建过程的同学可以翻看一下前面的分析。与服务端引导类不同,客户端使用Bootstrap进行引导,同样通过group方法绑定eventLoop,通过channel方法设置为nio模式,同样也通过handler方法绑定了channel初始化器,以此向channelPipeline中添加channelHandler。完成上面的配置后,调用入口方法connect方法,我们跟踪方法执行会走到Bootstrap的doResolveAndConnect方法

public ChannelFuture connect(String inetHost, int inetPort) {
        return connect(InetSocketAddress.createUnresolved(inetHost, inetPort));
    }
private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
        //1.创建channel,完成初始化和注册
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();

        if (regFuture.isDone()) {
            if (!regFuture.isSuccess()) {
                return regFuture;
            }
            return doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());
        } else {
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    Throwable cause = future.cause();
                    if (cause != null) {
                        promise.setFailure(cause);
                    } else {
                        promise.registered();
                        doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }

通过上面的代码我们可以看到,首先和服务端执行bind方法相同,先执行initAndRegister方法,先通过反射创建 channel然后完成初始化,之后完成注册。与服务端不同这里创建的是NioSocketChannel对象,我们先跟踪到这个类的无参构造方法中。

public NioSocketChannel() {
        this(DEFAULT_SELECTOR_PROVIDER);
    }
public NioSocketChannel(SelectorProvider provider) {
        //通过SelectorProvider创建SocketChannel
        this(newSocket(provider));
    }
public NioSocketChannel(Channel parent, SocketChannel socket) {
        //1.调用父类构造函数
        //socket jdk底层Nio的channel
        super(parent, socket);
        //2.创建config对象
        config = new NioSocketChannelConfig(this, socket.socket());
    }

这里首先调用父类的构造函数,我们先跟踪到父类中

 protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
        //parent=null  socketChannel 关注的IO事件
        super(parent, ch, SelectionKey.OP_READ);
    }  

可以看到继续调用父类构造函数,设置了关注IO事件

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        this.ch = ch;//nio的ServerSocketChannel
        this.readInterestOp = readInterestOp;//连接事件
        try {
            ch.configureBlocking(false);//设置非阻塞模式
        } catch (IOException e) {
            try {
                ch.close();//发生异常就关闭channel
            } catch (IOException e2) {
                if (logger.isWarnEnabled()) {
                    logger.warn(
                            "Failed to close a partially initialized socket.", e2);
                }
            }

            throw new ChannelException("Failed to enter non-blocking mode.", e);
        }
    }
 protected AbstractChannel(Channel parent) {
        this.parent = parent;//null
        //1.channel唯一标识id
        id = newId();
        //2.unsafe是channel的内部类,封装了很多底层nio的操作
        unsafe = newUnsafe();
        //3.每创建一个channel就会创建一个channelPipe用于组织handler
        pipeline = newChannelPipeline();
    }

这段代码在服务端启动流程中我们也看到过,继续调用父类的构造函数,给每一个NioSocketChannel创建一个唯一的id,channel内部创建了一个unsafe对象,里面封装了一些IO的底层操作,还创建了一个pipeline对象,用来组织handler。然后将channel设置为非阻塞。
完成NioSocketChannel的初始化后,我们继续跟踪到init初始化方法中。这里客户端的引导类是BootStrap,所以会走到这个类的init方法中

void init(Channel channel) throws Exception {
        //向pipeline中加入ChannelInitializer
        ChannelPipeline p = channel.pipeline();
        p.addLast(config.handler());
        //配置
        final Map<ChannelOption<?>, Object> options = options0();
        synchronized (options) {
            setChannelOptions(channel, options, logger);
        }
        //属性
        final Map<AttributeKey<?>, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }
        }
    }

这里方法很加单,首先向pipeline中添加了一个Channellnitializer,这个我们在上面也已经分析过 就是新加了
个PendingHandlerAddedTask任务,当channel完成注册的时候回调handlerAdd方法,调用Channellnitializer的 initChannel方法,向pipeline中添加handler,然后再把Channellnitializer初始化器从链表中删除。然后对channel的属性和配置进行解析。
channel初始化完成后开始执行register方法

public ChannelFuture register(Channel channel) {
       //1.创建一个DefaultChannelPromise用于返回执行结果
       return register(new DefaultChannelPromise(channel, this));
   }
public ChannelFuture register(final ChannelPromise promise) {
       ObjectUtil.checkNotNull(promise, "promise");//校验
       //调用unsafe的register方法
       promise.channel().unsafe().register(this, promise);
       return promise;
   }

最终会走到AbstractChannel的内部类AbstractUnsafe的register方法

public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            if (eventLoop == null) {
                throw new NullPointerException("eventLoop");
            }
            if (isRegistered()) {
                promise.setFailure(new IllegalStateException("registered to an event loop already"));
                return;
            }
            if (!isCompatible(eventLoop)) {
                promise.setFailure(
                        new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
                return;
            }
            //关联channel和eventLoop
            AbstractChannel.this.eventLoop = eventLoop;
            //1.当前运行的线程是eventLoopGroup线程,及内部线程
            if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {
                try {
                    //2.调用execute方法将register0方法包装成一个任务放入NioEventLoop的
                    //任务队列中,由NioEventLoop线程执行
                    eventLoop.execute(new Runnable() {
                        @Override
                        public void run() {
                            register0(promise);
                        }
                    });
                } catch (Throwable t) {
                    logger.warn(
                            "Force-closing a channel whose registration task was not accepted by an event loop: {}",
                            AbstractChannel.this, t);
                    closeForcibly();
                    closeFuture.setClosed();
                    safeSetFailure(promise, t);
                }
            }
        }
private void register0(ChannelPromise promise) {
            try {
                // check if the channel is still open as it could be closed in the mean time when the register
                // call was outside of the eventLoop
                if (!promise.setUncancellable() || !ensureOpen(promise)) {
                    return;
                }
                //1.设置第一此执行注册标志
                boolean firstRegistration = neverRegistered;
                //2.完成channel注册,返回连接selectionKey
                doRegister();
                neverRegistered = false;
                registered = true;

                // Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
                // user may already fire events through the pipeline in the ChannelFutureListener.
                //3.检查是否触发HandlerAdded事件
                pipeline.invokeHandlerAddedIfNeeded();
                //4.设置promise注册成功
                safeSetSuccess(promise);
                //5.触发ChannelRegistered事件
                pipeline.fireChannelRegistered();
                // Only fire a channelActive if the channel has never been registered. This prevents firing
                // multiple channel actives if the channel is deregistered and re-registered.
                //6.检查channel是不是已经属于连接成功状态,这里channel还没有bind,所以返回false
                if (isActive()) {
                    if (firstRegistration) {
                        pipeline.fireChannelActive();
                    } else if (config().isAutoRead()) {
                        // This channel was registered before and autoRead() is set. This means we need to begin read
                        // again so that we process inbound data.
                        //
                        // See https://github.com/netty/netty/issues/4805
                        //7.给channel注册读事件
                        beginRead();
                    }
                }
            } catch (Throwable t) {
                // Close the channel directly to avoid FD leak.
                closeForcibly();
                closeFuture.setClosed();
                safeSetFailure(promise, t);
            }
        }

下面的注册流程和我们对服务端的分析相同,由于这里调用的线程是main线程,所以会执行else,将Runnable任务添加到NioEventLoop的task任务队列中,并且检查NioEventLoop线程是否启动,如果没有启动就将这个线程启动,并设置thread属性就是当前运行的NioEventLoop,然后执行其内部的run方法,循环执行,上面说的三个事
到这里完成了NioSocketChannel的初始化和注册了,下面开始正在执行connect方法

private ChannelFuture doResolveAndConnect0(final Channel channel, SocketAddress remoteAddress,
                                               final SocketAddress localAddress, final ChannelPromise promise) {
        try {
            //获取channel关联的eventLoop
            final EventLoop eventLoop = channel.eventLoop();
            //获取连接地址
            final AddressResolver<SocketAddress> resolver = this.resolver.getResolver(eventLoop);

            if (!resolver.isSupported(remoteAddress) || resolver.isResolved(remoteAddress)) {
                doConnect(remoteAddress, localAddress, promise);
                return promise;
            }

            final Future<SocketAddress> resolveFuture = resolver.resolve(remoteAddress);
            //解析是否完成
            if (resolveFuture.isDone()) {
                final Throwable resolveFailureCause = resolveFuture.cause();
                //解析失败设置失败原因
                if (resolveFailureCause != null) {
                    channel.close();
                    promise.setFailure(resolveFailureCause);
                } else {
                    //如果成功开始执行doConnect方法
                    doConnect(resolveFuture.getNow(), localAddress, promise);
                }
                return promise;
            }
            //如果上面的解析还没有完成就添加一个回调事件,如果解析完成后还是会调用doConnect方法
            resolveFuture.addListener(new FutureListener<SocketAddress>() {
                @Override
                public void operationComplete(Future<SocketAddress> future) throws Exception {
                    if (future.cause() != null) {
                        channel.close();
                        promise.setFailure(future.cause());
                    } else {
                        doConnect(future.getNow(), localAddress, promise);
                    }
                }
            });
        } catch (Throwable cause) {
            promise.tryFailure(cause);
        }
        return promise;
    }
private static void doConnect(
            final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {
        //获取channel
        final Channel channel = connectPromise.channel();
        //调用connect方法
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (localAddress == null) {
                    channel.connect(remoteAddress, connectPromise);
                } else {
                    channel.connect(remoteAddress, localAddress, connectPromise);
                }
                connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            }
        });
    }

然后会走到AbstractChannelHandlerContext的connect方法

public ChannelFuture connect(
            final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {

        if (remoteAddress == null) {
            throw new NullPointerException("remoteAddress");
        }
        if (isNotValidPromise(promise, false)) {
            // cancelled
            return promise;
        }
        //查询outbound的ChannelHandlerContext,这里就是headContext
        final AbstractChannelHandlerContext next = findContextOutbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeConnect(remoteAddress, localAddress, promise);
        } else {
            safeExecute(executor, new Runnable() {
                @Override
                public void run() {
                    next.invokeConnect(remoteAddress, localAddress, promise);
                }
            }, promise, null);
        }
        return promise;
    }

经过逐层跟踪调用跟踪会走到AbstractNioUnsafe#connect方法

public final void connect(
                final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
            if (!promise.setUncancellable() || !ensureOpen(promise)) {
                return;
            }

            try {
                if (connectPromise != null) {
                    // Already a connect in process.
                    throw new ConnectionPendingException();
                }
                //检查是不是channel是不是已经是活跃状态
                boolean wasActive = isActive();
                //连接远程服务端
                if (doConnect(remoteAddress, localAddress)) {
                    //如果连接成功设置promise状态并通知,如果没有则关闭
                    fulfillConnectPromise(promise, wasActive);
                } else {
                    //如果连接失败
                    connectPromise = promise;
                    requestedRemoteAddress = remoteAddress;

                    // Schedule connect timeout.
                    //默认30秒
                    int connectTimeoutMillis = config().getConnectTimeoutMillis();
                    if (connectTimeoutMillis > 0) {
                        connectTimeoutFuture = eventLoop().schedule(new Runnable() {
                            @Override
                            public void run() {
                                ChannelPromise connectPromise = AbstractNioChannel.this.connectPromise;
                                ConnectTimeoutException cause =
                                        new ConnectTimeoutException("connection timed out: " + remoteAddress);
                                //定时30秒后,如果连接失败就报错并关闭
                                if (connectPromise != null && connectPromise.tryFailure(cause)) {
                                    close(voidPromise());
                                }
                            }
                        }, connectTimeoutMillis, TimeUnit.MILLISECONDS);
                    }
                    //给promise添加一个Listener回调,如果取消了连接任务则将连接任务取消并关闭
                    promise.addListener(new ChannelFutureListener() {
                        @Override
                        public void operationComplete(ChannelFuture future) throws Exception {
                            if (future.isCancelled()) {
                                if (connectTimeoutFuture != null) {
                                    connectTimeoutFuture.cancel(false);
                                }
                                connectPromise = null;
                                close(voidPromise());
                            }
                        }
                    });
                }
            } catch (Throwable t) {
                promise.tryFailure(annotateConnectException(t, remoteAddress));
                closeIfClosed();
            }
        }

这里我们关注的还是doConnect方法方法我们继续跟进

protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
        //这里传入的localAddress是null则不会执行
        if (localAddress != null) {
            doBind0(localAddress);
        }

        boolean success = false;
        try {
            //调用connect方法
            boolean connected = SocketUtils.connect(javaChannel(), remoteAddress);
            //如果连接还没有建立,或者在非阻塞模式下正在连接,则设置关心connect事件
            if (!connected) {
                selectionKey().interestOps(SelectionKey.OP_CONNECT);
            }
            success = true;
            return connected;
        } finally {
            if (!success) {
                doClose();
            }
        }
    }

这里我们可以看到,完成了nio底层的SocketChannel的connect方法调用,并设置了关注事件OP_CONNECT,到这里客户端的启动连接就完成了。如果有分析错误的地方还请不吝指正。感谢!!!

【Netty】netty启动流程源码解析 阅读详情

相关推荐

Netty源码分析:服务端启动全过程(篇幅很长)

Netty源码分析:服务端启动全过程一般服务端的代码如下所示: public final class SimpleServer { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(1);

wojiushimogui的博客 1万+

Netty-客户端及服务端代码模板

一、服务端代码模板 public class NettyServer { public static void main(String[] args) throws InterruptedException { //server启动器 ServerBootstrap serverBootstrap = new ServerBootstrap(); //boss线程组,监听端口链接 NioEve

m0_37914588的博客 1586

浏览器主页被360篡改?教你彻底解决并预防再次发生

通过本文介绍的快捷方式修复、注册表清理、启动项管理三步法,可解决99%的浏览器主页篡改问题。权限最小化原则:非管理员账户日常使用,关键系统目录设置写保护定期安全审计:每月检查启动项/计划任务/浏览器扩展备份还原机制:创建系统还原点,重要操作前做好快照安全意识培养:不安装来源不明的软件,谨慎处理软件安装向导中的附加选项浏览器安全是系统防护的重要环节,建议结合EDR(终端检测与响应)方案构建立体防护体系。对于普通用户,保持系统更新、使用官方渠道下载软件、定期清理系统垃圾,即可有效预防绝大多数安全威胁。

nntxthml的博客 1万+

Netty入门案例,实现简单地服务端到客户端的数据发送和读取

使用Netty 实现简单地服务端到客户端的数据发送和读取

不会骑摩托的程序员不是好的摄影师 4077

【本体】Protege高效批量导入实例(Individuals)的自动化实践

本文详细介绍了在Protege中高效批量导入实例(Individuals)的自动化实践方法。通过分析Protege本体文件结构,提供Python脚本示例,帮助用户快速处理大规模数据导入,显著提升知识图谱构建效率。特别适合需要处理数千个实例的医疗、制药等领域应用。

weixin_30275415的博客 408

把群晖NAS变成游戏开发服务器:Perforce与虚幻引擎深度整合的5个关键配置(含.p4ignore模板)

本文详细介绍了如何将群晖NAS配置为游戏开发服务器,重点讲解了Perforce与虚幻引擎的深度整合。通过五个关键配置节点,包括用户权限架构、Workspace策略、资产同步优化、仓库清洁度保障和编辑器深度集成,帮助小型团队打造高效开发环境。文章还提供了实用的.p4ignore模板和优化技巧,显著提升开发效率。

weixin_30430677的博客 317

Netty——简单的入门代码示例

一个非常简单的Netty入门示例,简简单单搞定服务端和客户端的通信

共勉 1861

Flutter + shorebird 热更新+自定义cdn下载

本文介绍了如何优化Flutter热更新方案Shorebird在国内网络环境下的使用体验。Shorebird默认使用海外CDN存在下载慢、连接不稳定等问题,建议将补丁包上传至国内CDN(如阿里云OSS)。文章详细讲解了Shorebird环境配置、项目集成、构建发布流程,并重点说明了如何修改客户端代码实现自定义补丁下载逻辑。通过替换默认下载器,将补丁下载地址指向国内CDN,可显著提升下载速度和稳定性。这种方案既保留了Shorebird的热更新能力,又解决了国内网络环境下的痛点,适合国内企业部署使用。

sir_Hu的博客 769

netty通信客户端代码

Netty是一个用于快速开发可维护的网络应用程序的框架。下面是一个简单的Netty客户端代码示例,它将与服务器建立连接并发送消息: public class NettyClient { public static void main(String[] args) throws Exception { // 创建客户端处理程序 ClientHandler cl...

weixin_35748962的博客 292

Playwright Java环境配置避坑指南:浏览器路径、类加载与离线部署

Playwright作为现代端到端测试框架,其Java SDK突破了传统Selenium的Driver管理模式,将Chromium/Firefox/WebKit内核视为可编程运行时资源。其核心原理在于SDK与浏览器二进制分离——Maven仅管理Java字节码,而浏览器需独立下载、校验与沙箱加载。这一设计带来显著技术价值:跨平台一致性、进程隔离安全性及多浏览器并行能力;典型应用场景涵盖Spring Boot集成测试、金融级离线CI/CD、Kubernetes容器化部署及Air-Gapped封闭环境。然而,因J

weixin_32956805的博客 675

模型参考自适应控制(MRAC):让PID参数动态优化

本文深入解析模型参考自适应控制(MRAC)技术,它通过实时调整PID参数,使控制系统能自动适应系统特性变化和外部干扰。文章详细介绍了MRAC的工作原理、数学基础,并提供了用C语言实现的实战代码示例,帮助工程师解决传统PID控制在非线性、时变系统中参数整定繁琐的难题。

tequila的博客 902

Netty Review - 客户端流程源码解析

我们创建了一个NIO的EventLoopGroup,使用NioSocketChannel作为客户端的Channel类型,设置了TCP连接的保持活动选项,并初始化ChannelPipeline。EventLoopGroup是一组处理I/O操作的线程池,通常包含一个用于处理连接的boss线程池和一个用于处理I/O事件的worker线程池。此时,客户端会尝试连接到指定的远程服务器,并返回一个ChannelFuture对象,用于异步等待连接的建立。事件时,会执行相应的处理逻辑。

小工匠 8214

Windows 11 安装 Miniforge,配置国内源

本文详细介绍了在 Windows 11 系统中安装 Miniforge 的完整流程,包括下载、安装选项设置、配置国内镜像源(如清华大学源)以加速包管理,并启用 mamba 提升依赖解析与安装速度。同时涵盖 conda 与 mamba 的基本使用验证、PowerShell 初始化及环境提示符优化,帮助用户快速搭建高效、开源、免费的 Python 科学计算环境。

寒水馨 · 轻量级技术文档 · hanshuixin.org 4500

Netty 核心源码解析

Netty 第一讲:Netty 架构与原理本文是《Netty 三讲》的第二讲:Netty 核心源码解析(部分),大纲如下:前言1. Netty 服务端启动过程源码剖析1.1. 执行 ne...

weixin_41385912的博客 1万+

YOLOV26详解 从原理到部署

计算机视觉领域最具影响力的目标检测框架迎来重大里程碑。2026年1月,Ultralytics 正式发布 YOLOv26——这不是一次简单的版本迭代,而是一场面向边缘计算时代的架构重构。本专栏基于 Ultralytics 官方技术白皮书、YOLO Vision 2025 大会首发内容及国际前沿研究成果,系统拆解 YOLOv26 从算法原理到工程落地的全栈技术体系。与常规教程不同,我们聚焦与,帮助你在端侧 AI 部署的实战中掌握核心竞争力。

u012133341的博客 2011

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

netty源码学习之服务端客户端初始化

本篇博文从netty的使用例子出发,服务端bind()方法和客户端的connect()方法,分别分析了一下netty服务端和客户端的建立过程(大量篇幅),最终给予对上述理解简要总结了一次基于netty的一次请求和响应过程图解,希望对大家学习netty知识有所帮助,如果帮到你的话希望点赞、转发、一键三连让我们一起在技术的海洋遨游,奥利给!!!

liushangzaibeijing的博客 5万+

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Netty源码—服务端启动流程

用户调用启动辅助类ServerBootstrap的bind()方法时,第一步通过反射创建服务端Channel会执行NioServerSocketChannel的默认构造方法,来创建一个NioServerSocketChannel对象,并且在创建过程中会创建Netty的一系列核心组件:如Channel、ChannelConfig、ChannelId、Unsafe、ChannelPipeline。ServerBootstrap.bind() //用户代码入口。

sdgfafg_25的博客 1053

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

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

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Netty源码—4.客户端接入流程一

第一部分是逐层调用父类的构造方法,其中会设置这个客户端Channel的阻塞模式为false,然后再把感兴趣的读事件OP_READ保存到这个Channel的成员变量中以便后续注册到Selector,接着会创建一系列的组件,包括作为Channel唯一标识的Id组件、用来进行底层数据读写的unsafe组件、用来作为业务逻辑载体的pipeline组件。Netty中最核心的是两种类型的Reactor线程,这两种类型的Reactor线程可以看作Netty中的两组发动机,驱动着Netty整个框架的运转。

mjunz的博客 1206

【Netty之旅四】你一定看得懂的Netty客户端启动源码分析!

前言 前面小飞已经讲解了NIO和Netty服务端启动,这一讲是Client的启动过程。 源码系列的文章依旧还是遵循大白话+画图的风格来讲解,本文Netty源码及以后的文章版本都基于:4.1.22.Final 本篇是以NettyClient启动为切入点,带大家一步步进入Netty源码的世界。 Client启动流程揭秘 1、探秘的入口:netty-client demo 这里用netty-exmaple中的EchoClient来作为例子: public final class EchoClient {

一枝花算不算浪漫的专栏 417

Netty启动源码解析

Netty启动源码解析

qq_40913932的博客 1100

Netty源码—客户端接入流程

在服务端Channel对应的NioEventLoop的run()方法的processSelectedKeys()方法里,发现产生的IO事件是ACCEPT事件之后,会通过JDK Channel的accept()方法取创建JDK的Channel,并把它包装成Netty自定义的NioSocketChannel。在这个过程中会通过一个RecvByteBufAllocator.Handle对象控制连接接入的速率,默认一次性读取16个连接。创建NioSocketChannel的逻辑可以分成两部分。

dsgdauigfs的博客 689

Netty源码—1.服务端启动流程一

用户调用启动辅助类ServerBootstrap的bind()方法时,第一步通过反射创建服务端Channel会执行NioServerSocketChannel的默认构造方法,来创建一个NioServerSocketChannel对象,并且在创建过程中会创建Netty的一系列核心组件:如Channel、ChannelConfig、ChannelId、Unsafe、ChannelPipeline。Selector的轮询操作是由其绑定的EventLoop线程run()方法驱动的,在一个循环体内循环执行。

mjunz的博客 1201

netty 多个端口_Netty 启动流程解析

Netty的启动流程(ServerBootstrap),就是创建NioEventLoopGroup(内部可能包含多个NioEventLoop,每个eventLoop是一个线程,内部包含一个FIFO的taskQueue和Selector)和ServerBootstrap实例,并进行bind的过程(bind流程涉及到channel的创建和注册),之后就可以对外提供服务了。Netty的启动流程中,涉及到...

weixin_39562340的博客 1824

Netty源码深度解析:从启动到数据流转的全链路剖析

本文深入解析Netty 4.1.x核心源码,揭示其高性能网络编程的实现原理。主要内容包括:1.启动流程分析:详细拆解ServerBootstrap初始化、Channel注册及端口绑定过程,展现主从Reactor模型的实现;2.线程模型剖析:深入EventLoop工作机制,包括事件循环、任务队列及JDK空轮询修复等关键技术;3.数据读写全链路:从客户端发送到服务端处理的完整流程,涵盖粘包拆包、编解码等核心环节;4.设计模式应用:责任链模式在ChannelPipeline中的实现,以及事件驱动模型的异步处理机制

weixin_38436441的博客 1086

31.Netty源码之客户端启动流程

highlight: arduino-light 客户端启动主要流程 如果看了服务器端的启动流程,这里简单看下就可以了。 java package io.netty.server; ​ import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLo...

qq_30635523的博客 632

[Netty源码] 客户端启动流程 (三)

[Netty源码] 客户端启动流程: 客户端通过解析地址, 获取连接

959 632
上一篇: netty源码浅析-服务端启动流程
下一篇: netty源码浅析-NioEventLoop-reactor线程分析
胖柯G
博客等级 码龄10年 453粉丝 57原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值