iOS之網路請求NSURLSession剖析

来源:http://www.cnblogs.com/fishbay/archive/2017/07/20/7209595.html
-Advertisement-
Play Games

`2013`年的`WWDC`大會上,蘋果推出了`NSURLSession`,對`Foundation URL`載入系統進行了徹底的重構,提供了更豐富的`API`來處理網路請求,如:支持`http2.0`協議、直接把數據下載到磁碟、同一`session`發送多個請求、下載是多線程非同步處理和提供全局的`... ...


2013年的WWDC大會上,蘋果推出了NSURLSession,對Foundation URL載入系統進行了徹底的重構,提供了更豐富的API來處理網路請求,如:支持http2.0協議、直接把數據下載到磁碟、同一session發送多個請求、下載是多線程非同步處理和提供全局的session並可以統一配置等等,提高了NSURLSession的易用性、靈活性,更加地適合移動開發的需求。


NSURLSession的介紹

1. session類型

Default session

+defaultSessionConfiguration 返回一個標準的 configuration,這個配置實際上與 NSURLConnection 的網路堆棧(networking stack)是一樣的,具有相同的共用 NSHTTPCookieStorage,共用 NSURLCache 和共用 NSURLCredentialStorage

Ephemeral session

+ephemeralSessionConfiguration 返回一個預設配置,這個配置中不會對緩存Cookie 和證書進行持久性的存儲,這對於實現像秘密瀏覽這種功能來說是很理想的。

Background session

+backgroundSessionConfiguration:(NSString *)identifier 的獨特之處在於,它會創建一個後臺 session。後臺 session 不同於常規的,普通的 session,它甚至可以在應用程式掛起,退出或者崩潰的情況下進行上傳和下載任務。初始化時指定的標識符,被用於向任何可能在進程外恢復後臺傳輸的守護進程。

2. 配置屬性

基本配置

HTTPAdditionalHeaders 指定了一組預設的可以設置請求(outbound request)的數據頭。這對於跨 session 共用信息,如內容類型、語言、用戶代理和身份認證,是很有用的。

    // 設置請求的header
    NSString *userPasswordString = [NSString stringWithFormat:@"%@:%@", user, password];
    NSData * userPasswordData = [userPasswordString dataUsingEncoding:NSUTF8StringEncoding];
    NSString *base64EncodedCredential = [userPasswordData base64EncodedStringWithOptions:0];
    NSString *authString = [NSString stringWithFormat:@"Basic %@", base64EncodedCredential];
    NSString *userAgentString = @"AppName/com.example.app (iPhone 5s; iOS 7.0.2; Scale/2.0)";

    configuration.HTTPAdditionalHeaders = @{@"Accept": @"application/json",
                                            @"Accept-Language": @"en",
                                            @"Authorization": authString,
                                            @"User-Agent": userAgentString};
  • networkServiceType 對標準的網路流量、網路電話、語音、視頻,以及由一個後臺進程使用的流量進行了區分。大多數應用程式都不需要設置這個。
  • allowsCellularAccessdiscretionary 被用於節省通過蜂窩網路連接的帶寬。對於後臺傳輸的情況,推薦大家使用 discretionary 這個屬性,而不是 allowsCellularAccess,因為前者會把 WiFi 和電源的可用性考慮在內。
  • timeoutIntervalForRequesttimeoutIntervalForResource 分別指定了對於請求和資源的超時間隔。許多開發人員試圖使用 timeoutInterval 去限制發送請求的總時間,但其實它真正的含義是:分組(packet)之間的時間。實際上我們應該使用 timeoutIntervalForResource 來規定整體超時的總時間,但應該只將其用於後臺傳輸,而不是用戶實際上可能想要去等待的任何東西。
  • HTTPMaximumConnectionsPerHostFoundation 框架中 URL 載入系統的一個新的配置選項。它曾經被 NSURLConnection 用於管理私有的連接池。現在有了 NSURLSession,開發者可以在需要時限制連接到特定主機的數量。
  • HTTPShouldUsePipelining 這個屬性在 NSMutableURLRequest 下也有,它可以被用於開啟 HTTP 管線化(HTTP pipelining),這可以顯著降低請求的載入時間,但是由於沒有被伺服器廣泛支持,預設是禁用的。
  • sessionSendsLaunchEvents 是另一個新的屬性,該屬性指定該 session 是否應該從後臺啟動。
  • connectionProxyDictionary 指定了 session 連接中的代理伺服器。同樣地,大多數面向消費者的應用程式都不需要代理,所以基本上不需要配置這個屬性。

Cookie 策略

  • HTTPCookieStorage 存儲了 session 所使用的 cookie。預設情況下會使用 NSHTTPCookieShorage+sharedHTTPCookieStorage 這個單例對象,這與 NSURLConnection 是相同的。
  • HTTPCookieAcceptPolicy 決定了什麼情況下 session 應該接受從伺服器發出的 cookie
  • HTTPShouldSetCookies 指定了請求是否應該使用 session 存儲的 cookie,即 HTTPCookieSorage 屬性的值。

安全策略

  • URLCredentialStorage 存儲了 session 所使用的證書。預設情況下會使用 NSURLCredentialStorage+sharedCredentialStorage 這個單例對象,這與 NSURLConnection 是相同的。
  • TLSMaximumSupportedProtocolTLSMinimumSupportedProtocol 確定 `session 是否支持 SSL 協議。

緩存策略

  • URLCachesession 使用的緩存。預設情況下會使用 NSURLCache+sharedURLCache 這個單例對象,這與 NSURLConnection 是相同的。
  • requestCachePolicy 指定了一個請求的緩存響應應該在什麼時候返回。這相當於 NSURLRequest-cachePolicy 方法。

自定義協議

protocolClasses 用來配置特定某個 session 所使用的自定義協議(該協議是 NSURLProtocol 的子類)的數組。

3. NSURLSessionTask

NSURLsessionTask 是一個抽象類,其下有 3 個實體子類可以直接使用:NSURLSessionDataTaskNSURLSessionUploadTaskNSURLSessionDownloadTask。這 3 個子類封裝了現代程式三個最基本的網路任務:獲取數據,比如 JSON 或者 XML,上傳文件和下載文件。

圖1

不同於直接使用 alloc-init 初始化方法,task 是由一個 NSURLSession 創建的。每個 task 的構造方法都對應有或者沒有 completionHandler 這個 block 的兩個版本。

4. 代理

針對NSURLsessionTask的代理,根代理為NSURLSessionDelegate,其它的代理直接或者間接繼承自改代理,如:NSURLSessionTaskDelegateNSURLSessionDataDelegateNSURLSessionDownloadDelegate。其中根代理NSURLSessionDelegate主要處理鑒權、後臺下載任務完成通知等等,NSURLSessionTaskDelegate主要處理收到鑒權響應、任務結束(無論是正常還是異常),NSURLSessionDataDelegate處理數據的接收、dataTaskdownloadTask、緩存等,NSURLSessionDownloadDelegate主要處理數據下載、數據進度通知等。

圖2

NSURLSession應用

1. NSURLSessionDataTask 發送 GET 請求

    //確定請求路徑
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520&pwd=520&type=JSON"];
    //創建 NSURLSession 對象
    NSURLSession *session = [NSURLSession sharedSession];

 /**
  根據對象創建 Task 請求,預設在子線程中解析數據

  url  方法內部會自動將 URL 包裝成一個請求對象(預設是 GET 請求)
  completionHandler  完成之後的回調(成功或失敗)

  param data     返回的數據(響應體)
  param response 響應頭
  param error    錯誤信息
  */
    NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:
             ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

        //解析伺服器返回的數據
        NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }];
    //發送請求(執行Task)
    [dataTask resume];

2. NSURLSessionDataTask 發送 POST 請求

    //確定請求路徑
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
    //創建可變請求對象
    NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:url];
    //修改請求方法
    requestM.HTTPMethod = @"POST";
    //設置請求體
    requestM.HTTPBody = [@"username=520&pwd=520&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
    //創建會話對象
    NSURLSession *session = [NSURLSession sharedSession];
    //創建請求 Task
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:requestM completionHandler:
             ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

        //解析返回的數據
        NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }];
    //發送請求
    [dataTask resume];

3. NSURLSessionDataTask 設置代理髮送請求

     //確定請求路徑
     NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
     //創建可變請求對象
     NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:url];
     //設置請求方法
     requestM.HTTPMethod = @"POST";
     //設置請求體
     requestM.HTTPBody = [@"username=520&pwd=520&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
     //創建會話對象,設置代理
 /**
  第一個參數:配置信息
  第二個參數:設置代理
  第三個參數:隊列,如果該參數傳遞nil 那麼預設在子線程中執行
  */
     NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
                              delegate:self delegateQueue:nil];
     //創建請求 Task
     NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:requestM];
     //發送請求
     [dataTask resume];

代理方法:

-(void)URLSession:(NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask 
didReceiveResponse:(nonnull NSURLResponse *)response 
completionHandler:(nonnull void (^)(NSURLSessionResponseDisposition))completionHandler {
     //子線程中執行
     NSLog(@"接收到伺服器響應的時候調用 -- %@", [NSThread currentThread]);

     self.dataM = [NSMutableData data];
     //預設情況下不接收數據
     //必須告訴系統是否接收伺服器返回的數據
     completionHandler(NSURLSessionResponseAllow);
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {

     NSLog(@"接受到伺服器返回數據的時候調用,可能被調用多次");
     //拼接伺服器返回的數據
     [self.dataM appendData:data];
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {

     NSLog(@"請求完成或者是失敗的時候調用");
     //解析伺服器返回數據
     NSLog(@"%@", [[NSString alloc] initWithData:self.dataM encoding:NSUTF8StringEncoding]);
}

設置代理之後的強引用問題

  • NSURLSession 對象在使用的時候,如果設置了代理,那麼 session 會對代理對象保持一個強引用,在合適的時候應該主動進行釋放
  • 可以在控制器調用 viewDidDisappear 方法的時候來進行處理,通過調用 invalidateAndCancel 方法或者是 finishTasksAndInvalidate 方法來釋放對代理對象的強引用。

其中,invalidateAndCancel是直接取消請求然後釋放代理對象,而finishTasksAndInvalidate是等請求完成之後釋放代理對象。

4. NSURLSessionDownloadTask 簡單下載

    //確定請求路徑
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"];
    //創建請求對象
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    //創建會話對象
    NSURLSession *session = [NSURLSession sharedSession];
    //創建會話請求
    //優點:該方法內部已經完成了邊接收數據邊寫沙盒的操作,解決了記憶體飆升的問題
    NSURLSessionDownloadTask *downTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {

        //預設存儲到臨時文件夾 tmp 中,需要剪切文件到 cache
        NSLog(@"%@", location);//目標位置
        NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]  
                         stringByAppendingPathComponent:response.suggestedFilename];

     /**
      fileURLWithPath:有協議頭
      URLWithString:無協議頭
      */
        [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];

    }];
    //發送請求
    [downTask resume];

以上方法無法監聽下載進度,如要獲取下載進度,可以使用代理的方式進行下載。

5. NSURLSessionDownloadTask 代理方式

    NSURL * url = [NSURL URLWithString:@"http://e.hiphotos.baidu.com/image/pic/item/63d0f703918fa0ec14b94082249759ee3c6ddbc6.jpg"];
    NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate:self delegateQueue: [NSOperationQueue mainQueue]];

    NSURLSessionDownloadTask * downloadTask =[ defaultSession downloadTaskWithURL:url];
    [downloadTask resume];

代理方法:

// 接收數據,可能多次被調用
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    float progress = totalBytesWritten * 1.0/totalBytesExpectedToWrite;
    
    // 主線程更新UI
    dispatch_async(dispatch_get_main_queue(),^ {
        [self.process setProgress:progress animated:YES];
    });
}

// 3.下載完成之後調用該方法
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    NSString *catchDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    NSString *filePath = [catchDir stringByAppendingPathComponent:@"app.dmg"];
    
    NSError *fileError = nil;
    NSURL *fileURL = [NSURL fileURLWithPath:filePath];
    [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&fileError];
    
    if (fileError) {
        NSLog(@"保存下載文件出錯:%@", fileError);
    } else {
        NSLog(@"保存成功:%@", filePath);
    }
}

暫停和恢復下載:

方式一:

// 暫停下載
- (IBAction)suspendDownload {
    if (self.session) {
        __weak typeof(self) weakSelf = self;
        [self.task cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
            weakSelf.receivedData = resumeData;
        }];
    }
}

// 恢復下載
- (IBAction)resumeDownload {
    if (self.session) {
        self.task = [self.session downloadTaskWithResumeData:self.receivedData];
    }
    
    [self.task resume];
}

方式二:

//暫停
[self.downloadTask suspend];
//恢復
[self.downloadTask resume];

6. NSURLSessionDownloadTask 後臺下載

// 後臺session
- (NSURLSession* ) backgroundURLSession {
    static NSURLSession * session = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSString * identifier = @"com.yourcompany.appId.BackgroundSession";
        NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
        session = [NSURLSession sessionWithConfiguration:sessionConfig
                                                delegate:self
                                           delegateQueue:[NSOperationQueue mainQueue]];
    });
    
    return session;
}

// 創建並啟動任務
- (void)beginDownloadWithUrl:(NSString *)downloadURLString {
    NSURL *downloadURL = [NSURL URLWithString:downloadURLString];
    NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL];
    NSURLSession *session = [self backgroundURLSession];
    NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request];
    
    [downloadTask resume];
}

appDelegate中實現application:handleEventsForBackgroundURLSession:completionHandler:方法,在後臺所有的任務完成後會調用給方法,但是我一直沒有調用成功,原因未知,高手可以告知一下

- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {

    NSURLSession *backgroundSession = [self backgroundURLSession];
    
    NSLog(@"Rejoining session with identifier %@ %@", identifier, backgroundSession);
    
    // 保存 completion handler 以在處理 session 事件後更新 UI
    [self addCompletionHandler:completionHandler forSession:identifier];
}

handleEventsForBackgroundURLSession 方法是在後臺下載的所有任務完成後才會調用。如果後臺任務完成且應用被殺掉,啟動應用程式後,該方法會在 application:didFinishLaunchingWithOptions:方法被調用之後被調用。

//NSURLSessionDelegate委托方法,會在NSURLSessionDownloadDelegate委托方法後執行
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
     NSLog(@"Background URL session %@ finished events.\n", session);
}

之後會調用接收完成的方法:

/*
 * 在該方法結束前,需要處理location指向的文件,因為方法結束後,臨時文件會被銷毀
 * 如果用模擬器保存,會出錯,因為模擬器上app退出後再啟動是,路徑會不一樣,導致找不到後臺下載的文件;而用真機調試則無此問題 !!!
 */
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location 
{}

/*
 * 該方法下載成功和失敗都會回調,只是失敗的是error是有值的,
 * 在下載失敗時,error的userinfo屬性可以通過NSURLSessionDownloadTaskResumeData
 * 這個key來取到resumeData(和上面的resumeData是一樣的),再通過resumeData恢復下載
 */
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error 
{}

7. NSURLSessionUploadTask上傳任務

    NSURL*URL = [NSURLURLWithString:@"http://example.com/upload"];

    NSURLRequest*request = [NSURLRequestrequestWithURL:URL];

    NSData*data = ...;

    NSURLSession*session = [NSURLSessionsharedSession];

    NSURLSessionUploadTask*uploadTask = [session uploadTaskWithRequest:request fromData:datacompletionHandler:^(NSData*data, NSURLResponse *response,NSError*error) {

        // ...
    }];

[uploadTask resume];

註意事項

1. 後臺下載的配置和限制

作為一個必須實現的委托,您不能對NSURLSession使用簡單的基於 block的回調方法。後臺啟動應用程式,是相對耗費較多資源的,所以總是採用HTTP重定向。後臺傳輸服務只支持HTTPHTTPS,你不能使用自定義的協議。系統會根據可用的資源進行優化,在任何時候你都不能強制傳輸任務在後臺進行。

另外,要註意的是在後臺會話中,NSURLSessionDataTasks 是完全不支持的,你應該只出於短期的、小請求等使用這些任務,而不是用來下載或上傳。

2. 後臺啟動新的下載

蘋果會對後臺的下載任務進行限制,大致流程如下:

  • 蘋果的NSURLSession這個類會維護一個Delay值(即延時執行時間),用於後臺啟動任務延時執行時使用;
  • 當在後臺啟動一個新任務時,蘋果會對這個任務進行延時執行,延時時間蘋果那邊是有一個預設的延時時間,當後臺啟動的任務數越多,這個值就會成2N-1冪倍增長;
  • 比如:假設蘋果設定的延時時間為Delay。當在後臺啟動了第一個任務時,這個任務的延時時間為Delay,這個任務會在Delay時間後開始執行;當啟動在後臺啟動第二個任務時,這個任務的延時時間為:2 * Delay,當啟動第三個任務是,該任務的延時執行時間即為:2 * 2 * Delay;以此類推,在後臺啟動第N個任務是,該任務的延時執行時間為:2^(N-1)次方 * Delay
  • 但是在應用從後臺切到前臺或者重新啟動時,這個延時時間會重置。

參考示例:

https://github.com/BirdandLion/NSURLSessionDemo

參考資料:
Life Cycle of a URL Session

http://www.jianshu.com/p/63e2ad28459f

http://www.jianshu.com/p/b0ddadd34037

http://www.jianshu.com/p/1211cf99dfc3

http://www.jianshu.com/p/02a5a896c9ed


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Android精選源碼 android實現最簡潔的標簽(label/tag)選擇/展示控制項 懂得智能配色的ImageView,還能給自己設置多彩的陰影哦 NicePhoto-基於 Kotlin 開發的 一款超簡單的圖片瀏覽+設置壁紙... 你的桌面從未如此炫酷(一句代碼搞定) 仿支付寶首頁下拉刷新 ...
  • 在開發`iOS`的客戶端應用時,經常需要從伺服器下載圖片,雖然系統提供了下載工具:NSData、NSURLSession等等方法,但是考慮到圖片下載過程中,需要考慮的因素比較多,比如:非同步下載、圖片緩存、錯誤處理、編碼解碼等,以及實際需要中根據不同網路載入不同畫質的圖片等等需求,因此下載操作不是一個... ...
  • 一,效果圖。 二,代碼。 ViewController.h ViewController.m ...
  • 最近研究了一下項目的組件化,把`casa`、`bang`、`limboy`的有關組件化的博客看了一遍,學到了不少東西,對目前業界的組件化方案有了一定的瞭解。這些高質量的博客大致討論了組件化的三種方案:`url-block`、`protocol-class`(和`url-controller`類似)、... ...
  • 一、概念與總結 1、淺拷貝 淺拷貝就是對記憶體地址的複製,讓目標對象指針和源對象指向同一片記憶體空間,當記憶體銷毀的時候,指向這片記憶體的幾個指針需要重新定義才可以使用,要不然會成為野指針。 淺拷貝就是拷貝指向原來對象的指針,使原對象的引用計數+1,可以理解為創建了一個指向原對象的新指針而已,並沒有創建一個 ...
  • <! Category 本文目錄 dispatch_queue_t、dispatch_block_t dispatch_sync、dispatch_async dispatch_set_target_queue、dispatch_object_t dispatch_after、dispatch_ti ...
  • iOS開發中經常會用到數據和模型的互相轉換,大致有兩種轉換方式:1.手動寫轉換的代碼,2.利用開源庫進行轉換。常用的開源庫有:`JSONModel`、`Mantle`、`MJExtension`、`YYModel`等等,本文主要介紹一下`MJExtension`的底層實現,看一看小碼哥如何設計這個輕... ...
  • 年前升級了`Cocoapods`庫,從`0.39`升級到了`1.2.0-beta`版,然後用模擬器和真機測試都是沒有問題的,均可以成功編譯。今天測試人員要測試包,準備`archive`打包時,卻提示:`ld: library not found for -lMantle`,瞬間感覺哪兒不對,明明可以... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...