DotNetty完全教程(九)

引导Bootstrap

引导一个应用程序是指对他进行配置并且使他运行的过程。

体系结构

DotNetty完全教程(九)_第1张图片
注意,DotNetty没有实现Cloneable的接口,而是直接实现了一个Clone方法。Netty实现这个接口是为了创建两个有着相同配置的应用程序,可以把一个配置整体应用到另一个上面,需要注意的是EventLoopGroup是一个浅拷贝,这就导致了拷贝的Bootstrap都会使用同一个EventLoopGroup,这在每个Channel生命周期很短的时候是没有太大影响的。

服务器引导和普通引导有什么区别呢?区别在于,服务器接收到客户端的连接请求,会用一个Channel接受连接,然后用另一个Channel与客户端进行交流,但是客户端只需要一个Channel就可以与服务器进行交互。

关于链式调用

我们发现Bootstrap类可以通过流式语法进行链式调用,这要归功于Bootstrap类的特殊定义。下面我们来看一下:

// 定义
public abstract class AbstractBootstrap
    where TBootstrap : AbstractBootstrap
    where TChannel : IChannel
// 定义子类
public class Bootstrap : AbstractBootstrap
// 方法实现
public virtual TBootstrap Group(IEventLoopGroup group)
{
    Contract.Requires(group != null);

    if (this.group != null)
    {
        throw new InvalidOperationException("group has already been set.");
    }
    this.group = group;
    return (TBootstrap)this;
}
// 使用
var bootstrap = new Bootstrap();
bootstrap
    .Group(group)
    .Channel()
    .Handler(new ActionChannelInitializer(channel =>
    {
        IChannelPipeline pipeline = channel.Pipeline;
        pipeline.AddLast(new EchoClientHandler());
    }));

API

DotNetty完全教程(九)_第2张图片
DotNetty完全教程(九)_第3张图片

客户端引导

var group = new MultithreadEventLoopGroup();
var bootstrap = new Bootstrap();
bootstrap
    .Group(group)
    .Channel()
    .Handler(new ActionChannelInitializer(channel =>
    {
        IChannelPipeline pipeline = channel.Pipeline;
        pipeline.AddLast(new EchoClientHandler());
    }));
IChannel clientChannel = await bootstrap.ConnectAsync(new IPEndPoint(IPAddress.Parse("10.10.10.158"), 3000));
Console.ReadLine();
await clientChannel.CloseAsync();

服务器引导

API:
DotNetty完全教程(九)_第4张图片
注意上面箭头指示的是与Bootstrap不一样的方法。
为什么会有子Channel的概念呢,我们看下面这个图:
DotNetty完全教程(九)_第5张图片
因为服务器是一对多的,所以有子Channel的概念。

IEventLoopGroup eventLoop;
eventLoop = new MultithreadEventLoopGroup();
try
{
    // 服务器引导程序
    var bootstrap = new ServerBootstrap();
    bootstrap.Group(eventLoop);
    bootstrap.Channel();
    bootstrap.ChildHandler(new ActionChannelInitializer(channel =>
    {
        IChannelPipeline pipeline = channel.Pipeline;
        pipeline.AddLast(new EchoServerHandler());
    }));
    IChannel boundChannel = await bootstrap.BindAsync(3000);
    Console.ReadLine();
    await boundChannel.CloseAsync();
}
catch (Exception ex)
{
    Console.WriteLine(ex);
}
finally
{
    await eventLoop.ShutdownGracefullyAsync();
}

从Channel中引导客户端

  • 场景

    如果我们的服务器需要去第三方获取数据,这时候服务器就需要充当客户端去第三方取数据,这时候就需要在Channel中再开一个客户端获取数据。

  • 方式

    我们最好是从Channel中获取当前EventLoop,这样新开的客户端就跟当前Channel在一个线程中,减少了线程切换带来的开销,尽可能的重用了EventLoop

  • 实现

    // 从Context创建客户端引导
    var bootstrap = new Bootstrap();
    bootstrap.Group(ctx.Channel.EventLoop);
    

初始化Pipeline

如果要添加的Handler不止一个,我们就需要用到ChannelInitializer,在DotNetty中,我们有十分简单的方法可以初始化一个pipeline

var bootstrap = new Bootstrap();
bootstrap
    .Group(group)
    .Channel()
    .Option(ChannelOption.TcpNodelay, true)
    .Handler(new ActionChannelInitializer(channel =>
    {
        IChannelPipeline pipeline = channel.Pipeline;

        if (cert != null)
        {
            pipeline.AddLast("tls", new TlsHandler(stream => new SslStream(stream, true, (sender, certificate, chain, errors) => true), new ClientTlsSettings(targetHost)));
        }
        pipeline.AddLast(new LoggingHandler());
        pipeline.AddLast("framing-enc", new LengthFieldPrepender(2));
        pipeline.AddLast("framing-dec", new LengthFieldBasedFrameDecoder(ushort.MaxValue, 0, 2, 0, 2));

        pipeline.AddLast("echo", new EchoClientHandler());
    }));

ChannelOption

ChannelOption可以在引导的时候将设置批量的设置到所有Channel上,而不必要在每一个Channel建立的时候手动的去指定它的配置,应用场景是比如设置KeepAlive或者设置超时时间。

bootstrap.Option(ChannelOption.SoKeepalive, true)
    .Option(ChannelOption.ConnectTimeout, new TimeSpan(5000));

面向无连接的用户数据报文

UDP的全称是“User Datagram Protocol”,在DotNetty中实现了SocketDatagramChannel来创建无连接的引导,需要注意的是无连接的引导不需要Connect只需要bind即可,代码如下:

var bootstrap = new Bootstrap();
bootstrap
    .Group(group)
    .Channel()
    .Option(ChannelOption.SoBroadcast, true)
    .Handler(new ActionChannelInitializer(channel =>
    {
        channel.Pipeline.AddLast("Quote", new QuoteOfTheMomentClientHandler());
    }));

IChannel clientChannel = await bootstrap.BindAsync(IPEndPoint.MinPort);

关闭

Channel的关闭:

await clientChannel.CloseAsync();

EventLoopGroup的关闭:

await group.ShutdownGracefullyAsync();

你可能感兴趣的:(DotNetty,C#)