asp文章网站程序源码分享 用asp做网站

大家好,今天来为大家分享asp文章网站程序源码分享的一些知识点,和用asp做网站的问题解析,大家要是都明白,那么可以忽略,如果不太清楚的话可以看看本篇文章,相信很大概率可以解决您的问题,接下来我们就一起来看看吧!

上节讲到了kestrel服务器的配置及使用,相信很多同学已经对kestrel服务器有了初步的了解,那么有的同学可能会想更加深入的了解一下Kestrel服务器的是怎么实现监听和接收http请求的,今天我们来看下Kestrel服务器的源码,相信看完这些,你一定会对Kestrel服务器的运行机制有更深入的了解。

首先,让我们从程序启动类Program.cs开始分析。

publicclassProgram\n{\npublicstaticvoidMain(string[]args)\n{\nCreateHostBuilder(args).Build().Run();\n}\n\npublicstaticIHostBuilderCreateHostBuilder(string[]args)=>\nHost.CreateDefaultBuilder(args)\n.ConfigureWebHostDefaults(webBuilder=>\n{\nwebBuilder.UseStartup<Startup>();\n});\n}

其中,Host类链式调用了两个方法:

CreateDefaultBuilderConfigureWebHostDefaults

首先我们来看下CreateDefaultBuidler方法:

publicstaticIHostBuilderCreateDefaultBuilder(string[]args)\n{\nHostBuilderhostBuilder=newHostBuilder();\nhostBuilder.UseContentRoot(Directory.GetCurrentDirectory());\nhostBuilder.ConfigureHostConfiguration((Action<IConfigurationBuilder>)(config=>\n{\n…\n}));\nhostBuilder.ConfigureAppConfiguration((Action<HostBuilderContext,IConfigurationBuilder>)((hostingContext,config)=>\n{\n…\n})).ConfigureLogging((Action<HostBuilderContext,ILoggingBuilder>)((hostingContext,logging)=>\n{\n…\n})).UseDefaultServiceProvider((Action<HostBuilderContext,ServiceProviderOptions>)((context,options)=>\n{\n…\n}));\nreturn(IHostBuilder)hostBuilder;\n}\n}

从上述代码可以看出,CreateDefaultBuilder并未涉及Kestrel服务器相关代码,仅仅是进行一些应用的初始化配置,例如,设置应用程序目录,设置配置文件等操作。

我们再来看下ConfigureWebHostDefaults方法:

publicstaticIHostBuilderConfigureWebHostDefaults(\nthisIHostBuilderbuilder,\nAction<IWebHostBuilder>configure)\n{\nif(configure==null)\nthrownewArgumentNullException(nameof(configure));\nreturnbuilder.ConfigureWebHost((Action<IWebHostBuilder>)(webHostBuilder=>\n{\nMicrosoft.AspNetCore.WebHost.ConfigureWebDefaults(webHostBuilder);\nconfigure(webHostBuilder);\n}));\n}

通过阅读源码可以发现:ConfigureWebHostDefaults方法中的Microsoft.AspNetCore.WebHost.ConfigureWebDefaults(IWebHostBuilder)为实际执行初始化Kestrel服务器的代码。

internalstaticvoidConfigureWebDefaults(IWebHostBuilderbuilder)\n{\n…\nbuilder.UseKestrel((Action<WebHostBuilderContext,KestrelServerOptions>)((builderContext,options)=>options.Configure((IConfiguration)builderContext.Configuration.GetSection(&34;),true))).ConfigureServices((Action<WebHostBuilderContext,IServiceCollection>)((hostingContext,services)=>\n{\nservices.PostConfigure<HostFilteringOptions>((Action<HostFilteringOptions>)(options=>\n{\n…\n}\n})).UseIIS().UseIISIntegration();\n}

看到这里,可能有的同学已经的迫不及待的想要看下Kestrel初始化流程相关的代码了。别着急,我们一步一步来。

首先我们查看一下上面的UseKestrel扩展方法:

publicstaticIWebHostBuilderUseKestrel(\nthisIWebHostBuilderhostBuilder,\nAction<WebHostBuilderContext,KestrelServerOptions>configureOptions)\n{\nreturnhostBuilder.UseKestrel().ConfigureKestrel(configureOptions);\n}

发现该方法只是对传入的配置项KestrelServerOptions做了封装,最终是调用了IWebHostBuilder的扩展方法UseKestrel和ConfigureKestrel(Action<WebHostBuilderContext,KestrelServerOptions>configureOptions)扩展方法来初始化Kestrel服务器配置,同样是链式调用。

现在我们来看下UseKestrel()这个扩展方法:

publicstaticIWebHostBuilderUseKestrel(thisIWebHostBuilderhostBuilder)\n{\nreturnhostBuilder.ConfigureServices((Action<IServiceCollection>)(services=>\n{\nservices.TryAddSingleton<IConnectionListenerFactory,SocketTransportFactory>();\nservices.AddTransient<IConfigureOptions<KestrelServerOptions>,KestrelServerOptionsSetup>();\nservices.AddSingleton<IServer,KestrelServerImpl>();\n}));\n}

细心的同学可能会发现,配置一个Kestrel服务器居然只需要仅仅三行代码?是不是感觉有些不可思议?Kestrel服务器这么简单?是的,Kestrel服务器就是这么简单。那么,Kestrel服务器是如何实现监听和接收请求的呢?

首先看下IConnectionListenerFactory接口类:

publicinterfaceIConnectionListenerFactory\n{\nValueTask<IConnectionListener>BindAsync(\nEndPointendpoint,\nCancellationTokencancellationToken=default(CancellationToken));\n}

这个接口职责只有一个,就是执行Sokcert的绑定EndPoint操作,然后返回一个IConnectionListener对象。EndPoint可以有三种实现:

FileHandleEndPointUnixDomainSocketEndPointIPEndPoint

我们再来看下实现类SocketTransportFactory:

publicsealedclassSocketTransportFactory:IConnectionListenerFactory\n{\nprivatereadonlySocketTransportOptions_options;\nprivatereadonlySocketsTrace_trace;\npublicSocketTransportFactory(\nIOptions<SocketTransportOptions>options,\nILoggerFactoryloggerFactory)\n{\nif(options==null)\nthrownewArgumentNullException(nameof(options));\nif(loggerFactory==null)\nthrownewArgumentNullException(nameof(loggerFactory));\nthis._options=options.Value;\nthis._trace=newSocketsTrace(loggerFactory.CreateLogger(&34;));\n}\npublicValueTask<IConnectionListener>BindAsync(\nEndPointendpoint,\nCancellationTokencancellationToken=default(CancellationToken))\n{\nSocketConnectionListenerconnectionListener=newSocketConnectionListener(endpoint,this._options,(ISocketsTrace)this._trace);\nconnectionListener.Bind();\nreturnnewValueTask<IConnectionListener>((IConnectionListener)connectionListener);\n}\n}

代码非常简单,先实例化SocketConnectionListener对象,然后调用SocketConnectionListener的Bind方法并根据传入的EndPoint类型来创建Socket对象,来实现对EndPoint的监听和绑定操作。

internalvoidBind()\n{\nif(this._listenSocket!=null)\nthrownewInvalidOperationException(SocketsStrings.TransportAlreadyBound);\nSocketlistenSocket;\nswitch(this.EndPoint)\n{\ncaseFileHandleEndPointfileHandleEndPoint:\nthis._socketHandle=newSafeSocketHandle((IntPtr)(long)fileHandleEndPoint.FileHandle,true);\nlistenSocket=newSocket(this._socketHandle);\nbreak;\ncaseUnixDomainSocketEndPointdomainSocketEndPoint:\nlistenSocket=newSocket(domainSocketEndPoint.AddressFamily,SocketType.Stream,ProtocolType.IP);\nBindSocket();\nbreak;\ncaseIPEndPointipEndPoint:\nlistenSocket=newSocket(ipEndPoint.AddressFamily,SocketType.Stream,ProtocolType.Tcp);\nif(ipEndPoint.Address==IPAddress.IPv6Any)\nlistenSocket.DualMode=true;\nBindSocket();\nbreak;\ndefault:\nlistenSocket=newSocket(this.EndPoint.AddressFamily,SocketType.Stream,ProtocolType.Tcp);\nBindSocket();\nbreak;\n}\nthis.EndPoint=listenSocket.LocalEndPoint;\nlistenSocket.Listen(this._options.Backlog);\nthis._listenSocket=listenSocket;\nvoidBindSocket()\n{\ntry\n{\nlistenSocket.Bind(this.EndPoint);\n}\ncatch(SocketExceptionex)when(ex.SocketErrorCode==SocketError.AddressAlreadyInUse)\n{\nthrownewAddressInUseException(ex.Message,(Exception)ex);\n}\n}\n}

现在我们已经知道了Kestrel服务器内部是如何进行绑定和监听操作。那么Kestrel服务器是如何对http请求进行接收处理的呢?

接下来我们来看IServer接口:

publicinterfaceIServer:IDisposable\n{\nIFeatureCollectionFeatures{get;}\nTaskStartAsync<TContext>(IHttpApplication<TContext>application,CancellationTokencancellationToken)whereTContext:notnull;\nTaskStopAsync(CancellationTokencancellationToken);\n}

IServer接口也非常简单,定义了一个Server最基本的有两个功能:启动和停止。那么Kestrel服务器是怎么实现的这个接口呢?

下面我们来看下微软官方为IServer注入的实现类KestrelServerImpl:

internalclassKestrelServerImpl:IServer\n{\n…\npublicIFeatureCollectionFeatures{get;}\npublicKestrelServerOptionsOptions=>ServiceContext.ServerOptions;\nprivateServiceContextServiceContext{get;}\nprivateIKestrelTraceTrace=>ServiceContext.Log;\nprivateAddressBindContextAddressBindContext{get;set;}\npublicasyncTaskStartAsync<TContext>(IHttpApplication<TContext>application,CancellationTokencancellationToken)\n{\n…\nasyncTaskOnBind(ListenOptionsoptions)\n{\nif(!BitConverter.IsLittleEndian)\n{\nthrownewPlatformNotSupportedException(CoreStrings.BigEndianNotSupported);\n}\nValidateOptions();\nif(_hasStarted)\n{\n//Theserverhasalreadystartedand/orhasnotbeencleanedupyet\nthrownewInvalidOperationException(CoreStrings.ServerAlreadyStarted);\n}\n_hasStarted=true;\n\nServiceContext.Heartbeat?.Start();\nif((options.Protocols&HttpProtocols.Http3)==HttpProtocols.Http3)\n{\nif(_multiplexedTransportFactoryisnull)\n{\nthrownewInvalidOperationException($&34;);\n}\n\noptions.UseHttp3Server(ServiceContext,application,options.Protocols);\nvarmultiplexedConnectionDelegate=((IMultiplexedConnectionBuilder)options).Build();\n\nmultiplexedConnectionDelegate=EnforceConnectionLimit(multiplexedConnectionDelegate,Options.Limits.MaxConcurrentConnections,Trace);\noptions.EndPoint=await_transportManager.BindAsync(options.EndPoint,multiplexedConnectionDelegate,options.EndpointConfig).ConfigureAwait(false);\n}\n\nif((options.Protocols&HttpProtocols.Http1)==HttpProtocols.Http1\n||(options.Protocols&HttpProtocols.Http2)==HttpProtocols.Http2\n||options.Protocols==HttpProtocols.None)//TODOatestfailsbecauseitdoesn&34;CannotstartHTTP/1.xorHTTP/2serverifno{nameof(IConnectionListenerFactory)}isregistered.&34;CannotbindwithConnectionDelegatenoIConnectionListenerFactoryisregistered.&34;Theconnectionlistenerfailedtoacceptanynewconnections.”);\n}\nfinally\n{\nthis._acceptLoopTcs.TrySetResult();\n}\n}\n}

相信现在大家已经了解是怎么回事了吧?原来Kestrel服务器是通过while(true)循环接收的方式接收用户请求数据,然后通过线程池的ThreadPool.UnsafeQueueUserWorkItem方法将请求分发到CLR线程池来处理的。换句话说,在请求到来时,TransportManager将OnBind方法加入线程池并待CLR线程池调度。

那么回到开始的时候,Kestrel服务器是如何启动的呢?

让我们再回顾一下Program.cs中的方法

publicstaticvoidMain(string[]args)\n{\nCreateHostBuilder(args).Build().Run();\n}

相信聪明的同学已经猜到了,是通过Run()方法来执行的,Run()方法做了些什么呢?

Run方法实际上是执行了Host类中的StartAsync方法,此方法通过获取预先注入的GenericeWebHostService类中注入的IServer类来最终调用到IServer实现类的StartAsnyc方法的。

internalclassGenericWebHostService:IHostedService\n{\n…\npublicIServerServer{get;}\n…\n\tpublicasyncTaskStartAsync(CancellationTokencancellationToken)\n\t{\n…\nvarhttpApplication=newHostingApplication(application,Logger,DiagnosticListener,HttpContextFactory);\nawaitServer.StartAsync(httpApplication,cancellationToken);\n…\n\t}\n}

至此,Kestrel成功启动并开始监听用户请求。

一句话总结:其实ASP.NETCore5中的Kestrel服务器只是对Socket的简单封装,简单到直接用socket通过while(true)的方式来循环接收socket请求,并直接放入clr线程池中来等待线程池调度处理。

原来,Kestrel服务器这么简单~

相信通过本文的介绍,大家已经对ASP.NETCore5中的Kestrel服务器有了解了吧?

还在等什么,赶快收藏加关注吧!更多精彩内容还在等着你!

OK,本文到此结束,希望对大家有所帮助。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平