阅读更多

1顶
1踩

编程语言

原创新闻 Alamofire的设计之道

2017-06-07 17:38 by 副主编 jihong10102006 评论(0) 有24191人浏览
引用


前言:

Alamofire是一个由Swift编写的优雅的网络开发框架。

大部分用Swift编写的iOS App的网络模块都是基于Alamofire的。作为Swift社区最活跃的几个项目之一,有许多开发者在不断的对其进行完善,所以学习这种优秀的开源代码对深入理解Swift的特性很有帮助。

本文很长,大到整个框架的设计,小到某些基础功能的使用都会涉及。

URL Loading System

iOS的网络开发(URL Loading System)的类层次如下:

从图中可以看出,整个框架包括URL Loading相关的核心类和五种辅助类。其中,五种辅助类划分如下
  • Configuration 配置信息,比如Cookie的存储策略,TLS版本等等。
  • Authentication and Credentials 授权和证书
  • Protocol support 用做proxy来拦截或特殊处理某些URL
  • Cookie Storage 管理Cookie
  • Cache Management 管理缓存
Alamofire就是建立在NSURLSession上的封装。

NSURLSession是在2013年推出的新API,并且Apple在2015年废弃了NSURLConnection。如果你的App还在用以NSURLConnection建立的网络层(比如AFNetworking 2.x),那么你真的应该考虑升级到NSURLSession(比如AFNetworking 3.x),废弃的API也许还能正常工作,但是Apple已对其不再维护,当然也就不支持HTTP 2.0等新特性。

关于NSURLSesson的基础使用,我之前有过几篇博客,可以在这个链接找到:博客iOS网络开发分类

那么,用NSURLSession来进行HTTP/HTTPS请求的时候,实际的过程如何呢?

  • 建立NSURLSessionTask,并且resume.
  • 检查cache策略,如果有需要从本地cache中直接返回数据
  • 通过DNS进行域名查找
  • 建立TCP连接
  • 如果是HTTPS,进行TLS握手(如有资源需要认证访问,可能需要客户端提供证书,用户名密码等信息)
  • 请求开始,收到HTTP的Response
  • 接收HTTP的Data
Tips: 理解HTTP/HTTPS的请求过程很重要,因为往往你需要统计API请求在哪个阶段出了问题,然后对症下药,提高用户体验。

整体架构

Alamofie的整体功能图如下:

其中:
  • 左侧是暴露给外部的接口,右侧是内部实现相关
  • 这三个模块比较独立:AlamofireImageAlamofireNetworkActivityIndicator 是基于Alamofire开发的独立的库,分别用来做图片和网络状态小菊花,NetworkReachabilityManager也是先对独立的用来检测蜂窝移动,WIFI等网络变化的。
我们先从一个API调用切入,来分析各个模块的作用:
Alamofire.request(/**/).validate(/**/).responseJSON {/**/}

初始化SessionManager的单例default
//整理后代码
self.delegate = SessionDelegate()
self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)

在初始化SessionManager代码里,提供了一个默认的SessionDelegate,并且初始化了一个URLSession,这个URLSession的delegate是SessionDelegate。
通过这个初始化,我们知道URLSession的几个代理事件都是传递给SessionManager的SessionDelegate了。
执行全局方法Alamofire.request

方法体中调用SessionManager.default单例的实例方法来创建DataRequest。这一步做了如下动作:
  • 根据传入的url,parameters等参数创建URLRequest
  • 根据URLRequest和SessionManager的属性session(URLSession),adapter(请求适配器),queue(GCD queue)创建URLSessionDataTask
  • 根据基类Request提供的方法,创建子类DataRequest实例,并且为子类DataRequest初始化一个DataTaskDelegate。
每一个DataRequest对应一个DataTaskDelegate,每一个TaskDelegate有一个OperationQueue,这个queue在初始化的时候是挂起状态的,并且是一个串行队列(maxConcurrentOperationCount = 1)。
open class Request{
    init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
    self.session = session
    switch requestTask {
    case .data(let originalTask, let task):
        taskDelegate = DataTaskDelegate(task: task)
        self.originalTask = originalTask
        //省略
    }
    delegate.error = error
    delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() } //加入统计请求结束的Operation
    }
}

  • 按需执行DataTask的resume方法
执行DataTask.validate

内容很简单,就是把传入的闭包保存起来,等待后续执行,并且返回Self

执行DataTask.responseJSON

在这个方法里,创建一个NSOperation加入到DataTaskDelegate的queue中,这个queue在创建之初是刮挂起状态的,所以提交的任务不会执行。

URLSession收到数据

首先SessionDelegate代理方法被调用:
open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
    if let dataTaskDidReceiveData = dataTaskDidReceiveData {//有自定义实现
            dataTaskDidReceiveData(session, dataTask, data)
        } else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {//走默认实现
            delegate.urlSession(session, dataTask: dataTask, didReceive: data)
        }
}

在这个代理方法里,根据存储的字典 URLSessionTask -> TaskDelegate 找到这个task的DataTaskDelegate,调用其方法
 func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
    //整理后代码
    mutableData.append(data) //存储数据到内存
   progressHandler.queue.async { progressHandler.closure(self.progress) } //回调progressHandler
}

URLSession完成Task

首先调用SessionDelegate中的URLSession的代理方法
open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
//整理后代码

//执行response的validation
request.validations.forEach { $0() } 
//唤起queue,来执行提交的任务
strongSelf[task]?.delegate.queue.isSuspended = false
strongSelf[task] = nil
}

由于queue被唤起,所以之前提交的完成callback会被执行。

执行网络请求完成的callback
//序列化请求结果,这里的responseSerializer为DataResponseSerializerProtocol协议类型
let result = responseSerializer.serializeResponse(
                self.request,
                self.response,
                self.delegate.data,
                self.delegate.error
            )
//建立Response对象
var dataResponse = DataResponse<T.SerializedObject>(
    request: self.request,
    response: self.response,
    data: self.delegate.data,
    result: result,
    timeline: self.timeline
)
//增加统计相关信息
dataResponse.add(self.delegate.metrics)

//执行传入的必报,也就是responseJSON函数传入的闭包
(queue ?? DispatchQueue.main).async { completionHandler(dataResponse) }

API设计

衡量一个框架好坏最重要的因素就是是否容易使用。

那么,如何定义容易使用呢?

根据二八原则,对于一个框架的使用百分之八十的时候都是很基础的功能使用,当这些基础的功能使用是容易的,我们认为这个框架是容易使用的。

我们来对比一下,同样GET一个URL,然后把数据解析成JSON。使用NSURLSession层次的API如下
guard let url = URL(string: "https://raw.githubusercontent.com/LeoMobileDeveloper/React-Native-Files/master/person.json") else {
    return;
}
let dataTask = URLSession.shared.dataTask(with: url) { (data, response, error) in
    guard let data = data else{
        return;
    }
    do{
        let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
        print(json)
    }catch let error{
        print(error)
    }
};
dataTask.resume()

使用Alamofire
Alamofire.request("https://raw.githubusercontent.com/LeoMobileDeveloper/React-Native-Files/master/person.json").responseJSON { (response) in
    if let JSON = response.result.value {
        print("JSON: \(JSON)")
    }
}

Tips: 这里的Alamofire.request指的是module(模块) Alamofire的一个全局方法request调用。

可以看到,使用系统的API,我们不得不先创建URL,然后建立DataTask,并且Resume。接着在callback里去解析JSON。由于Swift是一种强类型的语言,我们不得不进行大量的逻辑判断和try-catch。

而Alamofire把这些步骤简化成了一个静态的方法调用,并且用链式的方式来处理异步的Response解析。由于是链式的,你可以用链式的方式实现很多逻辑,比如验证返回值:
Alamofire.request("https://httpbin.org/get")
    .validate(statusCode: 200..<300) //返回值验证
    .responseData { response in //解析返回的数据
        switch response.result {
        case .success:
            print("Validation Successful")
        case .failure(let error):
            print(error)
        }
    }

用链式的方式进行异步处理是一个很好的实践,延伸阅读可以参考:PromiseKitRxSwift

链式的异步处理有很多优点:
  • 优雅的处理大量的callback
  • 代码更容易理解,更容易维护
  • 不需要在每一步都进行错误检查
80%情况下的API调用

Alamofire是采用静态方法的方式来提供80%情况下的API,这些全局方法可以在Alamofire.swift找到,以request为例:
@discardableResult //关键词告诉编译器,即使返回值不被持有,也别报警告
public func request(
    _ url: URLConvertible,
    method: HTTPMethod = .get,
    parameters: Parameters? = nil,
    encoding: ParameterEncoding = URLEncoding.default,
    headers: HTTPHeaders? = nil)
    -> DataRequest
{
    return SessionManager.default.request(
        url,
        method: method,
        parameters: parameters,
        encoding: encoding,
        headers: headers
    )
}

我们来分析下这个简单却又精炼的方法,方法的几个参数
  • url 请求的URL,协议URLConvertible类型(Alamofire用extension的方式为URL,String,URLComponents实现了这个协议)
  • method 请求的HTTP方法,默认为GET
  • parameters 请求的参数,默认为nil
  • encoding,,参数编码类型,默认URLEncoding.default,也就是根据HTTP方法的类型决定参数是query或者body里
  • headers, HTTP Header
返回值是一个DataRequest实例,这个实例就是异步调用链的头部。

Tips: 用默认参数来实现默认配置是一个很好的实践。

如何实现链式调用
open class Request {
    var validations: [() -> Void] = []
    public func validate(_ validation: @escaping Validation) -> Self {
        let validationExecution: () -> Void = {/**/}
        validations.append(validationExecution)
        return self
    }
}

从代码中,我们可以比较清楚的看出链式调用的原理:
函数的参数是闭包类型,方法体把这个闭包类型输入存储起来,并且返回Self。在合适的时候执行闭包即可实现异步的链式调用。

模块功能

软件设计有一个非常重要的原则就是:单一功能原则。

Alaofire的文件划分如下:

我们来分析Alamofire的各个模块负责的功能:
  • SessionManager 整个Alamofire框架的核心枢纽,封装了URLSession。负责提供外部调用的API,处理请求适配器,请求的重拾。
  • SessionDelegate SessionManager的代理,封装了URLSessionDelegate。负责对Task的回调事件提供默认实现(转发给TaskDelegate进行实际处理),并且以闭包的方式暴露给外部,让外部可以自定义实现。
  • TaskDelegate 对URLSessionTask的回调进行实际的处理,并且执行task的完成回调用
  • Request 是URLSessionTask的封装,是暴露给上层的请求任务
  • ParameterEncoding 对参数进行Encoding(JSON,query等)
  • Response 代表返回数据序列化后的结果
  • ResponseSerializer 对返回的数据进行序列化(JSON,property list等)
  • ServerTrustPolicyManager/ServerTrustPolicy 对TLS等过程中发生的认证进行处理
  • Timeline 纯粹的用来进行网络请求过程的数据统计
线程

Alamofire的线程处理都是采用GCD和NSOperation,并没有使用底层的Thread。

SessionManager

每一个SessionManager有一个常量属性
let queue = DispatchQueue(label: "org.alamofire.session-manager." + UUID().uuidString)

这个queue用来做task的初始化工作,也做了比如文件创建等
//task初始化
return task = queue.sync { session.downloadTask(with: urlRequest) }
//创建目录
try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)


URLSession

Session是这样被初始化的:
self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)

delegateQueue是URLSession的各种回调函数被调用的串行队列,这里传入nil,表示由系统自动为我们创建回调队列。

GlobalQueue

关于全局队列,有如下使用
//重试
DispatchQueue.utility.after{}

//初始化上传的MultipartFormData
DispatchQueue.global(qos: .utility).async


TaskDelegate

每一个Task有一个TaskDelegate,每一个TaskDelegate有一个常量属性queue
self.queue = {
            let operationQueue = OperationQueue()

            operationQueue.maxConcurrentOperationCount = 1
            operationQueue.isSuspended = true
            operationQueue.qualityOfService = .utility

            return operationQueue
        }()

这个queue有一点黑科技,在创建的时候是挂起的,然后不断的往里塞任务:比如responseJSON等。然后等Task完成的时候,再唤起queue,执行这些任务。

还是举一个例子,我们来看看队列之前的切换:
Alamofire.request(/**/).validate(/**/).responseJSON {/**/}

  • 主队列调用request方法
  • sync到SessionManager的queue上创建URLSessionDataTask
  • 主队列调用validate方法和responseJSON保存相关闭包
  • URLSession中由系统自动创建的queue收到delegate事件回调
  • 收到URLSessionTask完成的回调,TaskDelegate的queue被唤起
  • 异步到主队列执行responseJSON中传入的闭包
当然,上述的队列使用不包括以参数方式传递进入的,比如responseJSON,就可以指定这个闭包执行的队列
public func responseJSON(
        queue: DispatchQueue? = nil,
        options: JSONSerialization.ReadingOptions = .allowFragments,
        completionHandler: @escaping (DataResponse<Any>) -> Void)
        -> Self{}

错误处理

数据结构

AlamoFire的错误处理是采用了带关联值枚举,在Swift开发中,枚举是最常见的用来处理错误的。

在关联值枚举中,Alamofire还定义了内部类型,来对错误类型进行二次分类。代码如下:
public enum AFError: Error {
    public enum ParameterEncodingFailureReason {/*省略*/}
    public enum MultipartEncodingFailureReason {/*省略*/}
    public enum ResponseValidationFailureReason {/*省略*/}
    public enum ResponseSerializationFailureReason {/*省略*/}

    //枚举的可能值
    case invalidURL(url: URLConvertible)
    case parameterEncodingFailed(reason: ParameterEncodingFailureReason)
    case multipartEncodingFailed(reason: MultipartEncodingFailureReason)
    case responseValidationFailed(reason: ResponseValidationFailureReason)
    case responseSerializationFailed(reason: ResponseSerializationFailureReason)
}

我们来分析为什么要这样定义这些错误类型,一个典型的网络库的请求数据流如下:

其中,在调用URLSession相关的API之前,我们要先创建URLRequest,然后交给URLSession去做实际的HTTP请求,然后拿到HTTP请求的二进制数据,根据需要转换成字符串/JSON等交给上层。

所以,Alomofire的错误处理思想是:

根据错误发生的位置进行一级分类,再用嵌套类型对错误进行二次分类。

除了错误定义之外,开发者抓到错误能有友善的描述信息也是很重要的,这就是。Swift提供LocalizedError
extension AFError: LocalizedError {
    public var errorDescription: String? {
        /*省略*/
    }
}
extension AFError.ParameterEncodingFailureReason {
    var localizedDescription: String {
      /*省略*/
    }
}

Swift错误处理延伸阅读: 详解Swift中的错误处理
继承

NRULSessionTask是由继承来实现的,继承关系如下
URLSessionTask — Task的基类
  • URLSessionDataTask - 拉取URL的内容NSData
  • URLSessionUploadTask — 上传数据到URL,并且返回是NSData
  • URLSessionDownloadTask - 下载URL的内容到文件
  • URLSessionStreamTask — 建立TCP/IP连接
仿照这种关系,Alamofire的Request也是类似的继承关系:
Request — Task的基类
  • DataRequest - 拉取URL的内容NSData
  • UploadRequest — 上传数据到URL,并且返回是NSData
  • DownloadRequest - 下载URL的内容到文件
  • StreamRequest — 建立TCP/IP连接
其实原因和很简单:父类提供基础的属性和方法来给子类复用。

在Request中,除了继承,还使用了聚类的方式:由父类提供接口,初始化子类
init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
        self.session = session

        switch requestTask {
        case .data(let originalTask, let task):
            taskDelegate = DataTaskDelegate(task: task)
            self.originalTask = originalTask
            /**/
        }
    }

协议

Swift是面向协议编程的语言。

Alamofire的很多设计都是以协议为中心的,

以ParameterEncoding协议:

定义如下:
public protocol ParameterEncoding {
    func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest
}

接口依赖于这个协议类型
public func request(
    _ url: URLConvertible,
    method: HTTPMethod = .get,
    parameters: Parameters? = nil,
    encoding: ParameterEncoding = URLEncoding.default,
    headers: HTTPHeaders? = nil)
    -> DataRequest
{
    /*略*/
}

这样在传入的时候,只要是这个协议类型都可以,不管是struct,enum或者class。

Alamofire实现了三种Encoding方式:
public struct URLEncoding: ParameterEncoding {}
public struct JSONEncoding: ParameterEncoding {}
public struct PropertyListEncoding: ParameterEncoding {}

扩展性

由于提供的接口是协议类型的,于是你可以方便直接把一个实例当作url,并且自定义encodeing方法
enum API:URLConvertible{
    case login
    public func asURL() throws -> URL {
        //Return login URL
    }
}
class CustomEncoding: ParameterEncoding{/*/*}

然后,你就可以这么调用了
Alamofire.request(API.login, method: .post, encoding: CusomEncoding())

可以看到,使用协议提供的接口是抽象的接口,与具体的class/enum/struct无关,也就易于扩展

代理

代理是CocoaTouch一个很优秀的设计模式,它提供了一种盲通信的方式把相关的任务划分到不同的类中。

在Alamofire中,最主要的就是这两对代理关系:

由于Delegate的存在,
  • SessionManager只需要关注URLSession的封装即可,session层面的事件回调交给由SessionDelegate处理
  • Request只需要关注URLSessionTask的封装,task层面的任务交给RequestDelegate处理。
这样,保证了各个模块之间的功能单一,不会互相耦合。

类型安全

Swift本身是一种类型安全的语言,这意味着如果编译器发现类型不对,你的代码将编译不通过。

URLRequest有一个属性是HTTPMethod
var httpMethod: String? { get set }

它的类型是String类型,这意味着你可以随意的赋值,编译器缺不会提示你你的输入可能又问题。
request.httpMethod = "1234"

考虑到HTTPMethod无非也就是那几种,很适合用enum来做,Alamofire对其进行了封装
public enum HTTPMethod: String {
    case options = "OPTIONS"
    case get     = "GET"
    case head    = "HEAD"
    case post    = "POST"
    case put     = "PUT"
    case patch   = "PATCH"
    case delete  = "DELETE"
    case trace   = "TRACE"
    case connect = "CONNECT"
}

然后,上层的方法提供的接口是枚举类型:
public func request(
    _ url: URLConvertible,
    method: HTTPMethod = .get, //这里
    parameters: Parameters? = nil,
    encoding: ParameterEncoding = URLEncoding.default,
    headers: HTTPHeaders? = nil)
    -> DataRequest
{
    /*略*/
}

这样,编译器就能够进行合理的检查,也不容易出错了。

版本与平台适配

Alamofire适配的平台有ios/osx/tvos/watchos,适配的最低iOS版本是iOS 8。 那么,就出现了一个问题
  • 有些平台没有对应的API
  • 有些API是高版本的系统才有的
举个例子:
func streamTask(with service: NetService) -> URLSessionStreamTask

Alamofire采用如下方式进行适配:

@avialable - 用来标记适配系统版本(for编译器)

比如,这个函数被标记为iOS 9.0后可用,如果直接在target iOS 8的调用,则会报错。可以在if #available{}中调用
@discardableResult
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
public func stream(withHostName hostName: String, port: Int) -> StreamRequest {
    return SessionManager.default.stream(withHostName: hostName, port: port)
}

#if ... #endif - 用作条件编译(for编译器)

例如:在watchOS上不编译
#if !os(watchOS)
@discardableResult
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
public func stream(withHostName hostName: String, port: Int) -> StreamRequest {
    return SessionManager.default.stream(withHostName: hostName, port: port)
}
#endif

#available - 满足平台和系统要求才调用(for 编译器,运行时)
extension Response {
    mutating func add(_ metrics: AnyObject?) {
        #if !os(watchOS)
            guard #available(iOS 10.0, macOS 10.12, tvOS 10.0, *) else { return }
            guard let metrics = metrics as? URLSessionTaskMetrics else { return }

            _metrics = metrics
        #endif
    }
}

总结

Alamofire是一个优雅的Swift开源库,它的代码真的很优雅,强烈建议对Swift感兴趣并且想深入学习的同学用几天的空余时间去研究下。看的时候多问自己几个问题:
  • 为什么这里要用protocol而不用继承?
  • 为什么这里要用struct而不用class?
  • …..

总之,多问为什么,然后找到答案,就会很有收获。

本文同步放到我的github上,如有问题欢迎issue。
  • 大小: 46.3 KB
  • 大小: 63 KB
  • 大小: 30.7 KB
  • 大小: 88.9 KB
  • 大小: 136 KB
  • 大小: 131.5 KB
  • 大小: 12.7 KB
1
1
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • 【Swift】Alamofire的设计之道

    转载: Alamofire的设计之道

  • Moya的设计之道

    前言 Moya是一个基于Alamofire开发的,轻量级的Swift网络层。Moya的可扩展性非常强,可以方便的RXSwift,PromiseKit和ObjectMapper结合。 如果你的项目刚刚搭建,并且是纯...Alamofire的设计之道 Moya除了依赖Alamof

  • Alamofire源码解读系列(八)之安全策略(ServerTrustPolicy)

    本篇主要讲解Alamofire中安全验证代码 前言 作为开发人员,理解HTTPS的原理和应用算是一项基本技能。HTTPS目前来说是非常安全的,但仍然有大量的公司还在使用HTTP。其实HTTPS也并不是很贵啊。 在网上可以找到大把的...

  • iOS开源库源码解析之SDWebImage

    ,之所以这样,是因为从磁盘或者内存查询的过程是异步的,后面可能需要cancel,所以这样做。 我们再看看 queryDiskCacheForKey:key 这个方法是怎么实现的? - (NSOperation *)queryDiskCacheForKey:( ...

  • SDWebImage源码解读之干货大总结

    建筑师设计房屋更倾向于使用矢量的方式,因为矢量格式并不仅仅局限于线条的绘制,也可以用渐变或图案的填充作为展示,所以利用矢量方式完全可以生成房屋的拟真渲染图。 用于填充的图案单元则更适合被储存为一个...

  • SDWebImage源码解读之 干货大总结

    建筑师设计房屋更倾向于使用矢量的方式,因为矢量格式并不仅仅局限于线条的绘制,也可以用渐变或图案的填充作为展示,所以利用矢量方式完全可以生成房屋的拟真渲染图。 用于填充的图案单元则更适合被储存为一个...

  • 构建属于自己的Flutter混合开发框架

    在原生网络通信方面,目前市面上有很多优秀的第三方开源 SDK,比如 iOS 的 AFNetworking 和 Alamofire、Android 的 OkHttp 和 Retrofit 等。考虑到 AFNetworking 和 OkHttp 在各自平台的社区活跃度相对最高,因此...

  • 【学员作品】我的电商类App 3.2.1居然被拒了,怎么破?(酷课堂iOS交流群问答整理201812期)

    :漫道) 如下图:       3 、 刚 申 请 的号 , 用 测试 的unit id可以 显 示广告, 现 在要上架, 换 成自己的就不可以了,怎么破?(提 问 :MissLIBRA-石家庄-学生) 刚 申 请 的号 ,不...

  • iOS之第三方库以及XCode插件介绍

    也不错啦 但是还是用不惯) 帮助我轻易的跨越了Autolayout这道坎 我也曾多次在 文章 中提到过关于Masonry的使用方法 如果还没有用过的朋友 不妨看一看 pop Facebook的工程师一直是神一般的存在 对开源社区的 ...

  • iOS最全学习资源汇总

    Objective-C 编程之道 解析 iOS 的开山之作, 详细介绍了 MVC 在 Cocoa Touch 上的运作过程, 该书适用于 iOS 中级开发者阅读 Objective-C 高级编程 本书主要介绍 iOS 与 OS X 多线程和内存管理, 深入...

  • IOS 使用自签名证书开发HTTPS文件传输

    IOS 使用自签名证书开发HTTPS文件传输1. HTTPS文件传输简介2. HTTPS 对比HTTP简介3.... Alamofire实现SSL安全认证 源码解析7. HTTPS实现文件上传8. HTTPS实现文件下载9. HTTPS文件传输通过moya+alamofire+rxsw...

  • MATLAB Simulink搭建电动汽车整车七自由度模型:含七自由度建模细节与模糊控制算法及魔术公式等相关说明文档,MATLAB Simulink搭建电动汽车整车七自由度模型电动汽车七由度模型,包括

    MATLAB Simulink搭建电动汽车整车七自由度模型:含七自由度建模细节与模糊控制算法及魔术公式等相关说明文档,MATLAB Simulink搭建电动汽车整车七自由度模型电动汽车七由度模型,包括纵向运动,侧向运动横摆运动,模糊控制算法,轮胎模型,魔术公式等等,包含相关说明文档。 ,核心关键词:MATLAB Simulink; 电动汽车整车七自由度模型; 纵向运动; 侧向运动; 横摆运动; 模糊控制算法; 轮胎模型; 魔术公式; 相关说明文档。,MATLAB Simulink:电动汽车七自由度模型构建与模糊控制算法应用

  • 粒子群优化算法下的电力系统经济调度与经济成本优化策略(基于IEEE30节点六机模型),基于粒子群算法的电力系统最优潮流 以IEEE30节点的六机为对象,建立考虑功率平衡、机组爬坡约束、出力限制约束的

    粒子群优化算法下的电力系统经济调度与经济成本优化策略(基于IEEE30节点六机模型),基于粒子群算法的电力系统最优潮流 以IEEE30节点的六机为对象,建立考虑功率平衡、机组爬坡约束、出力限制约束的电力系统经济调度模型,采用粒子群算法对模型进行求解,得到六个机组的最优运行计划,确定系统最优运行成本。 这段程序主要是一个基于粒子群优化算法(PSO)的电力系统调度程序。它用于优化电力系统中火电、风电和光伏发电机组的出力,以实现最小化发电成本和最小化失负荷量的目标。 该程序的主要功能是根据给定的负荷数据、初始机组出力和风光发电数据,通过PSO算法求解最优的机组出力方案。它涉及到的领域是电力系统调度和优化。 程序的主要思路如下: 1. 首先,定义了一些参数,如最大迭代次数、搜索空间维数、粒子个数等。 2. 然后,加载了电力系统的一些数据,包括机组的发电成本、负荷数据、风电数据和光伏数据。 3. 接下来,使用PSO算法对每个小时的机组出力进行优化,得到最优的机组出力方案。 4. 计算每个小时的发电成本、失负荷量、弃风弃光量等指标。 5. 绘制机组出力曲线、风电出力曲线、光伏出力曲线、负荷曲

  • 西门子200smart恒压供水系统:三拖三配置,精准压力控制,稳定快速PLC内部PID调节,昆仑通态触摸屏操作,适用多场景 ,西门子200smart恒压供水(3托3) 功能: 三拖三(3台变频3台水泵

    西门子200smart恒压供水系统:三拖三配置,精准压力控制,稳定快速PLC内部PID调节,昆仑通态触摸屏操作,适用多场景。,西门子200smart恒压供水(3托3) 功能: 三拖三(3台变频3台水泵),3台水泵循环软启,定时轮工作。 硬件:采用西门子200smart +昆仑通态触摸屏。 优点: 1.一对一变频,一台变频器拖一台泵,解决变频切的繁琐和安全性; 2.适用于大小功率,主要应用于压力精度要求高设备或行业。 3.采用ABB acs510变频器 (也可用其他牌子没有限制) 4.采用plc内部PID,速度快,系统稳定; ,核心关键词:西门子200smart恒压供水;三拖三;水泵循环软启;定时轮换工作;昆仑通态触摸屏;一对一变频;变频切换;压力精度;ABB acs510变频器;PLC内部PID;系统稳定。,西门子200smart智能恒压供水系统:三拖三变频软启与高效能PID控制

  • shim-ia32-15-8.el7.x64-86.rpm.tar.gz

    1、文件内容:shim-ia32-15-8.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/shim-ia32-15-8.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、安装指导:私信博主,全程指导安装

  • 基于粒子群算法的配电网重构方案:降低网损与电压偏差,实现放射型网架潮流优化计算 ,基于粒子群算法的配电网重构 基于IEEE33节点电网,以网损和电压偏差最小为目标,考虑系统的潮流约束,采用粒子群算法求

    基于粒子群算法的配电网重构方案:降低网损与电压偏差,实现放射型网架潮流优化计算。,基于粒子群算法的配电网重构 基于IEEE33节点电网,以网损和电压偏差最小为目标,考虑系统的潮流约束,采用粒子群算法求解优化模型,得到确保放射型网架的配电网重构方案。 这个程序主要是一个潮流计算程序,用于解决电力系统中的潮流问题。潮流计算是电力系统分析中的基本问题之一,它用于确定电力系统中各个节点的电压幅值和相位,以及各个支路的功率流动情况。 该程序的主要思路是通过迭代的方式,不断修正节点的电压值,直到满足一定的收敛条件为止。程序首先对电力系统的节点和支路进行初始化,然后根据给定的初始条件,计算各个节点的注入功率和注入电流。接下来,根据节点的注入功率和注入电流,构建雅可比矩阵,并求解修正方程,得到节点电压的修正量。然后,根据修正量对节点电压进行修正,并重新计算节点的注入功率和注入电流。重复以上步骤,直到满足收敛条件为止。 在程序运行过程中,涉及到的主要内容包括电力系统的节点和支路参数、节点的注入功率和注入电流的计算、雅可比矩阵的构建、修正方程的求解、节点电压的修正、收敛条件的判断等。程序中还包括了一些

  • MMC-HVDC仿真模型详解:探索基于PSCAD的多端柔性直流输电技术基础模型与MMC逆变器应用,MMC-HVDC仿真模型,pscad柔性直流输电仿真mmc仿真模型,双端mmc模型,MMC为21电平N

    MMC-HVDC仿真模型详解:探索基于PSCAD的多端柔性直流输电技术基础模型与MMC逆变器应用,MMC-HVDC仿真模型,pscad柔性直流输电仿真mmc仿真模型,双端mmc模型,MMC为21电平NLM和均压控制,还有多端如张北直流电网以及基本mmc逆变器,自己为biye网上收集的一些觉得有用的基础模型 ,核心关键词:MMC-HVDC仿真模型; pscad柔性直流输电仿真; 双端mmc模型; 21电平NLM; 均压控制; 多端直流电网; 张北直流电网; 基本mmc逆变器; biye网上收集的基础模型。,"MMC-HVDC仿真模型研究:多端NLM均压控制与基本逆变器模型应用"

  • 2024定制版抢单支付系统源码-开代理-自动抢单接单系统源码

    2024定制版抢单支付系统源码|开代理|自动抢单接单 运行环境:php7.2+mysql5.6+宝塔面板

  • 基于Cadence的两级放大电路版图设计:通过LVS与DRC验证,实现高效集成电路功能,Cadence 两级放大电路,包括版图,已通过lvs ,drc检查 Cadence两级放大电路已经完成版图设计

    基于Cadence的两级放大电路版图设计:通过LVS与DRC验证,实现高效集成电路功能,Cadence 两级放大电路,包括版图,已通过lvs ,drc检查 Cadence两级放大电路已经完成版图设计,并且已经通过了LVS(Layout vs. Schematic)和DRC(Design Rule Check)的检查。 电路设计和集成电路设计工具。电路设计是指通过选择和配置电子元件,将它们连接在一起以实现特定功能的过程。而集成电路设计工具是用于设计和验证集成电路的软件工具,其中Cadence是一个常用的集成电路设计工具。 集成电路设计是现代电子技术中的重要领域,它涉及到将多个电子元件(如晶体管、电容器、电阻器等)集成到单个芯片上,以实现各种功能。集成电路设计工具是帮助工程师进行电路设计和验证的软件工具,它们提供了各种功能和模块,包括原理图设计、版图设计、模拟仿真、验证和布局布线等。 Cadence是一个知名的集成电路设计工具供应商,他们提供了一系列的软件工具,包括用于原理图设计的Capture、用于版图设计的Virtuoso、用于模拟仿真的Spectre等。这些工具能够帮助工程

Global site tag (gtag.js) - Google Analytics