美文网首页
多线程网络

多线程网络

作者: 恒筠 | 来源:发表于2018-03-05 10:53 被阅读0次

第一节

1.基本概念

01 进程
    进程是指在系统中正在运行的一个应用程序。每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内。
02 线程
    2-1 基本概念
        1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程),线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行。
    2-2 线程的串行
        1个线程中任务的执行是串行的,如果要在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务。也就是说,在同一时间内,1个线程只能执行1个任务。
03 多线程
    3-1 基本概念
        即1个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务。
    3-2 线程的并行
        并行即同时执行。比如同时开启3条线程分别下载3个文件(分别是文件A、文件B、文件C。
    3-3 多线程并发执行的原理
        在同一时间里,CPU只能处理1条线程,只有1条线程在工作(执行)。多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换),如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
    3-4 多线程优缺点
        优点
            1)能适当提高程序的执行效率。
            2)能适当提高资源利用率(CPU、内存利用率)
        缺点
            1)开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能。
            2)线程越多,CPU在调度线程上的开销就越大。
            3)程序设计更加复杂:比如线程之间的通信、多线程的数据共享
--------------------------
04 多线程在iOS开发中的应用
    4-1 主线程
        1)一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”。
        2)作用。刷新显示UI,处理UI事件。
    4-2 使用注意
        1)不要将耗时操作放到主线程中去处理,会卡住线程。
        2)和UI相关的刷新操作必须放到主线程中进行处理。
--------------------------
05 iOS中多线程的实现方案
    5-1 pthread
        a.特点:
          1)一套通用的多线程API
          2)适用于Unix\Linux\Windows等系统
          3)跨平台\可移植
          4)使用难度大
        b.使用语言:c语言
        c.使用频率:几乎不用
        d.线程生命周期:由程序员进行管理

    5-2 NSThread
        a.特点:
            1)使用更加面向对象
            2)简单易用,可直接操作线程对象
        b.使用语言:OC语言
        c.使用频率:偶尔使用
        d.线程生命周期:由程序员进行管理

    5-3 GCD
        a.特点:
            1)旨在替代NSThread等线程技术
            2)充分利用设备的多核(自动)
        b.使用语言:C语言
        c.使用频率:经常使用
        d.线程生命周期:自动管理

    5-4 NSOperation
        a.特点:
            1)基于GCD(底层是GCD)
            2)比GCD多了一些更简单实用的功能
            3)使用更加面向对象
        b.使用语言:OC语言
        c.使用频率:经常使用
        d.线程生命周期:自动管理

2.线程安全

01 前提:多个线程访问同一块资源会发生数据安全问题
02 解决方案:加互斥锁
03 相关代码:@synchronized(self){}
04 专业术语-线程同步
05 原子和非原子属性(是否对setter方法加锁)

3.线程间通信

-(void)touchesBegan:(nonnull NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event
{
//    [self download2];

    //开启一条子线程来下载图片
    [NSThread detachNewThreadSelector:@selector(downloadImage) toTarget:self withObject:nil];
}

-(void)downloadImage
{
    //1.确定要下载网络图片的url地址,一个url唯一对应着网络上的一个资源
    NSURL *url = [NSURL URLWithString:@"http://p6.qhimg.com/t01d2954e2799c461ab.jpg"];

    //2.根据url地址下载图片数据到本地(二进制数据
    NSData *data = [NSData dataWithContentsOfURL:url];

    //3.把下载到本地的二进制数据转换成图片
    UIImage *image = [UIImage imageWithData:data];

    //4.回到主线程刷新UI
    //4.1 第一种方式
//    [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];

    //4.2 第二种方式
//    [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];

    //4.3 第三种方式
    [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
}

4.GCD

  • GCD基本使用【重点】
01 异步函数+并发队列:开启多条线程,并发执行任务
02 异步函数+串行队列:开启一条线程,串行执行任务
03 同步函数+并发队列:不开线程,串行执行任务
04 同步函数+串行队列:不开线程,串行执行任务
05 异步函数+主队列:不开线程,在主线程中串行执行任务
06 同步函数+主队列:不开线程,串行执行任务(注意死锁发生)
07 注意同步函数和异步函数在执行顺序上面的差异
  • GCD的栅栏函数

在使用栅栏函数的时候,苹果官方明确规定栅栏函数只有在和使用create函数自己的创建的并发队列一起使用的时候才有效(没有给出具体原因)

第二节

1.单例模式

//提供一个static修饰的全局变量,强引用着已经实例化的单例对象实例
static XMGTools *_instance;

//类方法,返回一个单例对象
+(instancetype)shareTools
{
     //注意:这里建议使用self,而不是直接使用类名Tools(考虑继承)

    return [[self alloc]init];
}

//保证永远只分配一次存储空间
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
    //使用GCD中的一次性代码
//    static dispatch_once_t onceToken;
//    dispatch_once(&onceToken, ^{
//        _instance = [super allocWithZone:zone];
//    });

    //使用加锁的方式,保证只分配一次存储空间
    @synchronized(self) {
        if (_instance == nil) {
            _instance = [super allocWithZone:zone];
        }
    }
    return _instance;
}
/*
1. mutableCopy 创建一个新的可变对象,并初始化为原对象的值,新对象的引用计数为 1;
2. copy 返回一个不可变对象。分两种情况:(1)若原对象是不可变对象,那么返回原对象,并将其引用计数加 1 ;(2)若原对象是可变对象,那么创建一个新的不可变对象,并初始化为原对象的值,新对象的引用计数为 1。
*/
//让代码更加的严谨
-(nonnull id)copyWithZone:(nullable NSZone *)zone
{
//    return [[self class] allocWithZone:zone];
    return _instance;
}

-(nonnull id)mutableCopyWithZone:(nullable NSZone *)zone
{
    return _instance;
}

2.Opertion

(1)NSOperation中的两种队列

01 主队列 通过mainQueue获得,凡是放到主队列中的任务都将在主线程执行
02 非主队列 直接alloc init出来的队列。非主队列同时具备了并发和串行的功能,通过设置最大并发数属性来控制任务是并发执行还是串行执行

(2)设置最大并发数【控制任务并发和串行】

NSOperationQueue *queue = [[NSOperationQueue alloc]init];

    //2.设置最大并发数
    //注意点:该属性需要在任务添加到队列中之前进行设置
    //该属性控制队列是串行执行还是并发执行
    //如果最大并发数等于1,那么该队列是串行的,如果大于1那么是并行的
    //系统的最大并发数有个默认的值,为-1,如果该属性设置为0,那么不会执行任何任务
    queue.maxConcurrentOperationCount = 2;

(3)暂停和恢复以及取消

  //设置暂停和恢复
    //suspended设置为YES表示暂停,suspended设置为NO表示恢复
    //暂停表示不继续执行队列中的下一个任务,暂停操作是可以恢复的
    if (self.queue.isSuspended) {
        self.queue.suspended = NO;
    }else
    {
        self.queue.suspended = YES;
    }

    //取消队列里面的所有操作
    //取消之后,当前正在执行的操作的下一个操作将不再执行,而且永远都不在执行,就像后面的所有任务都从队列里面移除了一样
    //取消操作是不可以恢复的
    [self.queue cancelAllOperations];

(4)自定义NSOperation取消操作

-(void)main
{
    //耗时操作1
    for (int i = 0; i<1000; i++) {
        NSLog(@"任务1-%d--%@",i,[NSThread currentThread]);
    }
    NSLog(@"+++++++++++++++++++++++++++++++++");

    //苹果官方建议,每当执行完一次耗时操作之后,就查看一下当前队列是否为取消状态,如果是,那么就直接退出
    //好处是可以提高程序的性能
    if (self.isCancelled) {
        return;
    }

    //耗时操作2
    for (int i = 0; i<1000; i++) {
        NSLog(@"任务1-%d--%@",i,[NSThread currentThread]);
    }

    NSLog(@"+++++++++++++++++++++++++++++++++");
}

第三节

第三方框架SDWebImage

(1)SDWebImage基本使用

    03 系统级内存警告如何处理(面试)
    //取消当前正在进行的所有下载操作
    [[SDWebImageManager sharedManager] cancelAll];

    //清除缓存数据(面试)
    //cleanDisk:删除过期的文件数据,计算当前未过期的已经下载的文件数据的大小,如果发现该数据大小大于我们设置的最大缓存数据大小,那么程序内部会按照按文件数据缓存的时间从远到近删除,知道小于最大缓存数据为止。

    //clearMemory:直接删除文件,重新创建新的文件夹
    //[[SDWebImageManager sharedManager].imageCache cleanDisk];
    [[SDWebImageManager sharedManager].imageCache clearMemory];

    04 SDWebImage默认的缓存时间是1周

(2)SDWebImage内部结构
[图片上传失败...(image-a9bcc0-1520218368724)]

Runloop基础知识

  • Runloop对象
  (1)在iOS开发中有两套api来访问Runloop
      a.foundation框架【NSRunloop】
      b.core foundation框架【CFRunloopRef】
  (2)NSRunLoop和CFRunLoopRef都代表着RunLoop对象,它们是等价的,可以互相转换
  (3)NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面)

  • Runloop参考资料
(1)苹果官方文档
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html

(2)CFRunLoopRef开源代码下载地址:
http://opensource.apple.com/source/CF/CF-1151.16/
  • Runloop与线程
1.Runloop和线程的关系:一个Runloop对应着一条唯一的线程
 问题:如何让子线程不死
 回答:给这条子线程开启一个Runloop
2.Runloop的创建:主线程Runloop已经创建好了,子线程的runloop需要手动创建
3.Runloop的生命周期:在第一次获取时创建,在线程结束时销毁
  
注意点:开一个子线程创建runloop,不是通过alloc init方法创建,而是直接通过调用currentRunLoop方法来创建,它本身是一个懒加载的。
4.在子线程中,如果不主动获取Runloop的话,那么子线程内部是不会创建Runloop的。可以下载CFRunloopRef的源码,搜索_CFRunloopGet0,查看代码。
5.Runloop对象是利用字典来进行存储,而且key是对应的线程Value为该线程对应的Runloop。
  • Runloop相关类
    [图片上传失败...(image-bed3f1-1520218368724)]
  • Runloop和相关类之间的关系图

[图片上传失败...(image-756f0d-1520218368724)]

CFRunloopModeRef

  1.CFRunloopModeRef代表着Runloop的运行模式
  2.一个Runloop中可以有多个mode,一个mode里面又可以有多个source\observer\timer等等
  3.每次runloop启动的时候,只能指定一个mode,这个mode被称为该Runloop的当前mode
  4.如果需要切换mode,只能先退出当前Runloop,再重新指定一个mode进入
  5.这样做主要是为了分割不同组的定时器等,让他们相互之间不受影响
  6.系统默认注册了5个mode
      a.kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
      b.UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
      c.UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
      d.GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
      e.kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode

  • CFRunloopSourceRef
  1.是事件源也就是输入源,有两种分类模式;
    一种是按照苹果官方文档进行划分的
    另一种是基于函数的调用栈来进行划分的(source0和source1)。
  2.具体的分类情况
      (1)以前的分法
          Port-Based Sources
          Custom Input Sources
          Cocoa Perform Selector Sources

      (2)现在的分法
          Source0:非基于Port的
          Source1:基于Port的
  3.可以通过打断点的方式查看一个方法的函数调用栈
  • CFRunLoopObserverRef

/创建一个runloop监听者
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(),kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {

        NSLog(@"监听runloop状态改变---%zd",activity);
    });

    //为runloop添加一个监听者
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);

    CFRelease(observer);
  • Runloop运行逻辑

[图片上传失败...(image-b83f47-1520218368724)]

  • Runloop应用
常驻线程:在子线程中开启一个runloop
自动释放池
    第一次创建:进入runloop的时候
    最后一次释放:runloop退出的时候
    其它创建和释放:当runloop即将休眠的时候会把之前的自动释放池释放,然后重新创建一个新的释放池

第四节

static

static关键字会在声明变量的时候分配内存,在程序运行期间只分配一次内存。之后再访问时,实际都是在访问原先分配的内存
如果使用static来修饰局部变量,那么局部变量在代码块结束后将不会回收,下次使用保持上次使用后的值。
如果使用static来修饰全局变量,那么表示该全局变量只在本文件中有效,外界无法使用extern来引用。static变量的作用域被限制在定义变量的当前文件中,其它文件是不能访问的

NSURLConnection使用

  • 步骤
    01 设置请求路径
    02 创建请求对象(默认是GET请求,且已经默认包含了请求头)
    03 使用NSURLSession sendsync方法发送网络请求
    04 接收到服务器的响应后,解析响应体

JSON解析

  (1)JSON的格式很像OC中的字典和数组
  (2)标准JSON格式key必须是双引号
  • JSON解析方案
  a.第三方框架  JSONKit\SBJSON\TouchJSON
  b.苹果原生(NSJSONSerialization)
  • OC对象和JSON数据格式之间的一一对应关系
//OC对象和JSON数据之间的一一对应关系
-(void)oCWithJSON
{
    //JSON的各种数据格式
    //NSString *test = @"\"wendingding\"";
    //NSString *test = @"true";
    NSString *test = @"{\"name\":\"wendingding\"}";

    //把JSON数据->OC对象,以便查看他们之间的一一对应关系
    //注意点:如何被解析的JSON数据如果既不是字典也不是数组(比如是NSString), 那么就必须使用这NSJSONReadingAllowFragments
    id obj = [NSJSONSerialization JSONObjectWithData:[test dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];

    NSLog(@"%@", [obj class]);


    /* JSON数据格式和OC对象的一一对应关系
         {} -> 字典
         [] -> 数组
         "" -> 字符串
         10/10.1 -> NSNumber
         true/false -> NSNumber
         null -> NSNull
     */
}
}

XML解析

  • XML解析
 a.XML解析的两种方式
        001 SAX:从根元素开始,按顺序一个元素一个元素的往下解析,可用于解析大、小文件
        002 DOM:一次性将整个XML文档加载到内存中,适合较小的文件
    b.解析XML的工具
        001 苹果原生NSXMLParser:使用SAX方式解析,使用简单
        002 第三方框架
            libxml2:纯C语言的,默认包含在iOS SDK中,同时支持DOM和SAX的方式解析
            GDataXML:采用DOM方式解析,该框架由Goole开发,是基于xml2的
  • 使用GDataParser解析XML的步骤和方法

//4.0 配置环境
// 001 先导入框架,然后按照框架使用注释配置环境
// 002 GDataXML框架是MRC的,所以还需要告诉编译器以MRC的方式处理GDataXML的代码

//4.1 加载XML文档(使用的是DOM的方式一口气把整个XML文档都吞下)
    GDataXMLDocument *doc = [[GDataXMLDocument alloc]initWithData:data options:kNilOptions error:nil];

//4.2 获取XML文档的根元素,根据根元素取出XML中的每个子元素
  NSArray * elements = [doc.rootElement elementsForName:@"video"];

//4.3 取出每个子元素的属性并转换为模型
for (GDataXMLElement *ele in elements) {

    XMGVideo *video = [[XMGVideo alloc]init];
    video.name = [ele attributeForName:@"name"].stringValue;
    video.length = [ele attributeForName:@"length"].stringValue.integerValue;
    video.url = [ele attributeForName:@"url"].stringValue;
    video.image = [ele attributeForName:@"image"].stringValue;
    video.ID = [ele attributeForName:@"id"].stringValue;

    //4.4 把转换好的模型添加到tableView的数据源self.videos数组中
    [self.videos addObject:video];
}

如何解决字典和数组中输出乱码的问题

给字典和数组添加一个分类,重写descriptionWithLocale方法,在该方法中拼接元素格式化输出。
-(nonnull NSString *)descriptionWithLocale:(nullable id)locale

第五节

大文件的下载

实现思路:边接收数据边写文件以解决内存越来越大的问题
有两种方法:一种是实用文件句柄下载NSFileHandle
另外一种是实用NSOutStream输出流下载

第一种



//当接收到服务器响应的时候调用,该方法只会调用一次
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    //0.获得当前要下载文件的总大小(通过响应头得到)
    NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
    self.totalLength = res.expectedContentLength;
    NSLog(@"%zd",self.totalLength);

    //创建一个新的文件,用来当接收到服务器返回数据的时候往该文件中写入数据
    //1.获取文件管理者
    NSFileManager *manager = [NSFileManager defaultManager];

    //2.拼接文件的全路径
    //caches文件夹路径
    NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];

    NSString *fullPath = [caches stringByAppendingPathComponent:res.suggestedFilename];
    self.fullPath  = fullPath;
    //3.创建一个空的文件
    [manager createFileAtPath:fullPath contents:nil attributes:nil];

}
//当接收到服务器返回的数据时会调用
//该方法可能会被调用多次
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{

    //1.创建一个用来向文件中写数据的文件句柄
    //注意当下载完成之后,该文件句柄需要关闭,调用closeFile方法
    NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:self.fullPath];

    //2.设置写数据的位置(追加)
    [handle seekToEndOfFile];

    //3.写数据
    [handle writeData:data];

    //4.计算当前文件的下载进度
    self.currentLength += data.length;

    NSLog(@"%f",1.0* self.currentLength/self.totalLength);
    self.progressView.progress = 1.0* self.currentLength/self.totalLength;
}

第二种

/*
     第一个参数:二进制的流数据要写入到哪里
     第二个参数:采用什么样的方式写入流数据,如果YES则表示追加,如果是NO则表示覆盖
     */
    NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:fullPath append:YES];

    //只要调用了该方法就会往文件中写数据
    //如果文件不存在,那么会自动的创建一个
    [stream open];
    self.stream = stream;

    //2.当接收到数据的时候写数据
    //使用输出流写数据
    /*
     第一个参数:要写入的二进制数据
     第二个参数:要写入的数据的大小
     */
    [self.stream write:data.bytes maxLength:data.length];

    //3.当文件下载完毕的时候关闭输出流
    //关闭输出流
    [self.stream close];
    self.stream = nil;

大文件断点下载

实现思路:在下载文件的时候不再是整块的从头开始下载,而是看当前文件已经下载到哪个地方,然后从该地方接着往后面下载。可以通过在请求对象中设置请求头实现。

解决方法


    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

    //2.1 设置下载文件的某一部分
    // 只要设置HTTP请求头的Range属性, 就可以实现从指定位置开始下载
    /*
     表示头500个字节:Range: bytes=0-499
     表示第二个500字节:Range: bytes=500-999
     表示最后500个字节:Range: bytes=-500
     表示500字节以后的范围:Range: bytes=500-
     */
    NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentLength];
    [request setValue:range forHTTPHeaderField:@"Range"];

注意点
下载进度并判断是否需要重新创建文件

//获得当前要下载文件的总大小(通过响应头得到)
    NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;

    //注意点:res.expectedContentLength获得是本次请求要下载的文件的大小(并非是完整的文件的大小)
    //因此:文件的总大小 == 本次要下载的文件大小+已经下载的文件的大小
    self.totalLength = res.expectedContentLength + self.currentLength;

    NSLog(@"----------------------------%zd",self.totalLength);

    //0 判断当前是否已经下载过,如果当前文件已经存在,那么直接返回
    if (self.currentLength >0) {
        return;
    }

输出流

 //1.创建一个数据输出流
    /*
     第一个参数:二进制的流数据要写入到哪里
     第二个参数:采用什么样的方式写入流数据,如果YES则表示追加,如果是NO则表示覆盖
     */
    NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:fullPath append:YES];

    //只要调用了该方法就会往文件中写数据
    //如果文件不存在,那么会自动的创建一个
    [stream open];
    self.stream = stream;

    //2.当接收到数据的时候写数据
    //使用输出流写数据
    /*
     第一个参数:要写入的二进制数据
     第二个参数:要写入的数据的大小
     */
    [self.stream write:data.bytes maxLength:data.length];

    //3.当文件下载完毕的时候关闭输出流
    //关闭输出流
    [self.stream close];
    self.stream = nil;

使用多线程下载文件思路

01 开启多条线程,每条线程都只下载文件的一部分(通过设置请求头中的Range来实现)
02 创建一个和需要下载文件大小一致的文件,判断当前是那个线程,根据当前的线程来判断下载的数据应该写入到文件中的哪个位置。(假设开5条线程来下载10M的文件,那么线程1下载0-2M,线程2下载2-4M一次类推,当接收到服务器返回的数据之后应该先判断当前线程是哪个线程,假如当前线程是线程2,那么在写数据的时候就从文件的2M位置开始写入)
03 代码相关:使用NSFileHandle这个类的seekToFileOfSet方法,来向文件中特定的位置写入数据。
04 技术相关
    a.每个线程通过设置请求头下载文件中的某一个部分
    b.通过NSFileHandle向文件中的指定位置写数据

文件的压缩和解压缩

  • 使用ZipArchive来压缩和解压缩文件需要添加依赖库(libz),使用需要包含SSZipArchive文件

文件的上传

如何获得文件的MIMEType类型

  • 直接对该对象发送一个异步网络请求,在响应头中通过response.MIMEType拿到文件的MIMEType类型
- (NSString *)getMIMEType
{
    NSString *filePath = @"/Users/文顶顶/Desktop/备课/其它/swift.md";

    NSURLResponse *response = nil;
    [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath]] returningResponse:&response error:nil];
    return response.MIMEType;
}

//对该文件发送一个异步请求,拿到文件的MIMEType
- (void)MIMEType
{

    //    NSString *file = @"file:///Users/文顶顶/Desktop/test.png";

    [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:@"/Users/文顶顶/Desktop/test.png"]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * __nullable response, NSData * __nullable data, NSError * __nullable connectionError) {
        //       response.MIMEType
        NSLog(@"%@",response.MIMEType);

    }];
}

NSURLConnection和Runloop

  • 开子线程发送网络请求的注意点,适用于自动发送网络请求模式
//在子线程中发送网络请求-调用startf方法发送
-(void)createNewThreadSendConnect1
{
    //1.创建一个非主队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];

    //2.封装操作,并把任务添加到队列中执行
    [queue addOperationWithBlock:^{

        NSLog(@"%@",[NSThread currentThread]);
        //2-1.确定请求路径
        NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=dd&pwd=ww&type=JSON"];

        //2-2.创建请求对象
        NSURLRequest *request = [NSURLRequest requestWithURL:url];

        //2-3.使用NSURLConnection设置代理,发送网络请求
        NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:YES];

        //2-4.设置代理方法在哪个队列中执行,如果是非主队列,那么代理方法将再子线程中执行
        [connection setDelegateQueue:[[NSOperationQueue alloc]init]];

        //2-5.发送网络请求
        //注意:start方法内部会把当前的connect对象作为一个source添加到当前线程对应的runloop中
        //区别在于,如果调用start方法开发送网络请求,那么再添加source的过程中,如果当前runloop不存在
        //那么该方法内部会自动创建一个当前线程对应的runloop,并启动。
        [connection start];

    }];
}

//在子线程中发送网络请求-自动发送网络请求
-(void)createNewThreadSendConnect2
{
    NSLog(@"-----");
    //1.创建一个非主队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];

    //2.封装操作,并把任务添加到队列中执行
    [queue addOperationWithBlock:^{

        //2-1.确定请求路径
        NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=dd&pwd=ww&type=JSON"];

        //2-2.创建请求对象
        NSURLRequest *request = [NSURLRequest requestWithURL:url];

        //2-3.使用NSURLConnection设置代理,发送网络请求
        //注意:该方法内部虽然会把connection添加到runloop,但是如果当前的runloop不存在,那么不会主动创建。
        NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];

        //2-4.设置代理方法在哪个队列中执行,如果是非主队列,那么代理方法将再子线程中执行
        [connection setDelegateQueue:[[NSOperationQueue alloc]init]];

        //2-5 创建当前线程对应的runloop,并开启
       [[NSRunLoop currentRunLoop]run];
    }];
}

第六节

NSURLSession下载文件-代理

  • 遵守代理协议,实现代理方法(3个相关的代理方法)
/*
 1.当接收到服务器响应的时候调用
     session:发送请求的session对象
     dataTask:根据NSURLSession创建的task任务
     response:服务器响应信息(响应头)
     completionHandler:通过该block回调,告诉服务器端是否接收返回的数据
 */
-(void)URLSession:(nonnull NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask didReceiveResponse:(nonnull NSURLResponse *)response completionHandler:(nonnull void (^)(NSURLSessionResponseDisposition))completionHandler

/*
 2.当接收到服务器返回的数据时调用
 该方法可能会被调用多次
 */
-(void)URLSession:(nonnull NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask didReceiveData:(nonnull NSData *)data

/*
 3.当请求完成之后调用该方法
 不论是请求成功还是请求失败都调用该方法,如果请求失败,那么error对象有值,否则那么error对象为空
 */
-(void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error
  • 当接收到服务器响应的时候,告诉服务器接收数据(调用block)
//默认情况下,当接收到服务器响应之后,服务器认为客户端不需要接收数据,所以后面的代理方法不会调用
    //如果需要继续接收服务器返回的数据,那么需要调用block,并传入对应的策略

    /*
        NSURLSessionResponseCancel = 0, 取消任务
        NSURLSessionResponseAllow = 1,  接收任务
        NSURLSessionResponseBecomeDownload = 2, 转变成下载
        NSURLSessionResponseBecomeStream NS_ENUM_AVAILABLE(10_11, 9_0) = 3, 转变成流
    */

    completionHandler(NSURLSessionResponseAllow);

NSURLSessionDownloadTask实现大文件下载

    /*
 1.当接收到下载数据的时候调用,可以在该方法中监听文件下载的进度
 该方法会被调用多次
 totalBytesWritten:已经写入到文件中的数据大小
 totalBytesExpectedToWrite:目前文件的总大小
 bytesWritten:本次下载的文件数据大小
 */
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
/*
 2.恢复下载的时候调用该方法
 fileOffset:恢复之后,要从文件的什么地方开发下载
 expectedTotalBytes:该文件数据的总大小
 */
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
/*
 3.下载完成之后调用该方法
 */
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(nonnull NSURL *)location
/*
 4.请求完成之后调用
 如果请求失败,那么error有值
 */
-(void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error

使用NSURLSessionDataTask实现大文件离线断点下载(完整)

  • 关于网络请求请求头的设置(可以设置请求下载文件的某一部分)
 //1. 设置请求对象
    //1.1 创建请求路径
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];

    //1.2 创建可变请求对象
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

    //1.3 拿到当前文件的残留数据大小
    self.currentContentLength = [self FileSize];

    //1.4 告诉服务器从哪个地方开始下载文件数据
    NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentContentLength];
    NSLog(@"%@",range);

    //1.5 设置请求头
    [request setValue:range forHTTPHeaderField:@"Range"];
  • NSURLSession对象的释放
-(void)dealloc
{
    //在最后的时候应该把session释放,以免造成内存泄露
    //    NSURLSession设置过代理后,需要在最后(比如控制器销毁的时候)调用session的invalidateAndCancel或者resetWithCompletionHandler,才不会有内存泄露
    //    [self.session invalidateAndCancel];
    [self.session resetWithCompletionHandler:^{

        NSLog(@"释放---");
    }];
}

关于NSURLSessionConfiguration相关

01 作用:可以统一配置NSURLSession,如请求超时等
02 创建的方式和使用
//创建配置的三种方式
+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier NS_AVAILABLE(10_10, 8_0);

//统一配置NSURLSession
-(NSURLSession *)session
{
    if (_session == nil) {

        //创建NSURLSessionConfiguration
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];

        //设置请求超时为10秒钟
        config.timeoutIntervalForRequest = 10;

        //在蜂窝网络情况下是否继续请求(上传或下载)
        config.allowsCellularAccess = NO;

        _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    }
    return _session;
}

第七节

AFN框架基本使用

 - NSURLSession
        + AFURLSessionManager
        + AFHTTPSessionManager(封装了常用的 HTTP 方法)
            * GET
            * POST
            * UIKit + AFNetworking 分类
            * NSProgress :利用KVO

    - 半自动的序列化&反序列化的功能
        + AFURLRequestSerialization :请求的数据格式/默认是二进制的
        + AFURLResponseSerialization :响应的数据格式/默认是JSON格式
    - 附加功能
        + 安全策略
            * HTTPS
            * AFSecurityPolicy
        + 网络检测
            * 对苹果的网络连接检测做了一个封装
            * AFNetworkReachabilityManager

使用AFN下载文件

-(void)download
{
    //1.创建会话管理者
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];


    //2.创建请求对象
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_13.png"]];

    //3.创建下载Task
    /*
     第一个参数:请求对象
     第二个参数:进度回调
        downloadProgress.completedUnitCount :已经下载的数据
        downloadProgress.totalUnitCount:数据的总大小
     第三个参数:destination回调,该block需要返回值(NSURL类型),告诉系统应该把文件剪切到什么地方
        targetPath:文件的临时保存路径
        response:响应头信息
     第四个参数:completionHandler请求完成后回调
        response:响应头信息
        filePath:文件的保存路径,即destination回调的返回值
        error:错误信息
     */
    NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
        NSLog(@"%f",1.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount);

    } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {

        NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename];
        NSLog(@"%@\n%@",targetPath,fullPath);
        return [NSURL fileURLWithPath:fullPath];

    } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
        NSLog(@"%@",filePath);
    }];

    //4.执行Task
    [downloadTask resume];
}

AFN使用技巧

1.在开发的时候可以创建一个工具类,继承自我们的AFN中的请求管理者,再控制器中真正发请求的代码使用自己封装的工具类。
2.这样做的优点是以后如果修改了底层依赖的框架,那么我们修改这个工具类就可以了,而不用再一个一个的去修改。
3.该工具类一般提供一个单例方法,在该方法中会设置一个基本的请求路径。
4.该方法通常还会提供对GET或POST请求的封装。
5.在外面的时候通过该工具类来发送请求
6.单例方法:
+ (instancetype)shareNetworkTools
{
    static XMGNetworkTools *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 注意: BaseURL中一定要以/结尾
        instance = [[self alloc] initWithBaseURL:[NSURL URLWithString:@"http://120.25.226.186:32812/"]];
    });
    return instance;
}

AFN文件上传

1.文件上传拼接数据的第一种方式
[formData appendPartWithFileData:data name:@"file" fileName:@"xxoo.png" mimeType:@"application/octet-stream"];
2.文件上传拼接数据的第二种方式
 [formData appendPartWithFileURL:fileUrl name:@"file" fileName:@"xx.png" mimeType:@"application/octet-stream" error:nil];
3.文件上传拼接数据的第三种方式
 [formData appendPartWithFileURL:fileUrl name:@"file" error:nil];
4.【注】在资料中已经提供了一个用于文件上传的分类。

/*文件上传相关的代码如下*/
-(void)upload1
{
    //1.创建会话管理者
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

    //2.处理参数(非文件参数)
    NSDictionary *dict = @{
                           @"username":@"123"
                           };

    //3.发送请求上传文件
    /*
     第一个参数:请求路径(NSString类型)
     第二个参数:非文件参数,以字典的方式传递
     第三个参数:constructingBodyWithBlock 在该回调中拼接文件参数
     第四个参数:progress 进度回调
        uploadProgress.completedUnitCount:已经上传的数据大小
        uploadProgress.totalUnitCount:数据的总大小
     第五个参数:success 请求成功的回调
        task:上传Task
        responseObject:服务器返回的响应体信息(已经以JSON的方式转换为OC对象)
     第六个参数:failure 请求失败的回调
        task:上传Task
        error:错误信息
     */
    [manager POST:@"http://120.25.226.186:32812/upload" parameters:dict constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {

        UIImage *image = [UIImage imageNamed:@"Snip20160117_1"];
        NSData *imageData = UIImagePNGRepresentation(image);

        //在该block中拼接要上传的文件参数
        /*
         第一个参数:要上传的文件二进制数据
         第二个参数:文件参数对应的参数名称,此处为file是该台服务器规定的(通常会在接口文档中提供)
         第三个参数:该文件上传到服务后以什么名称保存
         第四个参数:该文件的MIMeType类型
         */
        [formData appendPartWithFileData:imageData name:@"file" fileName:@"123.png" mimeType:@"image/png"];

    } progress:^(NSProgress * _Nonnull uploadProgress) {
        NSLog(@"%f",1.0 * uploadProgress.completedUnitCount / uploadProgress.totalUnitCount);
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {

        NSLog(@"请求成功----%@",responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

        NSLog(@"请求失败----%@",error);
    }];
}

-(void)upload2
{
    //1.创建会话管理者
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

    //2.处理参数(非文件参数)
    NSDictionary *dict = @{
                           @"username":@"123"
                           };

    //3.发送请求上传文件
    /*
     第一个参数:请求路径(NSString类型)
     第二个参数:非文件参数,以字典的方式传递
     第三个参数:constructingBodyWithBlock 在该回调中拼接文件参数
     第四个参数:progress 进度回调
        uploadProgress.completedUnitCount:已经上传的数据大小
        uploadProgress.totalUnitCount:数据的总大小
     第五个参数:success 请求成功的回调
        task:上传Task
        responseObject:服务器返回的响应体信息(已经以JSON的方式转换为OC对象)
     第六个参数:failure 请求失败的回调
        task:上传Task
        error:错误信息
     */
    [manager POST:@"http://120.25.226.186:32812/upload" parameters:dict constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {

        NSURL *fileUrl = [NSURL fileURLWithPath:@"/Users/文顶顶/Desktop/Snip20160117_1.png"];


        //在该block中拼接要上传的文件参数
        //第一种拼接方法
        /*
         第一个参数:要上传的文件的URL路径
         第二个参数:文件参数对应的参数名称,此处为file是该台服务器规定的(通常会在接口文档中提供)
         第三个参数:该文件上传到服务后以什么名称保存
         第四个参数:该文件的MIMeType类型
         第五个参数:错误信息,传地址
         */
        //[formData appendPartWithFileURL:fileUrl name:@"file" fileName:@"1234.png" mimeType:@"image/png" error:nil];


        //第二种拼接方法:简写方法
        /*
         第一个参数:要上传的文件的URL路径
         第二个参数:文件参数对应的参数名称,此处为file
         第三个参数:错误信息
         说明:AFN内部自动获得路径URL地址的最后一个节点作为文件的名称,内部调用C语言的API获得文件的类型
         */
        [formData appendPartWithFileURL:fileUrl name:@"file" error:nil];

    } progress:^(NSProgress * _Nonnull uploadProgress) {
        NSLog(@"%f",1.0 * uploadProgress.completedUnitCount / uploadProgress.totalUnitCount);
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {

        NSLog(@"请求成功----%@",responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

        NSLog(@"请求失败----%@",error);
    }];
}

使用AFN进行序列化处理

1.AFN它内部默认把服务器响应的数据当做json来进行解析,所以如果服务器返回给我的不是JSON数据那么请求报错,这个时候需要设置AFN对响应信息的解析方式。AFN提供了三种解析响应信息的方式,分别是:
1)AFXMLParserResponseSerializer----XML
2) AFHTTPResponseSerializer---------默认二进制响应数据
3)AFJSONResponseSerializer---------JSON

2.还有一种情况就是服务器返回给我们的数据格式不太一致(开发者工具Content-Type:text/xml),那么这种情况也有可能请求不成功。解决方法:
1) 直接在源代码中修改,添加相应的Content-Type
2) 拿到这个属性,添加到它的集合中

使用AFN来检测网络状态


说明:可以使用AFN框架中的AFNetworkReachabilityManager来监听网络状态的改变,也可以利用苹果提供的Reachability来监听。建议在开发中直接使用AFN框架处理。
 */
//使用AFN框架来检测网络状态的改变
-(void)AFNReachability
{
    //1.创建网络监听管理者
    AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];

    //2.监听网络状态的改变
    /*
     AFNetworkReachabilityStatusUnknown          = 未知
     AFNetworkReachabilityStatusNotReachable     = 没有网络
     AFNetworkReachabilityStatusReachableViaWWAN = 3G
     AFNetworkReachabilityStatusReachableViaWiFi = WIFI
     */
    [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        switch (status) {
            case AFNetworkReachabilityStatusUnknown:
                NSLog(@"未知");
                break;
            case AFNetworkReachabilityStatusNotReachable:
                NSLog(@"没有网络");
                break;
            case AFNetworkReachabilityStatusReachableViaWWAN:
                NSLog(@"3G");
                break;
            case AFNetworkReachabilityStatusReachableViaWiFi:
                NSLog(@"WIFI");
                break;

            default:
                break;
        }
    }];

    //3.开始监听
    [manager startMonitoring];
}

数据安全

01 攻城利器:Charles(公司中一般都使用该工具来抓包,并做网络测试)
注意:Charles在使用中的乱码问题,可以显示包内容,然后打开info.plist文件,找到java目录下面的VMOptions,在后面添加一项:-Dfile.encoding=UTF-8
02 MD5消息摘要算法是不可逆的。
03 数据加密的方式和规范一般公司会有具体的规定,不必多花时间。

HTTPS的基本使用

  • 如果是自己使用NSURLSession来封装网络请求,涉及代码如下。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];

    NSURLSessionDataTask *task =  [session dataTaskWithURL:[NSURL URLWithString:@"https://www.apple.com"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }];
    [task resume];
}

 只要请求的地址是HTTPS的, 就会调用这个代理方法
 我们需要在该方法中告诉系统, 是否信任服务器返回的证书
 Challenge: 挑战 质问 (包含了受保护的区域)
 protectionSpace : 受保护区域
 NSURLAuthenticationMethodServerTrust : 证书的类型是 服务器信任
 */
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
    //    NSLog(@"didReceiveChallenge %@", challenge.protectionSpace);
    NSLog(@"调用了最外层");
    // 1.判断服务器返回的证书类型, 是否是服务器信任
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        NSLog(@"调用了里面这一层是服务器信任的证书");
        /*
         NSURLSessionAuthChallengeUseCredential = 0,                     使用证书
         NSURLSessionAuthChallengePerformDefaultHandling = 1,            忽略证书(默认的处理方式)
         NSURLSessionAuthChallengeCancelAuthenticationChallenge = 2,     忽略书证, 并取消这次请求
         NSURLSessionAuthChallengeRejectProtectionSpace = 3,            拒绝当前这一次, 下一次再询问
         */
//        NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];

        NSURLCredential *card = [[NSURLCredential alloc]initWithTrust:challenge.protectionSpace.serverTrust];
        completionHandler(NSURLSessionAuthChallengeUseCredential , card);
    }
}
  • 如果是使用AFN框架,那么我们不需要做任何额外的操作,AFN内部已经做了处理。

Cocoapods的安装


1.先升级Gem
    sudo gem update --system
2.切换cocoapods的数据源
    【先删除,再添加,查看】
    gem sources --remove https://rubygems.org/
    gem sources -a https://ruby.taobao.org/
    gem sources -l
3.安装cocoapods
    sudo gem install cocoapods
    或者(如10.11系统)sudo gem install -n /usr/local/bin cocoapods
4.将Podspec文件托管地址从github切换到国内的oschina
    【先删除,再添加,再更新】
    pod repo remove master
    pod repo add master http://git.oschina.net/akuandev/Specs.git
    pod repo add master https://gitcafe.com/akuandev/Specs.git
    pod repo update
5.设置pod仓库
    pod setup
6.测试
    【如果有版本号,则说明已经安装成功】
    pod --version
7.利用cocoapods来安装第三方框架
    01 进入要安装框架的项目的.xcodeproj同级文件夹
    02 在该文件夹中新建一个文件podfile
    03 在文件中告诉cocoapods需要安装的框架信息
        a.该框架支持的平台
        b.适用的iOS版本
        c.框架的名称
        d.框架的版本
8.安装
pod install --no-repo-update
pod update --no-repo-update

9.说明
platform :ios, '8.0' 用来设置所有第三方库所支持的iOS最低版本
pod 'SDWebImage','~>2.6' 设置框架的名称和版本号
版本号的规则:
'>1.0'    可以安装任何高于1.0的版本
'>=1.0'   可以安装任何高于或等于1.0的版本
'<1.0'    任何低于1.0的版本
'<=1.0'   任何低于或等于1.0的版本
'~>0.1'   任何高于或等于0.1的版本,但是不包含高于1.0的版本
'~>0'     任何版本,相当于不指定版本,默认采用最新版本号

10.使用pod install命令安装框架后的大致过程:
01 分析依赖:该步骤会分析Podfile,查看不同类库之间的依赖情况。如果有多个类库依赖于同一个类库,但是依赖于不同的版本,那么cocoaPods会自动设置一个兼容的版本。
02 下载依赖:根据分析依赖的结果,下载指定版本的类库到本地项目中。
03 生成Pods项目:创建一个Pods项目专门用来编译和管理第三方框架,CocoaPods会将所需的框架,库等内容添加到项目中,并且进行相应的配置。
04 整合Pods项目:将Pods和项目整合到一个工作空间中,并且设置文件链接。

注意:遇到问题可以参考https://www.jianshu.com/p/2f61a90f49f3

Base64补充


1.Base64简单说明
    描述:Base64可以成为密码学的基石,非常重要。
    特点:可以将任意的二进制数据进行Base64编码
    结果:所有的数据都能被编码为并只用65个字符就能表示的文本文件。
    65字符:A~Z a~z 0~9 + / =
    对文件进行base64编码后文件数据的变化:编码后的数据~=编码前数据的4/3,会大1/3左右。
    
2.命令行进行Base64编码和解码
    编码:base64 123.png -o 123.txt
    解码:base64 123.txt -o test.png -D


3.终端测试命令
        $ echo -n A | base64
        $ echo -n QQ== |base64 -D

加密相关


网络应用程序数据的原则:

1. 在网络上"不允许"传输用户隐私数据的"明文"
2. 在本地"不允许"保存用户隐私数据的"明文"

加密相关

1. base64 编码格式
2. 密码学演化 "秘密本"-->RSA

RSA简单说明:加密算法算法是公开的,加密方式如下:

- "公钥"加密,"私钥"解密
- "私钥"加密,"公钥"解密

目前流行的加密方式:
---------------
- 哈希(散列)函数
    - MD5
    - SHA1
    - SHA256

- 对称加密算法
    - DES
    - 3DES
    - AES(高级密码标准,美国国家安全局使用的)

- 非对称加密算法(RSA)

散列函数:
---------------
特点:
    - 算法是公开的
    - "对相同的数据加密,得到的结果是一样的"
    - 对不同的数据加密,得到的结果是定长的,MD5对不同的数据进行加密,得到的结果都是 32 个字符长度的字符串
    - 信息摘要,信息"指纹",是用来做数据识别的!
    - 不能反算的

用途:
    - 密码,服务器并不需要知道用户真实的密码!
    - 搜索
        张老师 杨老师 苍老师
        苍老师 张老师 杨老师

        张老师            1bdf605991920db11cbdf8508204c4eb
        杨老师             2d97fbce49977313c2aae15ea77fec0f
        苍老师             692e92669c0ca340eff4fdcef32896ee

        如何判断:对搜索的每个关键字进行三列,得到三个相对应的结果,按位相加结果如果是一样的,那搜索的内容就是一样的!
    - 版权
        版权保护,文件的识别。

破解:
    - http://www.cmd5.com 记录超过24万亿条,共占用160T硬盘 的密码数据,通过对海量数据的搜索得到的结果!

提升MD5加密安全性,有两个解决办法
1. 加"盐"(佐料)
2. HMAC:给定一个"秘钥",对明文进行加密,并且做"两次散列"!-> 得到的结果,还是 32 个字符

相关文章

  • iOS知识体系

    UI 网络 多线程

  • 多线程、网络-整理中

    多线程、网络-整理中

  • 多线程网络

    第一节 1.基本概念 2.线程安全 3.线程间通信 4.GCD GCD基本使用【重点】 GCD的栅栏函数 在使用栅...

  • 多线程网络

    下文中,前面介绍一些简单的概念。虽然简单,但还是需要了解。后面介绍的pthread简单了解即可。GCD和NSOpe...

  • 多线程网络

    1.多线程的底层实现 1> 首先搞清楚什么是线程,什么是多线程 说起多线程,那么就不得不说什么是线程,而说起线程,...

  • 网络多线程

    怎么保证线程安全?只在主线程刷新UI。防止线程资源抢夺,要用@synchronized进行加锁保护。异步操作保证线...

  • 网络多线程

    第1天 1 基本概念 2 pthread 3 NSThread (1)NSThread的基本使用 (2)设置线程的...

  • 网络多线程

    Future的错误和状态 让异步耗时操作处理完成之后,再执行下面代码,Future前面需要添加await 下面我们...

  • 多线程面试

    首先分析多线程的使用环境: 多线程处理包括Core Data的多线程访问,UI的并行绘制,异步网络请求以及一些在运...

  • Redis各版本特性

    Redis6.0 多线程IO Redis 6引入多线程IO,但多线程部分只是用来处理网络数据的读写和协议解析,执行...

网友评论

      本文标题:多线程网络

      本文链接:https://www.haomeiwen.com/subject/iyiqfftx.html