netty-客户端启动过程

976 阅读9分钟

前言

主要是连接。

应用程序

客户端.connect()方法。

//官方demo

/*

 * Copyright 2012 The Netty Project

 *

 * The Netty Project licenses this file to you under the Apache License,

 * version 2.0 (the "License"); you may not use this file except in compliance

 * with the License. You may obtain a copy of the License at:

 *

 *   http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT

 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the

 * License for the specific language governing permissions and limitations

 * under the License.

 */

package io.netty.example.echo;



import io.netty.bootstrap.Bootstrap;

import io.netty.channel.ChannelFuture;

import io.netty.channel.ChannelInitializer;

import io.netty.channel.ChannelOption;

import io.netty.channel.ChannelPipeline;

import io.netty.channel.EventLoopGroup;

import io.netty.channel.nio.NioEventLoopGroup;

import io.netty.channel.socket.SocketChannel;

import io.netty.channel.socket.nio.NioSocketChannel;

import io.netty.handler.ssl.SslContext;

import io.netty.handler.ssl.SslContextBuilder;

import io.netty.handler.ssl.util.InsecureTrustManagerFactory;



/**

 * Sends one message when a connection is open and echoes back any received

 * data to the server.  Simply put, the echo client initiates the ping-pong

 * traffic between the echo client and server by sending the first message to

 * the server.

 */

public final class EchoClient {



    static final boolean SSL = System.getProperty("ssl") != null;

    static final String HOST = System.getProperty("host", "127.0.0.1");

    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

    static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));



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

        // Configure SSL.git

        final SslContext sslCtx;

        if (SSL) {

            sslCtx = SslContextBuilder.forClient()

                .trustManager(InsecureTrustManagerFactory.INSTANCE).build();

        } else {

            sslCtx = null;

        }



        // 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 LoggingHandler(LogLevel.INFO));

                     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();

        }

    }

}


netty源码

1.启动类

public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {


/**

     * @see #connect()

     */

    private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {

        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 {

            // Registration future is almost always fulfilled already, but just in case it's not.

            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);

            regFuture.addListener(new ChannelFutureListener() {

                @Override

                public void operationComplete(ChannelFuture future) throws Exception {

                    // Directly obtain the cause and do a null check so we only need one volatile read in case of a

                    // failure.

                    Throwable cause = future.cause();

                    if (cause != null) {

                        // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an

                        // IllegalStateException once we try to access the EventLoop of the Channel.

                        promise.setFailure(cause);

                    } else {

                        // Registration was successful, so set the correct executor to use.

                        // See https://github.com/netty/netty/issues/2586

                        promise.registered();

                        doResolveAndConnect0(channel, remoteAddress, localAddress, promise);

                    }

                }

            });

            return promise;

        }

    }





private ChannelFuture doResolveAndConnect0(final Channel channel, SocketAddress remoteAddress,

                                               final SocketAddress localAddress, final ChannelPromise promise) {

        try {

            final EventLoop eventLoop = channel.eventLoop();

            final AddressResolver<SocketAddress> resolver = this.resolver.getResolver(eventLoop);



            if (!resolver.isSupported(remoteAddress) || resolver.isResolved(remoteAddress)) {

                // Resolver has no idea about what to do with the specified remote address or it's resolved already.

                doConnect(remoteAddress, localAddress, promise);

                return promise;

            }



            final Future<SocketAddress> resolveFuture = resolver.resolve(remoteAddress);



            if (resolveFuture.isDone()) {

                final Throwable resolveFailureCause = resolveFuture.cause();



                if (resolveFailureCause != null) {

                    // Failed to resolve immediately

                    channel.close();

                    promise.setFailure(resolveFailureCause);

                } else {

                    // Succeeded to resolve immediately; cached? (or did a blocking lookup)

                    doConnect(resolveFuture.getNow(), localAddress, promise);

                }

                return promise;

            }



            // Wait until the name resolution is finished.

            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) {



        // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up

        // the pipeline in its channelRegistered() implementation.

        final Channel channel = connectPromise.channel();

        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);

            }

        });

    }





2.通道.连接()


abstract class AbstractChannelHandlerContext extends DefaultAttributeMap

        implements ChannelHandlerContext, ResourceLeakHint {



@Override

    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;

        }



        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;

    }







private void invokeConnect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {

        if (invokeHandler()) {

            try {

                ((ChannelOutboundHandler) handler()).connect(this, remoteAddress, localAddress, promise);

            } catch (Throwable t) {

                notifyOutboundHandlerException(t, promise);

            }

        } else {

            connect(remoteAddress, localAddress, promise);

        }

    }





3.通道实现类

class SocketChannelImpl

    extends SocketChannel

    implements SelChImpl

{


public boolean connect(SocketAddress sa) throws IOException {

        int localPort = 0;



        synchronized (readLock) {

            synchronized (writeLock) {

                ensureOpenAndUnconnected();

                InetSocketAddress isa = Net.checkAddress(sa);

                SecurityManager sm = System.getSecurityManager();

                if (sm != null)

                    sm.checkConnect(isa.getAddress().getHostAddress(),

                                    isa.getPort());

                synchronized (blockingLock()) {

                    int n = 0;

                    try {

                        try {

                            begin();

                            synchronized (stateLock) {

                                if (!isOpen()) {

                                    return false;

                                }

                                // notify hook only if unbound

                                if (localAddress == null) {

                                    NetHooks.beforeTcpConnect(fd,

                                                           isa.getAddress(),

                                                           isa.getPort());

                                }

                                readerThread = NativeThread.current();

                            }

                            for (;;) {

                                InetAddress ia = isa.getAddress();

                                if (ia.isAnyLocalAddress())

                                    ia = InetAddress.getLocalHost();

                                n = Net.connect(fd,

                                                ia,

                                                isa.getPort()); //最终还是调用nio.channel包里的Net类——》native方法 所以netty和nio一样 只是封装了nio nio也是调用Net.native()方法

                                if (  (n == IOStatus.INTERRUPTED)

                                      && isOpen())

                                    continue;

                                break;

                            }



                        } finally {

                            readerCleanup();

                            end((n > 0) || (n == IOStatus.UNAVAILABLE));

                            assert IOStatus.check(n);

                        }

                    } catch (IOException x) {

                        // If an exception was thrown, close the channel after

                        // invoking end() so as to avoid bogus

                        // AsynchronousCloseExceptions

                        close();

                        throw x;

                    }

                    synchronized (stateLock) {

                        remoteAddress = isa;

                        if (n > 0) {



                            // Connection succeeded; disallow further

                            // invocation

                            state = ST_CONNECTED;

                            if (isOpen())

                                localAddress = Net.localAddress(fd);

                            return true;

                        }

                        // If nonblocking and no exception then connection

                        // pending; disallow another invocation

                        if (!isBlocking())

                            state = ST_PENDING;

                        else

                            assert false;

                    }

                }

                return false;

            }

        }

    }

nio框架-netty

private static void doConnect(

            final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {



        // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up

        // the pipeline in its channelRegistered() implementation.

        final Channel channel = connectPromise.channel();

        channel.eventLoop().execute(new Runnable() {

            @Override

            public void run() {

                if (localAddress == null) {

                    channel.connect(remoteAddress, connectPromise); //

                } else {

                    channel.connect(remoteAddress, localAddress, connectPromise); //NioSocketChannel

                }

                connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);

            }

        });

    }


private final DefaultChannelPipeline pipeline;


 @Override

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

        return pipeline.connect(remoteAddress, localAddress, promise); 

    }

final AbstractChannelHandlerContext tail;


@Override

    public final ChannelFuture connect(

            SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {

        return tail.connect(remoteAddress, localAddress, promise); //

ChannelHandlerContext(DefaultChannelPipeline$TailContext#0, [id: 0x28fdf4fa])

    }



@Override

    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;

        }



        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;

    }



private void invokeConnect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {

        if (invokeHandler()) {

            try {

                ((ChannelOutboundHandler) handler()).connect(this, remoteAddress, localAddress, promise); //

            } catch (Throwable t) {

                notifyOutboundHandlerException(t, promise);

            }

        } else {

            connect(remoteAddress, localAddress, promise);

        }

    }


private final Unsafe unsafe;


 @Override

        public void connect(

                ChannelHandlerContext ctx,

                SocketAddress remoteAddress, SocketAddress localAddress,

                ChannelPromise promise) throws Exception {

            unsafe.connect(remoteAddress, localAddress, promise);

        }


@Override

        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();

                }



                boolean wasActive = isActive();

                if (doConnect(remoteAddress, localAddress)) { //

                    fulfillConnectPromise(promise, wasActive);

                } else {

                    connectPromise = promise;

                    requestedRemoteAddress = remoteAddress;



                    // Schedule connect timeout.

                    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);

                                if (connectPromise != null && connectPromise.tryFailure(cause)) {

                                    close(voidPromise());

                                }

                            }

                        }, connectTimeoutMillis, TimeUnit.MILLISECONDS);

                    }



                    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();

            }

        }


@Override

    protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {

        if (localAddress != null) {

            doBind0(localAddress);

        }



        boolean success = false;

        try {

            boolean connected = SocketUtils.connect(javaChannel(), remoteAddress); //

            if (!connected) {

                selectionKey().interestOps(SelectionKey.OP_CONNECT);

            }

            success = true;

            return connected;

        } finally {

            if (!success) {

                doClose();

            }

        }

    }


public static boolean connect(final SocketChannel socketChannel, final SocketAddress remoteAddress)

            throws IOException {

        try {

            return AccessController.doPrivileged(new PrivilegedExceptionAction<Boolean>() {

                @Override

                public Boolean run() throws IOException {

                    return socketChannel.connect(remoteAddress); //
SocketChannalImpl

                }

            });

        } catch (PrivilegedActionException e) {

            throw (IOException) e.getCause();

        }

    }


jdk nio-channel

//sun.nio.channel.ChannelImpl通道实现-SocketChannalImpl

 public boolean connect(SocketAddress sa) throws IOException {

        int localPort = 0;



        synchronized (readLock) {

            synchronized (writeLock) {

                ensureOpenAndUnconnected();

                InetSocketAddress isa = Net.checkAddress(sa);

                SecurityManager sm = System.getSecurityManager();

                if (sm != null)

                    sm.checkConnect(isa.getAddress().getHostAddress(),

                                    isa.getPort());

                synchronized (blockingLock()) {

                    int n = 0;

                    try {

                        try {

                            begin();

                            synchronized (stateLock) {

                                if (!isOpen()) {

                                    return false;

                                }

                                // notify hook only if unbound

                                if (localAddress == null) {

                                    NetHooks.beforeTcpConnect(fd,

                                                           isa.getAddress(),

                                                           isa.getPort());

                                }

                                readerThread = NativeThread.current();

                            }

                            for (;;) {

                                InetAddress ia = isa.getAddress();

                                if (ia.isAnyLocalAddress())

                                    ia = InetAddress.getLocalHost();

                                n = Net.connect(fd,

                                                ia,

                                                isa.getPort());

                                if (  (n == IOStatus.INTERRUPTED)

                                      && isOpen())

                                    continue;

                                break;

                            }



                        } finally {

                            readerCleanup();

                            end((n > 0) || (n == IOStatus.UNAVAILABLE));

                            assert IOStatus.check(n);

                        }

                    } catch (IOException x) {

                        // If an exception was thrown, close the channel after

                        // invoking end() so as to avoid bogus

                        // AsynchronousCloseExceptions

                        close();

                        throw x;

                    }

                    synchronized (stateLock) {

                        remoteAddress = isa;

                        if (n > 0) {



                            // Connection succeeded; disallow further

                            // invocation

                            state = ST_CONNECTED;

                            if (isOpen())

                                localAddress = Net.localAddress(fd);

                            return true;

                        }

                        // If nonblocking and no exception then connection

                        // pending; disallow another invocation

                        if (!isBlocking())

                            state = ST_PENDING;

                        else

                            assert false;

                    }

                }

                return false;

            }

        }

    }


//同上包.Net

private static native int connect0(boolean preferIPv6,

                                       FileDescriptor fd,

                                       InetAddress remote,

                                       int remotePort)

        throws IOException; //最终调用的也是native方法。和java net包的socket一样,最终也是调用jvm native方法。


总结

1.包
与net包socket的区别? jdk nio包。和net包的socket完全不一样。2种实现方式。一个基于socket/单向读或写流,一个基于双向通道channel(可以同时读写)。

2.流程
nio包——》jvm native——》操作系统

channel()方法

应用程序代码

官方demo

/*

 * Copyright 2012 The Netty Project

 *

 * The Netty Project licenses this file to you under the Apache License,

 * version 2.0 (the "License"); you may not use this file except in compliance

 * with the License. You may obtain a copy of the License at:

 *

 *   http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT

 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the

 * License for the specific language governing permissions and limitations

 * under the License.

 */

package io.netty.example.echo;



import io.netty.bootstrap.Bootstrap;

import io.netty.channel.ChannelFuture;

import io.netty.channel.ChannelInitializer;

import io.netty.channel.ChannelOption;

import io.netty.channel.ChannelPipeline;

import io.netty.channel.EventLoopGroup;

import io.netty.channel.nio.NioEventLoopGroup;

import io.netty.channel.socket.SocketChannel;

import io.netty.channel.socket.nio.NioSocketChannel;

import io.netty.handler.ssl.SslContext;

import io.netty.handler.ssl.SslContextBuilder;

import io.netty.handler.ssl.util.InsecureTrustManagerFactory;



/**

 * Sends one message when a connection is open and echoes back any received

 * data to the server.  Simply put, the echo client initiates the ping-pong

 * traffic between the echo client and server by sending the first message to

 * the server.

 */

public final class EchoClient {



    static final boolean SSL = System.getProperty("ssl") != null;

    static final String HOST = System.getProperty("host", "127.0.0.1");

    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

    static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));



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

        // Configure SSL.git

        final SslContext sslCtx;

        if (SSL) {

            sslCtx = SslContextBuilder.forClient()

                .trustManager(InsecureTrustManagerFactory.INSTANCE).build();

        } else {

            sslCtx = null;

        }



        // 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 LoggingHandler(LogLevel.INFO));

                     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();

        }

    }

}


源码

public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {




/**

     * The {@link Class} which is used to create {@link Channel} instances from.

     * You either use this or {@link #channelFactory(io.netty.channel.ChannelFactory)} if your

     * {@link Channel} implementation has no no-args constructor.

     */

    public B channel(Class<? extends C> channelClass) {

        if (channelClass == null) {

            throw new NullPointerException("channelClass");

        }

        return channelFactory(new ReflectiveChannelFactory<C>(channelClass)); //?

    }


 /**

     * @deprecated Use {@link #channelFactory(io.netty.channel.ChannelFactory)} instead.

     */

    @Deprecated

    public B channelFactory(ChannelFactory<? extends C> channelFactory) {

        if (channelFactory == null) {

            throw new NullPointerException("channelFactory");

        }

        if (this.channelFactory != null) {

            throw new IllegalStateException("channelFactory set already");

        }



        this.channelFactory = channelFactory; //主要是设置通道工厂类

        return self();

    }



@SuppressWarnings("unchecked")

    private B self() {

        return (B) this; //返回值就是启动类本身

    }


和nio channel区别?

一样 封装了一下nio

通道channel到底指什么?

java网络通信 有2套机制
1.java.net.socket
2.java.nio.channel

相同点 都是表示一个连接 服务器和客户端的连接 但是网络编程里并不真正存在连接这个对象 jdbc里存在连接对象

不同点 首先 1.它们是互相独立的 之间没有任何关系 是java网络通信的2套解决方案 2.1是同步阻塞 2是异步非阻塞 异步非阻塞 可以提高网络通信的性能 就是不要一直卡在那里 等在那里 等就是浪费时间 异步就是连接之间互不影响 自己干自己的事情

group()方法

应用程序代码

官方demo

/*

 * Copyright 2012 The Netty Project

 *

 * The Netty Project licenses this file to you under the Apache License,

 * version 2.0 (the "License"); you may not use this file except in compliance

 * with the License. You may obtain a copy of the License at:

 *

 *   http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT

 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the

 * License for the specific language governing permissions and limitations

 * under the License.

 */

package io.netty.example.echo;



import io.netty.bootstrap.Bootstrap;

import io.netty.channel.ChannelFuture;

import io.netty.channel.ChannelInitializer;

import io.netty.channel.ChannelOption;

import io.netty.channel.ChannelPipeline;

import io.netty.channel.EventLoopGroup;

import io.netty.channel.nio.NioEventLoopGroup;

import io.netty.channel.socket.SocketChannel;

import io.netty.channel.socket.nio.NioSocketChannel;

import io.netty.handler.ssl.SslContext;

import io.netty.handler.ssl.SslContextBuilder;

import io.netty.handler.ssl.util.InsecureTrustManagerFactory;



/**

 * Sends one message when a connection is open and echoes back any received

 * data to the server.  Simply put, the echo client initiates the ping-pong

 * traffic between the echo client and server by sending the first message to

 * the server.

 */

public final class EchoClient {



    static final boolean SSL = System.getProperty("ssl") != null;

    static final String HOST = System.getProperty("host", "127.0.0.1");

    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

    static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));



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

        // Configure SSL.git

        final SslContext sslCtx;

        if (SSL) {

            sslCtx = SslContextBuilder.forClient()

                .trustManager(InsecureTrustManagerFactory.INSTANCE).build();

        } else {

            sslCtx = null;

        }



        // Configure the client.

        EventLoopGroup group = new NioEventLoopGroup();

        try {

            Bootstrap b = new Bootstrap();

            b.group(group) //设置事件循环组EventLoopGroup

             .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 LoggingHandler(LogLevel.INFO));

                     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();

        }

    }

}


源码

public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {



   

volatile EventLoopGroup group; //



    @SuppressWarnings("deprecation")

    private volatile ChannelFactory<? extends C> channelFactory; //

 




/**

     * The {@link EventLoopGroup} which is used to handle all the events for the to-be-created

     * {@link Channel}

     */

    public B group(EventLoopGroup group) {

        if (group == null) {

            throw new NullPointerException("group");

        }

        if (this.group != null) {

            throw new IllegalStateException("group set already");

        }

        this.group = group; //主要设置事件循环组EventLoopGroup

        return self();

    }