iOS - NetRequest 网络数据请求

时间:2024-06-27 08:04:07

1、网络请求

1.1 网络通讯三要素

  • 1、IP 地址(主机名):

    • 网络中设备的唯一标示。不易记忆,可以用主机名(域名)。

      1. IP V4:
      • 0255.0255.0255.0255 ,共有 2^8^4 = 2^32 = 42 亿。
      1. 本地回环地址:
      • 每台机器都有自己的本地回环地址,ip 为 127.0.0.1 ,主机名为 localhost。如果 127.0.0.1 ping 不通,则网卡不正常。

      • 本地 hosts 文件修改,终端:

        • $ cd /etc
        • $ sudo vim hosts
        • $ 输入密码进入 hosts 文件编辑界面
        • $ 将光标移动到指定位置

          - 英文输入模式下按 i 键进入编辑状态,

          - 英文输入模式下按 esc 键进入命令状态,

          - 在命令状态下输入 :wq 回车,保存退出 hosts 文件。
  • 2、端口号:

    • 用于标示进程的逻辑地址,不同进程的标示。

    • 有效端口为 0 ~ 65535,其中 0 ~ 1024 由系统使用或者保留端口,开发中不要使用 1024 以下的端口。

      1. Netcat 的使用:
      • Netcat 是 Mac 终端下用于调试和检查网络的工具包,可用于创建 TCP/IP 连接。
      • 终端:$ nc -lk 12345,开启监听,终端将始终监听本地计算机 12345 端口的数据。
  • 3、传输协议(通讯的规则):

      1. TCP:传输控制协议:
      • 建立连接,形成传输数据的通道(建立连接的三次握手,断开连接的四次握手)。
      • 在连接中进行大数据传输,数据大小不收限制。
      • 通过三次握手完成连接,是可靠协议,数据安全送达。
      • 必须建立连接,效率会稍低。
      1. UDP:用户数据报协议:
      • 只管发送,不确认对方是否接收到。
      • 不需要建立连接,将数据及源和目的封装成数据包中,每个数据报的大小限制在 64K 之内。
      • 因为无需连接,因此是不可靠协议。
      • 不需要建立连接,速度快。
      • 应用场景:多媒体教室/网络流媒体。
      1. 常见网络协议:

        应用层协议 | 端口 | 说明

        -----------------|----------------|---------------------------------

        HTTP | 80 | 超文本传输协议

        HTTPS | 443 | HTTP+SSL,HTTP 的安全版

        FTP | 20, 21, 990 | 文件传输

        POP3 | 110 | 邮局协议

        SMTP | 25 | 简单邮件传输协议

        telnet | 23 | 远程终端协议

1.2 网络参考模型

ISO 参考模型 TCP/IP 参考 说明
应用层 应用层 FTP,HTTP,TELNET,SMTP,DNS 等协议
表示层
会话层
传输层 传输层 Socket 开发,TCP 协议,UDP 协议
网络层 网络互连层 路由器,IP 协议,ICMP 协议,ARP 协议,RARP 协议和 BOOTP 协议
数据链路层 网络接口层 交换机
物理层 网线

1.3 HTTP 请求

  • HTTP(HyperText Transfer Protocol 超文本传输协议)是一套计算机通过网络进行通信的规则。计算机专家设计出 HTTP,使 HTTP 客户(如 Web 浏览器)能够从 HTTP 服务器(Web 服务器)请求信息和服务。HTTP 是一种无状态的协议,无状态是指 Web 浏览器和 Web 服务器之间不需要建立持久的连接,这意味着当一个客户端向服务器端发出请求,然后 Web 服务器返回响应(response),连接就被关闭了,在服务器端不保留连接的有关信息。HTTP 遵循请求(Request)/应答(Response)模型。Web 浏览器向 Web 服务器发送请求,Web 服务器处理请求并返回适当的应答。所有 HTTP 连接都被构造成一套请求和应答。

1.3.1 HTTP 通信机制

  • HTTP 通信机制是在一次完整的 HTTP 通信过程中,Web 浏览器与 Web 服务器之间将完成下列 7 个步骤:

  • 1、建立 TCP 连接

    • 在 HTTP 工作开始之前,Web 浏览器首先要通过网络与 Web 服务器建立连接,该连接是通过 TCP 来完成的,该协议与 IP 协议共同构建 Internet,即著名的 TCP/IP 协议族,因此Internet 又被称作是 TCP/IP 网络。HTTP 是比 TCP 更高层次的应用层协议,根据规则,只有低层协议建立之后才能进行更层协议的连接,因此,首先要建立 TCP 连接,一般 TCP 连接的端口号是 80。
  • 2、Web 浏览器向 Web 服务器发送请求命令

    • 一旦建立了 TCP 连接,Web 浏览器就会向 Web 服务器发送请求命令。例如:GET /sample/hello.jsp HTTP/1.1
  • 3、Web 浏览器发送请求头信息

    • 浏览器发送其请求命令之后,还要以头信息的形式向 Web 服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。
  • 4、Web 服务器应答

    • 客户机向服务器发出请求后,服务器会给客户机回送应答,HTTP/1.1 200 OK,应答的第一部分是协议的版本号和应答状态码。
  • 5、Web 服务器发送应答头信息

    • 正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。
  • 6、Web 服务器向浏览器发送数据

    • Web 服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以 Content-Type 应答头信息所描述的格式发送用户所请求的实际数据。
  • 7、Web 服务器关闭 TCP 连接

    • 一般情况下,一旦 Web 服务器向浏览器发送了请求数据,它就要关闭 TCP 连接,然后如果浏览器或者服务器在其头信息加入了这行代码,Connection:keep-alive,TCP 连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。

1.3.2 HTTP 请求格式

  • 当浏览器向 Web 服务器发出请求时,它向服务器传递了一个数据块,也就是请求信息,HTTP 请求信息由 3 部分组成:

    • 请求行:请求方法 请求路径 协议/版本

    • 请求头

    • 请求体

    • 如:

      	GET /videos.jsp HTTP/1.1                   // 请求行
      Host: localhost // 请求头
      User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:36.0) Gecko/20100101 Firefox/36.0
      Accept: text/html,application/xhtml+xml,application/xml;q=0.9,* / *;q=0.8
      Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
      Accept-Encoding: gzip, deflate
      Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7
      Connection: keep-alive username=jinqiao&password=1234 // 请求体
  • 1、请求行:

    • 请求的第一行,其组成为:请求方法 请求路径 协议/版本

      • 请求行 :GET /videos.jsp HTTP/1.1

      • 请求方法 :GET

      • 请求路径 :/videos.jsp

      • 协议/版本:HTTP/1.1

    • (1) 请求方法:

      • 根据 HTTP 标准,HTTP 请求可以使用多种请求方法。例如:HTTP1.1 支持 7 种请求方法:GET、POST、HEAD、OPTIONS、PUT、DELETE 和 TARCE。在 Internet 应用中,最常用的方法是 GET 和 POST。

      • GET 方法:

        • GET 方法是默认的 HTTP 请求方法,通过请求 URL 得到资源。我们日常用 GET 方法来提交表单数据,然而用 GET 方法提交的表单数据只经过了简单的编码,同时它将作为 URL 的一部分向 Web 服务器发送,因此,如果使用 GET 方法来提交表单数据就存在着安全隐患上。例如:

          Http://127.0.0.1/login.jsp?Name=zhangshi&Age=30&Submit=%cc%E+%BD%BB

        • 从上面的 URL 请求中,很容易就可以辩认出表单提交的内容。(?之后的内容)另外由于 GET 方法提交的数据是作为 URL 请求的一部分所以提交的数据量不能太大。

      • POST 方法:

        • POST 方法是 GET 方法的一个替代方法,用于添加新的内容。它主要是向 Web 服务器提交表单数据,尤其是大批量的数据。POST 方法克服了 GET 方法的一些缺点。通过 POST 方法提交表单数据时,数据不是作为 URL 请求的一部分而是作为标准数据传送给 Web 服务器,这就克服了 GET 方法中的信息无法保密和数据量太小的缺点。因此,出于安全的考虑以及对用户隐私的尊重,通常表单提交时采用 POST 方法。

        • 从编程的角度来讲,如果用户通过 GET 方法提交数据,则数据存放在 QUERY_STRING 环境变量中,而 POST 方法提交的数据则可以从标准输入流中获取。

      • DELETE:删除某个内容

      • CONNECT:用于代理进行传输,如使用 SSL

      • OPTIONS:询问可以执行哪些方法

      • PATCH:部分文档更改

      • PROPFIND, (wedav):查看属性

      • PROPPATCH, (wedav):设置属性

      • MKCOL, (wedav):创建集合(文件夹)

      • COPY, (wedav):拷贝

      • MOVE, (wedav):移动

      • LOCK, (wedav):加锁

      • UNLOCK (wedav):解锁

      • TRACE:用于远程诊断服务器

      • PUT :改

      • HEAD:类似于 GET, 只返回响应,不返回实体数据,用于检查对象是否存在,以及得到对象的元数据,下载数据前使用

      • apache2 中,可使用 Limit,LimitExcept 进行访问控制的方法包括:GET, POST, PUT, DELETE, CONNECT,OPTIONS, PATCH, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE,LOCK, 和 UNLOCK。其中, HEAD GET POST OPTIONS PROPFIND 是和读取相关的方法,MKCOL PUT DELETE LOCK UNLOCK COPY MOVE PROPPATCH 是和修改相关的方法。

    • (2) 请求路径:

      • 请求路径(URI) 完整地指定了要访问的网络资源,通常只要给出相对于服务器的根目录的相对目录即可,因此总是以 “/” 开头,最后,协议版本声明了通信过程中使用 HTTP 的版本。
    • (3) 协议/版本:

      • HTTP/1.1
  • 2、请求头:

    • 请求头包含许多有关客户端环境和请求体的有用信息。例如,请求头可以声明浏览器所用的语言,请求体的长度等。当我们打开一个网页时,浏览器要向网站服务器发送一个 HTTP 请求头,然后网站服务器根据 HTTP 请求头的内容生成当次请求的内容发送给浏览器。

      	Host: localhost                                                              // 请求主机
      User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:36.0) Gecko/20100101 Firefox/36.0 // 告诉服务器客户端的类型
      Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 // 告诉服务器客户端支持的格式
      Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 // 告诉服务器客户端的语言
      Accept-Encoding: gzip, deflate // 告诉服务器客户端支持的压缩格式
      Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7 // 告诉服务器客户端字符编码
      Connection: keep-alive // 告诉服务器连接类型
      Content-Length: 4817 // 提交给服务器的内容长度
      Content-Type: text/html // 提交给服务器的内容类型
      Authorization: authorString // 访问服务器的验证信息
      Range: bytes=0-499 // 指定要返回的数据范围
    • (1) Host

      • 表示请求的服务器网址。请求报头域主要用于指定被请求资源的 Internet 主机和端口号,它通常从 HTTP URL 中提取出来的。

      • 例如: 我们在浏览器中输入:http://www.guet.edu.cn/index.html,浏览器发送的请求消息中,就会包含 Host 请求报头域,如下:Host:http://www.guet.edu.cn,此处使用缺省端口号 80,若指定了端口号,则变成:Host:指定端口号

    • (2) User-Agent

      • 告诉 HTTP 服务器,客户端使用的操作系统和浏览器的名称和版本。

      • User-Agent(用户代理),简称 UA,它是一个特殊字符串头,使得服务器能够识别客户端使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。

      • 例如:User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; CIBA; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; InfoPath.2; .NET4.0E)

    • (3) Accept

      • 告诉服务器浏览器端可以接受的媒体类型。

      • 客户端支持的 MIME 类型分别是 text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8,优先顺序是它们从左到右的排列顺序。

      • text/html,application/xhtml+xml,application/xml 都是 MIME 类型,也可以称为媒体类型和内容类型,斜杠前面的是 type(类型),斜杠后面的是 subtype(子类型);type 指定大的范围,subtype 是 type 中范围更明确的类型,即大类中的小类。

        • text :用于标准化地表示的文本信息,文本消息可以是多种字符集和或者多种格式的;

        • text/html :表示 html 文档;

        • application :用于传输应用程序数据或者二进制数据;

        • application/xhtml+xml :表示 xhtml 文档;

        • application/xml :表示 xml 文档。

      • q 是权重系数,范围 0 =< q <= 1,q 值越大,请求越倾向于获得其 “;” 之前的类型表示的内容。

        • 若没有指定 q 值,则默认为 1。
        • 若被赋值为 0,则用于提醒服务器哪些是浏览器不接受的内容类型。
    • (4) Accept-Language

      • 告诉服务器浏览器申明自己接收的语言。

      • 语言跟字符集的区别:中文是语言,中文有多种字符集,比如 big5,gb2312,gbk 等等;

      • 例如:Accept-Language: en-us

      • zh-cn :表示简体中文

      • zh :表示中文

    • (5) Accept-Encoding

      • 浏览器申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,deflate),(注意:这不是只字符编码);

      • 例如:Accept-Encoding: gzip, deflate

      • gzip :是 GNU zip 的缩写,它是一个 GNU *软件的文件压缩程序,也经常用来表示 gzip 这种文件格式

      • deflate :是同时使用了 LZ77 算法与哈夫曼编码(Huffman Coding)的一个无损数据压缩算法

    • (6) Accept-Charset

      • 浏览器申明自己接收的字符集,这就是本文前面介绍的各种字符集和字符编码,如 gb2312,utf-8(通常我们说Charset包括了相应的字符编码方案);

      • GB2312 :是中国国家标准简体中文字符集,全称《信息交换用汉字编码字符集·基本集》,又称 GB0,由中国国家标准总局发布,1981年5月1日实施

      • utf-8 :是 Unicode 的一种变长字符编码又称万国码,由 Ken Thompson 于 1992 年创建,现在已经标准化为 RFC 3629

      • * :表示任意字符编码,虽然 q 都是等于 0.7,但明确指定的 GB2312,utf-8 比 * 具有更高的优先级

    • (7) Connection

      • 告诉服务器连接类型。

      • Connection:Keep-Alive :表示持久连接,长连接。当一个网页打开完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。

      • Connection:close :代表一个 Request 完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接会关闭,当客户端再次发送 Request,需要重新建立 TCP 连接。

      • 长连接:连上服务器就不断开。双方传递数据,不需要再次建立连接。

        • 优点:效率高,通常只有非常有钱的公司,才会提供长连接的服务。比如:apple 的消息推送服务就是长连接。
        • 缺点:用户体验不好,而且服务器的负担很重。
      • 短连接:连上服务器,获取完数据,就立即断开。HTTP 访问,都是短连接,使用情况非常多。

    • (8) Content-Length

      • 发送给HTTP服务器数据的长度。

      • 例如: Content-Length:38

    • (9) Content-Type

      • 提交给服务器的内容类型

      • 例如:Content-Type: application/x-www-form-urlencoded

    • (10) Authorization

      • 访问服务器的验证信息
    • (11) Range

      • 指定要返回的数据范围

      • - :用于分隔,前面的数字表示起始字节数,后面的数组表示截止字节数,没有表示到末尾。

        • 如:bytes=0-499 从 0 到 499 的头 500 个字节,bytes=500- 从 500 字节以后的所有字节
      • , :用于分组,可以一次指定多个 Range,不过很少用。

        • 如:bytes=500-599,800-899 同时指定几个范围
    • (12) Referer

      • 提供了 Request 的上下文信息的服务器,告诉服务器我是从哪个链接过来的,比如从我主页上链接到一个朋友那里,他的服务器就能够从 HTTP Referer中统计出每天有多少用户点击我主页上的链接访问他的网站。

      • 例如: Referer:http://translate.google.cn/?hl=zh-cn&tab=wT

    • (13) Pragma

      • 防止页面被缓存,在 HTTP/1.1 版本中,它和 Cache-Control:no-cache 作用一模一样,Pargma 只有一个用法,

      • 例如:Pragma:no-cache

    • (14) Cookie

      • 最重要的 header, 将 cookie 的值发送给 HTTP 服务器。
  • 3、请求体:

    • 请求头和请求体之间是一个空行,这个行非常重要,它表示请求头已经结束,接下来的是请求体。请求体中可以包含客户提交的查询字符串信息。如:

      username=jinqiao&password=1234

    • 在以上的例子的 HTTP 请求中,请求的正文只有一行内容。当然,在实际应用中,HTTP 请求体可以包含更多的内容。

1.3.3 HTTP 响应格式

  • HTTP 应答与 HTTP 请求相似,HTTP 响应也由 3 个部分构成,分别是:

    • 状态行:协议/版本 状态码 状态码描述

    • 响应头

    • 响应正文

    • 如:

      	HTTP/1.1 200 OK                         // 状态行
      Date: Wed, 29 Jun 2016 10:31:34 GMT // 响应头
      Server: Apache/2.4.18 (Unix)
      Content-Location: index.html.en
      Content-Length: 4817
      Content-Type: text/html
      Keep-Alive: timeout=5, max=100 <html> // 响应正文
      <body>
      <h1>It works!</h1>
      </body>
      </html>
  • 1、状态行:

    • HTTP 响应的第一行类似于 HTTP 请求的第一行,其组成为:协议/版本 状态码 状态码描述

      • 状态行 :HTTP/1.1 200 OK

      • 协议/版本:HTTP/1.1

      • 状态码 :200 OK

    • (1) 协议/版本:

      • HTTP/1.1
    • (2) 状态码:

      • HTTP 应答码也称为状态码,它反映了 Web 服务器处理 HTTP 请求状态。HTTP 应答码由 3 位数字构成,其中首位数字定义了应答码的类型:

        • 1XX-信息类(Information) :表示收到 Web 浏览器请求,正在进一步的处理中。
        • 2XX-成功类(Successful) :表示用户请求被正确接收,理解和处理例如:200 OK。
        • 3XX-重定向类(Redirection) :表示请求没有成功,客户必须采取进一步的动作。
        • 4XX-客户端错误(Client Error):表示客户端提交的请求有错误,例如:404 NOT Found,意味着请求中所引用的文档不存在。
        • 5XX-服务器错误(Server Error):表示服务器不能完成对请求的处理:如 500。
    • (3) 状态码描述:

      • 用来解析状态码的状态。

      • 如:

        • 200 OK :请求已成功,请求所希望的响应头或数据体将随此响应返回。
        • 404 Not Found :请求失败,请求所希望得到的资源未被在服务器上发现。
  • 2、响应头:

    • 响应头也和请求头一样包含许多有用的信息,例如服务器类型、日期时间、内容类型和长度等:

      	Date: Wed, 29 Jun 2016 10:31:34 GMT       // 访问日期
      Server: Apache/2.4.18 (Unix) // 访问服务器的类型
      Content-Location: index.html.en // 访问的文件名
      Content-Length: 4817 // 访问文件的大小
      Content-Type: text/html // 访问文件的类型
      Keep-Alive: timeout=5, max=100 // 连接类型
    • (1) Cache-Control

      • 这个是非常重要的规则。这个用来指定 Response-Request 遵循的缓存机制。各个指令含义如下

        • Cache-Control:Public 可以被任何缓存所缓存
        • Cache-Control:Private 内容只缓存到私有缓存中
        • Cache-Control:no-cache 所有内容都不会被缓存
    • (2) Content-Type

      • WEB 服务器告诉浏览器自己响应的对象的类型和字符集。

      • 例如:

        • Content-Type:text/html; charset=utf-8
        • Content-Type:text/html;charset=GB2312
        • Content-Type:image/jpeg
    • (3) Expires

      • 浏览器会在指定过期时间内使用本地缓存
      • 例如: Expires:Tue, 08 Feb 2022 11:35:14 GMT
    • (4) Last-Modified

      • 用于指示资源的最后修改日期和时间。
      • 例如:Last-Modified: Wed, 21 Dec 2011 09:09:10 GMT
    • (5) Server

      • 指明 HTTP 服务器的软件信息。
      • 例如:Server:Microsoft-IIS/7.5
    • (6) X-AspNet-Version

      • 如果网站是用 ASP.NET 开发的,这个 header 用来表示 ASP.NET 的版本。
      • 例如:X-AspNet-Version: 4.0.30319
    • (7) X-Powered-By

      • 表示网站是用什么技术开发的。
      • 例如:X-Powered-By: ASP.NET
    • (8) Connection

      • 例如:Connection:keep-alive 当一个网页打开完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。
      • 例如:Connection:close 代表一个 Request 完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接会关闭,当客户端再次发送 Request,需要重新建立 TCP 连接。
    • (9) Content-Length

      • 指明实体正文的长度,以字节方式存储的十进制数字来表示。在数据下行的过程中,Content-Length 的方式要预先在服务器中缓存所有数据,然后所有数据再一股脑儿地发给客户端。
      • 例如:Content-Length: 19847
    • (10) Date

      • 生成消息的具体时间和日期。
      • 例如:Date: Sat, 11 Feb 2012 11:35:14 GMT
  • 3、响应正文:

    • 响应正文就是服务器返回的 HTML 页面,响应头和正文之间也必须用空行分隔。

      	<html>
      <body>
      <h1>It works!</h1>
      </body>
      </html>

1.3.4 安全连接

  • Web 应用最常见的用途之一是电子商务,可以利用 Web 服务器端程序使人们能够网络购物,需要指出一点是,缺省情况下,通过 Internet 发送信息是不安全的,如果某人碰巧截获了你发给朋友的一则消息,他就能打开它,假想在里面有你的信用卡号码,这会有多么糟糕,幸运的是,很多 Web 服务器以及 Web 浏览器都有创立安全连接的能力,这样它们就可以安全的通信了。

  • 通过 Internet 提供安全连接最常见的标准是安全套接层(Secure Sockets layer,SSl)协议。SSL 协议是一个应用层协议(和 HTTP 一样),用于安全方式在 Web 上交换数据,SSL 使用公开密钥编码系统。从本质讲,这意味着业务中每一方都拥有一个公开的和一个私有的密钥。当一方使用另一方公开密钥进行编码时,只有拥有匹配密钥的人才能对其解码。简单来讲,公开密钥编码提供了一种用于在两方之间交换数据的安全方法,SSL 连接建立之后,客户和服务器都交换公开密钥,并在进行业务联系之前进行验证,一旦双方的密钥都通过验证,就可以安全地交换数据。

1.4 WebDav 文件服务器

  • 基于 http 协议的 "文件" 服务器,实现文件的上传/下载/修改/删除。

  • WebDav 权限,授权信息的格式 BASIC (用户名:口令)base64

  • 安全性并不高,密码很容易被拦截和破解。

  • 应用场景:开发企业级的管理系统,可以用 WebDav 搭建一个内部的文件管理服务器,只是在公司内网使用。

  • PUT 增,如果文件不存在,就是新增

  • DELETE 删

  • PUT 改

  • GET 查

  • GET 不需要设置权限验证,只是从服务器获取数据。

  • PUT/DELETE 都需要设置权限验证,新增(修改)/删除 服务器的内容,需要权限验证。

  • 请求状态码:

    • GET:

      • status code: 200 请求成功
      • status code: 404 文件不存在
    • PUT:

      • status code: 201 新增

      • status code: 204 修改

      • status code: 401 身份验证失败

    • DELETE:

      • status code: 204 删除成功

      • status code: 404 文件不存在

      • status code: 401 身份验证失败

1.5 移动客户端网络请求

  • 对于移动客户端来说,网络的重要性不言而喻。常见的网络请求有同步 GET,同步 POST,异步 GET,异步 POST。

  • 同步和异步网络请求的区别:

    • 1、同步网络请求,主线程负责数据的、视图的初始化和界面的展示等。同步网络数据请求也在主线程中进行,如果耗时较长,在数据请求完毕之前,其他线程一律不响应,会对主线程造成阻塞,造成程序假死现象,用 户体验极差。

    • 2、异步网络请求,主线程负责数据的、视图的初始化和界面的展示等。异步网络数据请求的时候,会在主线程之外单独开辟一个线程去处理网络请求,主线程依然处于可交互状态,程序运行流畅。

    • 许多开发者都会认为同步的连接将会堵塞主线程,其实这种观点是错误的。一个同步的连接是会阻塞调用它的线程。如果你在主线程中创建一个同步连接,没错,主线程会阻塞。但是如果你并不是从主线程开启的一个同步的连接,它将会类似异步的连接一样。因此这种情况并不会堵塞你的主线程。事实上,同步和异步的主要区别就是运行 runtime 为会异步连接创建一个线程,而同步连接则不会。

    • 使用 iOS 的网络接口(NSURLSession/NSURLConnection)开发时,上传和下载操作一般都是一条线程异步执行的。但是代理回调有可能是多个线程。

  • GET 请求和 POST 请求的区别:

    • GET 请求:

      • GET 的本质是 "得",从服务器拿数据,效率更高,从数学的角度来讲,GET 的结果是 "幂等" 的。
      • GET 请求能够被缓存。
      • 在 HTTP 协议定义中,没有对 GET 请求的数据大小限制,不过因为浏览器不同,一般限制在 2~8K 之间。
      • GET 请求的接口会包含参数部分,所有参数包装在 URL 中作为网址的一部分。并且服务器的访问日志会记录,不能传递敏感信息。
      • 参数格式:
        • 服务器资源路径与参数之间通过 ? 来间隔。
        • 每一个变量及值按照 变量名=变量值 方式设定,不能包含空格或者中文。
        • 多个参数使用 & 连接。
        • URL 字符串中如果包含中文,需要添加百分号转义。
    • POST 请求:

      • POST 的本质是 "给",向服务器发送数据,也可以获得服务器处理之后的结果,效率不如 GET。
      • POST 请求不能被缓存。
      • POST 提交数据比较大,大小靠服务器的设定值限制,PHP 通常限定 2M。
      • POST 请求会将服务器地址与参数分开,请求接口中只有服务器地址,而参数会作为请求的一部分,提交后台服务器。服务器日志不会记录参数,相对更安全。
      • 参数被包装成二进制的数据体,格式与 GET 基本一致,只是不包含 ?。所有涉及用户隐私的数据(密码,银行卡号)一定记住使用 POST 方式传递。
      • 虽然 GET 请求和 POST 请求都可以用来请求和提交数据,但是一般的 GET 多用于从后台请求数据,POST 多用于向后台提交数据。

1.6 iOS 网络请求基本设置

  • 1、url:NSURL

    • 要访问的资源路径。

      	+ (nullable instancetype)URLWithString:(NSString *)URLString;
      + (nullable instancetype)URLWithString:(NSString *)URLString relativeToURL:(nullable NSURL *)baseURL;
      1. URL 结构:
      	// 带方括号 [] 的为可选项
      scheme://host[:port]/path/[;parameters][?query]#fragment
      perty url description
      https://swiftinaction.com/example?section=5&ie=utf-8 URL 路径
      eme https 协议,http、https、ftp、file(本地文件)、data(数据)
      t swiftinaction.com 主机,可以利用 Reachability 框架判断能否连接到要访问的主机
      t nil 端口,nil 使用默认端口 80
      h example 路径,不包含协议头/主机地址/端口/参数
      ameters nil 参数,变量名和变量值成对出现,使用 = 分隔,多个参数使用 & 连接
      ry section=5&ie=utf-8 查询参数
      gment nil 信息片段
                      |                                                       |

      absoluteString | https://swiftinaction.com/example?section=5&ie=utf-8 | 完整的字符串

      resourceSpecifier | //swiftinaction.com/example?section=5&ie=utf-8 | 不包含协议头的剩余内容

      baseURL | nil | 参照路径,默认为 nil

      1. URL 特殊字符处理:
      • 在一个 URL 中,所有的字符都必须是 ASCII 码,不能包含特殊符号,比如 空格、中文等,如果有特殊符号需要使用百分号转义。

      • 1> 百分号转义编码规则:

        • URL 中一些字符的特殊含义,基本编码规则如下。
        作用 十六进制值
        分隔目录和子目录 %2F
        分隔 URL 和查询参数 %3F
        分隔参数 %26
        指定书签 %23
        指定特殊字符 %25
            |                     |

        " | | %22

        \ | | %5C

        ` | 转义 ` | %60

        | | 转义 | | %7C

        { | 转义 { | %7B

        } | 转义 } | %7D

        [ | 转义 [ | %5B

        ] | 转义 ] | %5D

        < | 转义 < | %3C

         |   转义 >			   |     %3E

        ^ | 转义 中文字符 |

        _ | 转义 空格 | %20

      • 2> 百分号转义 编码:

        • stringByAddingPercentEscapesUsingEncoding 只对 ` # % ^ { } [ ] | \ " < > 加 空格 共 14 个字符编码,不包括 & ? 等符号,iOS9 中此方法被淘汰,建议用 stringByAddingPercentEncodingWithAllowedCharacters 方法,此方法中 Characters 的创建方式如下:

        • 编码方式:

          	// 转义的字符包含 ”#%/<>?@\^`{|}
          NSCharacterSet *allowedCharacters = [NSCharacterSet URLHostAllowedCharacterSet]; // 转义的字符包含 "#%;<>?[\]^`{|}
          NSCharacterSet *allowedCharacters = [NSCharacterSet URLPathAllowedCharacterSet]; // 转义的字符包含 "#%<>[\]^`{|}
          NSCharacterSet *allowedCharacters = [NSCharacterSet URLQueryAllowedCharacterSet]; // 转义的字符包含 "#%<>[\]^`{|}
          NSCharacterSet *allowedCharacters = [NSCharacterSet URLFragmentAllowedCharacterSet]; // 转义的字符包含 "#%/:<>?@[\]^`{|}
          NSCharacterSet *allowedCharacters = [NSCharacterSet URLPasswordAllowedCharacterSet]; // 转义的字符包含 "#%/:<>?@[\]^`
          NSCharacterSet *allowedCharacters = [NSCharacterSet URLUserAllowedCharacterSet]; // 自定义方式
          NSCharacterSet *customAllowedSet = [NSCharacterSet characterSetWithCharactersInString:@"`#%^{}\"[]|\\<> "].invertedSet;
        • 使用:

          • 对整个 URL 进行转义:

            	NSString *urlStr = @"http://192.168.88.200/测试/demo.json";
            
            	NSString *newUrlStr = [urlStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"^"].invertedSet];
            
            	NSURL *url = [NSURL URLWithString:newUrlStr];
          • 对部分 URL 进行转义:

            	NSString *urlStr = [@"测试/demo.json" stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"^"].invertedSet];
            
            	NSString *newUrlStr = [@"http://192.168.88.200" stringByAppendingPathComponent:urlStr];
            
            	NSURL *url = [NSURL URLWithString:newUrlStr];
      • 3> 百分号转义 解码:

        • stringByRemovingPercentEncoding 在 Xcode7 中可能会提示要将 stringByAddingPercentEscapesUsingEncoding 替换成此方法,要根据是否是解码来区分。
  • 2、request:NSURLRequest

    • 要发送给服务器的请求。

      	+ (instancetype)requestWithURL:(NSURL *)URL;
      
      	+ (instancetype)requestWithURL:(NSURL *)URL
      cachePolicy:(NSURLRequestCachePolicy)cachePolicy
      timeoutInterval:(NSTimeInterval)timeoutInterval;
      1. 参数:
      • 1> URL:要访问的资源路径

      • 2> cachePolicy:缓存策略

        	// 默认的缓存策略,会在本地缓存
        NSURLRequestUseProtocolCachePolicy = 0, // 忽略本地缓存数据,永远都是从服务器获取数据,不使用缓存
        NSURLRequestReloadIgnoringLocalCacheData = 1, // 应用场景:股票,彩票
        NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData // 首先使用缓存,如果没有本地缓存,才从原地址下载
        NSURLRequestReturnCacheDataElseLoad = 2, // 使用本地缓存,从不下载,如果本地没有缓存,则请求失败和 "离线" 数据访问有关,可以和 Reachability 框架结合使用,
        // 如果用户联网,直接使用默认策略。如果没有联网,可以使用返回缓存策略郑重提示:要把用户拉到网络上来。
        NSURLRequestReturnCacheDataDontLoad = 3, // 无视任何缓存策略,无论是本地的还是远程的,总是从原地址重新下载
        NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // Unimplemented // 如果本地缓存是有效的则不下载,其他任何情况都从原地址重新下载
        NSURLRequestReloadRevalidatingCacheData = 5, // Unimplemented 缓存的数据保存在沙盒路径下 Caches 文件夹中的 SQLite 数据库中。
      • 3> timeoutInterval:访问超时时长

        • 默认的超时时长是 60s

        • 建议时长:15~30s,不要设置的太短.

        • SDWebImage : 15s

        • AFNetWorking: 60s

      1. NSURLRequest (NSHTTPURLRequest):
      • 1> HTTPMethod:请求方法

        • HTTP 访问方法,如 GET(默认)、POST ...
      • 2> HTTPBody:请求体

        • 提交给服务器的二进制数据。
      • 3> HTTPShouldHandleCookies:是否需要处理 Cookies

        • 默认为 YES,需要处理 Cookie。
      • 4> allHTTPHeaderFields:所有 HTTP 请求头字典

      1. 请求行:
      • 包含了请求方法、请求资源路径、HTTP协议版本。
      1. 请求头:
      • 对客户端的环境描述、客户端请求的主机地址等信息。
      1. 请求体:
      • 客户端发给服务器的具体数据。
  • 3、response:NSURLResponse

    • 从服务器返回的响应。

      void (^)(NSURLResponse* __nullable response, NSData* __nullable data, NSError* __nullable connectionError)
      1. 参数:
      • 1> response:服务器返回的响应

        • 包含状态行和响应头。
        • 一般只有在开发下载的时候,才会使用 response。
      • 2> data

        • 服务器返回的数据实体,需要处理的二进制数据,最重要的。
      • 3> onnectionError

        • 访问服务器过程中,出现的连接错误。
        • 所有网络访问都有可能出现错误,在商业开发中,一定要做错误处理。
      1. NSURLResponse/NSHTTPURLResponse:
      • 1> statusCode:服务器返回的状态码

        • 1XX 消息,2XX 成功,3XX 扩展属性,4XX 客户端错误,5XX 服务器错误

        • 404 页面没找到

        • 301 没有变化,可以使用缓存

        • 200 成功

      • 2> URL:服务器返回的 URL

        • 有的时候,访问服务器的时候,服务器会根据客户端的环境做 “重定向” 连接,造成服务器返回的 URL 和自己添加的 URL 不相同。
        • 重定向是服务器来做的。
      • 3> MIMEType:数据类型

        • 返回二进制数据的类型。通过 MIMEType 可以知道如何处理服务器返回的二进制数据。
        • 等价于 Content-Type。
      • 4> expectedContentLength:预期的数据长度

        • 主要用在下载,可以用来提示用户文件大小以及进度。
      • 5> suggestedFilename:建议的文件名

        • 跟下载有关,可以默认将文件保存成建议的文件名,即使用文件在服务器端的名称。
      • 6> textEncodingName:文本的编码名称

        • 如果没有特殊指定,统一使用 UTF8。

        • 字符编码:

          • UTF8: UNICODE 是一个字符编码标准,UTF8 是 UNICODE 的一个子集。

            • 特点:使用 1~4 个字节描述一个字符。一个汉字是 3 个字节
            • 4G 个字符,能够涵盖全世界所有的字符!
          • GB2312: 只是在一些比较老的网站还在使用,国标,1980 颁布的标准,涵盖了 6700+ 汉字

            • 汉字:最全的字典收录了 85000+ 汉字,康熙字典 47000+
      1. 状态行:
      • 包含了 HTTP 协议版本、状态码、状态英文描述。
      1. 响应头:
      • 包含了对服务器的描述、对返回数据的描述。
      1. 响应体:
      • 服务器返回给客户端的具体二进制数据。
  • 4、请求结果处理:

      1. 服务器返回给客户端的数据样式:
      • 主要格式:字符串、JSON、XML、PList
      • 其他格式:文件下载可以返回任意格式、图像、zip
      1. 序列化/反序列化:
      • 序列化:向服务器发送请求前,需要把提交给服务器的 OC 对象(NSArray / NSDictionary)转换成二进制数据。
      • 反序列化:从服务器接收到数据后,需要将服务器返回的二进制数据转换成 OC 对象(NSArray / NSDictionary)。
      1. 错误处理标准代码:
      	// 有的时候,没有错误,但是也获取不到数据
      if (error != nil || data == nil) { // 不要告诉用户太准确的错误信息
      NSLog(@"您的网络不给力,请稍候再试 !");
      return;
      } NSLog(@"网络请求成功 %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);

1.7 iOS App Transport Security

  • iOS9 引入了新特性 App Transport Security (ATS),新特性要求 App 内访问的网络必须使用 HTTPS 协议。iOS9 把所有的 http 请求都改为 https,系统发送的网络请求将统一使用 TLS 1.2 SSL。采用 TLS 1.2 协议,目的是强制增强数据访问安全,而且系统 Foundation 框架下的相关网络请求,将不再默认使用 Http 等不安全的网络协议,而默认采用 TLS 1.2。服务器因此需要更新,以解析相关数据。如不更新,可通过在 Info.plist 中声明,倒退回不安全的网络请求。

  • 1、关闭强制 https 请求:

    • 设置如下,添加 App Transport Security Settings 项,在其下添加 Allows Arbitrary Loads 并设置值为 YES。

      iOS - NetRequest   网络数据请求

    • 设置代码如下:

      	<key>NSAppTransportSecurity</key>
      <dict>
      <key>NSAllowsArbitraryLoads</key>
      <true/>
      </dict>
  • 2、设置 http 请求例外:

    • 设置如下,NSIncludeSubdomains 顾名思义是包括子域的意思。

      iOS - NetRequest   网络数据请求

    • 设置代码如下:

      	<key>NSAppTransportSecurity</key>
      <dict>
      <key>NSExceptionDomains</key>
      <dict>
      <key>qq.com</key>
      <dict>
      <key>NSIncludesSubdomains</key>
      <true/>
      </dict>
      <key>sina.com.cn</key>
      <dict>
      <key>NSIncludesSubdomains</key>
      <true/>
      </dict>
      </dict>
      </dict>

1.8 iOS POST 常用方法

  • 1、URL 编码表单数据

    • Content-Type: application/x-www-form-urlencoded

    • 主要向服务器提交与用户隐私相关的信息。比如用户登录。

      1. 特点:

           这种方式访问服务器,只需要指定访问方法和数据体即可。
        Content-Type 和 Content-Length 会自动完成。Content-Type 是客户端告诉服务器发送数据的类型。Content-Length 发送的数据长度。
      1. 实现代码:

           request.HTTPMethod = @"POST";
        request.HTTPBody = [bodyString dataUsingEncoding:NSUTF8StringEncoding]; 在 firebug 中,可以通过 POST -> 源代码,将发送的 httpbody 的内容粘贴出来。
  • 2、二进制数据 编码表单数据

    • Content-Type: multipart/form-data

    • 向服务器上传文件,能够上传文件的大小受服务器限制,PHP 限制是 2M。

      1. 实现代码:
      	request.HTTPMethod = @"POST";
      request.HTTPBody = 使用规定的格式拼接 // 设置请求头格式,如:Content-Type: multipart/form-data; boundary=%@
      Content-Type: multipart/form-data; boundary(分隔线)=(可以随便写,ASCII,字母和数字) // 设置文件类型格式,8 进制流,如果不想告诉服务器具体的文件类型,可以使用这个
      Content-Type: application/octet-stream
      1. 文件上传参数设置:
      • name (file) :后台规定的文件字段名,是负责上传文件的脚本中的字段名,开发的时候,可以咨询后端程序员。对文件上传时是数组的形式,如:userfile[]

      • filename :将文件保存在服务器上的文件名称。

      • Content-Type:客户端告诉服务器上传文件的文件类型。

        • 文件类型:

          g 图片文件
          图片文件
          图片文件
                                  |

          audio/mpeg | mp3 文件

          video/mp4 | mp4 文件

          |

          text/html | html 文本文件

          text/plain | txt 文本文件

          text/rtf | rtf 文本文件

          |

          application/pdf | pdf 文件

          application/json | json 文件

          |

          application/octet-stream | 8 进制流,如果不想告诉服务器具体的文件类型,可以使用这个

      • name (text) :后台规定的文本内容字段名,是负责上传文件的脚本中的字段名

      1. 文件上传数据封装:
      • 每一行末尾需要有一定的 \r\n

      • 有些服务器可以直接使用 \n,但是新浪微博如果使用 \n 上传文件,服务器会返回 “没有权限” 的错误。

      • 有些服务器可以在上传文件的同时,提交一些文本内容给服务器。典型应用:新浪微博,上传图片的同时,发送一个微博。

      • 以下部分,是发送给服务器的二进制数据的组成格式,如果在 iOS 中,要实现 POST 上传文件,需要按照以下格式拼接数据。格式是 W3C 指定的标准格式,苹果没有做任何封装。

      • 1> 上传单个文件

        	#define boundary @"uploadBoundary"
        
        	// 设置请求头
        
        	Content-Type: multipart/form-data; boundary=boundary
        
        	// 上传的文件数据封装 --- 二进制数据格式
        
        	--boundary\r\n
        Content-Disposition: form-data; name="userfile"; filename="文件1.txt"\r\n // 设置文件名
        Content-Type: application/octet-stream\r\n // 设置文件类型
        \r\n
        待上传文件的二进制数据
        \r\n --boundary\r\n
        Content-Disposition: form-data; name="usertext"\r\n // 设置文本字段名
        \r\n
        待上传文本内容二进制数据
        \r\n --boundary--
        \r\n
      • 2> 上传多个文件

        	#define boundary @"uploadBoundary"
        
        	// 设置请求头
        
        	Content-Type: multipart/form-data; boundary=boundary
        
        	// 上传的文件数据封装 --- 二进制数据格式
        
        	--boundary\r\n
        Content-Disposition: form-data; name="userfile[]"; filename="文件1.txt"\r\n // 设置文件名,第一个上传的文件
        Content-Type: application/octet-stream\r\n // 设置文件类型
        \r\n
        待上传文件的二进制数据
        \r\n --boundary\r\n
        Content-Disposition: form-data; name="userfile[]"; filename="文件2.txt"\r\n // 设置文件名,第二个上传的文件
        Content-Type: application/octet-stream\r\n // 设置文件类型
        \r\n
        待上传文件的二进制数据
        \r\n --boundary\r\n
        Content-Disposition: form-data; name="usertext"\r\n // 设置文本字段名
        \r\n
        待上传文本内容二进制数据
        \r\n --boundary-- // 结束分割线标记
        \r\n
      1. 关于第三方框架:
      • AFN 能够同时实现上传 "一个文件",有些格式的文件,用 AFN 无法上传。
      • ASI 能够同时实现上传多个文件,是 MRC 的,2012 年就停止更新了,设计的目标平台,iOS 2.0/iOS 3.0 用的。
      1. 为什么要同时上传多个文件:
      • 以前很多服务器对上传文件的大小有限制,PHP 限制是 2M。
      • 目前很多服务器不仅不限制大小,而且鼓励上传多个文件。
      • 云服务器的普及。
      • 软件商希望获得更多的用户数据。
  • 3、RESTful 设计风格

    • RESTful 的设计风格,让后端的设计更加直观,解读起来非常容易。目前在国际上非常流行,国内也开始逐渐普及。

    • 主要用在后端开发的,前端程序员只要知道即可。

      1. 最直观的特点:
      • 没有脚本文件的扩展名,直接就是语义的表达。
      1. 主要的表现形式:
      1. POST 数据编码类型:
      • 1> Content-Type: application/json

        • 向服务器发送 JSON 数据。

          	[p dictionaryWithValuesForKeys:@[@"username", @"age", @"title", @"height"]];        模型转字典,传入的数组是需要序列化的属性
          [NSJSONSerialization isValidJSONObject:obj]; 判断给定的对象能够被序列化
          [NSJSONSerialization dataWithJSONObject:obj options:0 error:NULL]; 序列化
      • 2> Content-Type: text/xml

        • 向服务器发送 XML 数据。

        • iOS 开发极少用,如果开发中碰到可以使用 GData 框架。

1.9 iOS 文件下载设置

  • 1、HEAD 方法:

    • HEAD 方法通常是用来在下载文件之前,获取远程服务器上的文件信息。与 GET 方法相比,同样能够拿到响应头,但是不返回数据实体,用户可以根据响应头信息,确定下一步操作。

    • 用响应头的 response.expectedContentLength 可以获取文件数据的大小。

    • 同步方法是阻塞式的,通常只有 HEAD 方法(获得的数据很少,而且后续的下载会依赖)才会使用同步方法。在截取网络数据的时候,最好使用同步方法,而且如果频率太高,需要增加延时,否则会被服务器给加入黑名单。

  • 2、文件下载方法:

      1. 使用 GET 数据请求方法,数据请求完成后,将数据手动写入文件中。
      1. 使用专门用于下载的 GET 方法,数据请求完成后,将会自动写入指定文件中。
  • 3、文件本地保存方法:

      1. 拼接数据,统一写入磁盘
      • 问题:内存峰值可能会很高。
      1. 每次接收到二进制数据,都直接写入磁盘
      • NSFileHandle 文件句柄写入
      • NSOutputStream 文件流写入
  • 4、文件断点下载步骤:

      1. 检查服务器上的文件信息:
      • 文件大小
      • 文件名
      1. 检查本地文件信息:
      • 1> 本地没有文件,从头开始下载
      • 2> 本地有文件,比服务器小,从本地文件大小开始下载
      • 3> 本地有文件,和服务器一样大,表示下载完成
      • 4> 本地有文件,比服务器大,删除本地文件,重新下载
      1. 根据本地文件的长度,从对应 "偏移" 位置开始下载:
      • 请求头设置 Range:
        • bytes=x-y 从 x 字节开始下载,下载 x-y 个字节

        • bytes=x- 从 x 字节开始下载,下载到文件末尾

        • bytes=-x 从 文件开始下载,下载 x 字节

        • 如:[urlRequest setValue:[NSString stringWithFormat:@"bytes=%lld-", offset] forHTTPHeaderField:@"Range"];

  • 5、文件下载设置:

      1. NSURLConnection:
      • 代理方法默认都是在主线程上执行的,会对界面产生卡顿。

      • For the connection to work correctly, the calling thread’s run loop must be operating in the default run loop mode.

      • 为了让连接工作正常,调用线程的运行循环必须在默认的运行循环模式下。

      • 如果要让 NSURLConnection 实现在后台线程回调代理方法,需要在后台线程启动 NSURLConnection,并启动后台线程的运行循环,NSURLConnection 执行完毕后,会自动停止后台线程的运行循环。

      1. NSURLSession:
      • 文件下载成功后,如果不做任何处理,下载的文件会被自动删除。

      • 如果显示比较大的图片,NSURLSession 可以利用磁盘缓存直接下载到本地,不会造成内存占用太大。

      • 一般从网络上下载文件,zip 压缩包会比较多。如果是 zip 文件,下载完成后需要:

        • 下载压缩包
        • 解压缩(异步执行)到目标文件夹
        • 删除压缩包
      • 下载任务的特点可以让程序员只关心解压缩的工作。

      1. 启动子线程的运行循环方法:
      	CFRunLoopRun();
      
      	// NSRunLoop 只能启动,没有提供停止的接口
      [[NSRunLoop currentRunLoop] run];
  • 6、块代码回调:

      1. Block 的属性定义:
      • 如果本方法能够直接执行到 block,就不需要定义 block 属性。
      • 如果本方法不能直接执行 block,就需要定义 block 属性。
      1. 网络 block 回调的几个 "约定":
      • AFN/SDWebImage 等第三方框架,都是这么做的。

      • 1> 进度的回调,在异步回调

        • 进度的频率非常高,如果在某些 UI 上高频率回调进度,会造成界面卡顿。
        • 如果需要,就在主线程更新 UI,如果卡的太厉害,就想别的解决方法,如使用旋转的小菊花等。
      • 2> 成功的回调,在主线程回调

        • 成功只有一个,在主线程回调,调用方不需要考虑线程间通讯。
      • 3> 失败的回调,可以在主线程,也可以在后台线程

2、同步 GET 网络请求

  • Objective-C

    • NSURLConnection 同步请求

      	// 设置网络接口
      NSURL *url1 = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video?type=JSON"]; // 创建同步网络请求
      NSData *syncNetData1 = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url1]
      returningResponse:nil
      error:NULL];
    • NSData 同步请求

      	// 设置网络接口
      NSURL *url2 = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video?type=JSON"]; // 创建同步网络请求
      NSData *syncNetData2 = [NSData dataWithContentsOfURL:url2];
    • NSString 同步请求

      	// 设置网络接口
      NSURL *url3 = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video?type=JSON"]; // 创建同步网络请求
      NSString *syncNetString = [NSString stringWithContentsOfURL:url3 encoding:NSUTF8StringEncoding error:nil];
    • WebView 同步请求

      	// 设置网络接口
      NSURL *url4 = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer"]; // 创建同步网络请求
      [self.webView loadRequest:[NSURLRequest requestWithURL:url4]];
  • Swift

    • NSURLConnection 同步请求

      	// 设置网络接口
      let url1 = NSURL(string: "http://192.168.88.200:8080/MJServer/video?type=JSON") // 创建同步网络请求
      let syncNetData1 = try! NSURLConnection.sendSynchronousRequest(NSURLRequest(URL: url1!), returningResponse: nil)
    • NSData 同步请求

      	// 设置网络接口
      let url2 = NSURL(string: "http://192.168.88.200:8080/MJServer/video?type=JSON") // 创建同步网络请求
      let syncNetData2 = NSData(contentsOfURL: url2!)
    • NSString 同步请求

      	// 设置网络接口
      let url3 = NSURL(string: "http://192.168.88.200:8080/MJServer/video?type=JSON") // 创建同步网络请求
      let syncNetString = try! NSString(contentsOfURL: url3!, encoding: NSUTF8StringEncoding)
    • WebView 同步请求

      	// 设置网络接口
      let url4 = NSURL(string: "http://192.168.88.200:8080/MJServer") // 创建同步网络请求
      self.webView.loadRequest(NSURLRequest(URL: url4!))

3、同步 POST 网络请求

  • Objective-C

    	// 设置网络接口
    NSURL *url = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video"]; // 创建请求
    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url]; // 设置请求体(请求参数)
    urlRequest.HTTPBody = [@"type=JSON" dataUsingEncoding:NSUTF8StringEncoding]; // 设置请求方式
    urlRequest.HTTPMethod = @"POST"; // 创建同步链接
    NSData *syncNetData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:nil error:nil];
  • Swift

    	// 设置网络接口
    let url = NSURL(string: "http://192.168.88.200:8080/MJServer/video") // 创建请求
    let urlRequest = NSMutableURLRequest(URL: url!) // 设置请求体(请求参数)
    urlRequest.HTTPBody = "type=JSON".dataUsingEncoding(NSUTF8StringEncoding) // 设置请求方式
    urlRequest.HTTPMethod = "POST" // 创建同步链接
    let syncNetData = try? NSURLConnection.sendSynchronousRequest(urlRequest, returningResponse: nil)

4、异步 GET 网络请求

  • Objective-C

    	// 设置网络接口
    NSURL *url = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video?type=XML"]; // 创建异步网络请求
    [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url]
    queue:[NSOperationQueue mainQueue]
    completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { }];
  • Swift

    	// 设置网络接口
    let url = NSURL(string: "http://192.168.88.200:8080/MJServer/video?type=XML") // 创建异步网络请求
    NSURLConnection.sendAsynchronousRequest(NSURLRequest(URL: url!),
    queue: NSOperationQueue.mainQueue())
    { (response:NSURLResponse?, data:NSData?, connectionError:NSError?) in }

5、异步 POST 网络请求

  • Objective-C

    	// 设置网络接口
    NSURL *url = [NSURL URLWithString:@"http://192.168.88.200:8080/MJServer/video"]; // 创建请求
    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url]; // 设置请求体(请求参数)
    urlRequest.HTTPBody = [@"type=XML" dataUsingEncoding:NSUTF8StringEncoding]; // 设置请求方式
    urlRequest.HTTPMethod = @"POST"; // 创建异步连接
    [NSURLConnection sendAsynchronousRequest:urlRequest
    queue:[NSOperationQueue mainQueue]
    completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { }];
  • Swift

    	// 设置网络接口
    let url = NSURL(string: "http://192.168.88.200:8080/MJServer/video") // 创建请求
    let urlRequest = NSMutableURLRequest(URL: url!) // 设置请求体(请求参数)
    urlRequest.HTTPBody = "type=XML".dataUsingEncoding(NSUTF8StringEncoding) // 设置请求方式
    urlRequest.HTTPMethod = "POST" // 创建异步网络请求
    NSURLConnection.sendAsynchronousRequest(urlRequest,
    queue: NSOperationQueue.mainQueue())
    { (response:NSURLResponse?, data:NSData?, connectionError:NSError?) in }

6、网络请求基本设置

  • Objective-C

    	// 设置网络接口
    NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/demo.json"]; // 创建网络请求
    NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:15.0]; // 发送网络连接
    [[[NSURLSession sharedSession] dataTaskWithRequest:request
    completionHandler:^(NSData * _Nullable data,
    NSURLResponse * _Nullable response,
    NSError * _Nullable error) { /*
    网络请求完成,处理请求结果,error != nil || data == nil 请求失败。
    */ // 错误处理的标准代码 - 有的时候,没有错误,但是也获取不到数据
    if (error != nil || data == nil) { // 不要告诉用户太准确的错误信息
    NSLog(@"您的网络不给力,请稍候再试 !"); return;
    } // 请求数据解析
    id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL]; NSLog(@"网络请求成功 %@", result); }] resume];

7、文件上传设置

  • Objective-C

    • 单文件上传

      	// 设置网络接口
      NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/upload/upload.php"]; // 创建网络请求
      NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
      request.HTTPMethod = @"POST"; // 设置请求头 #define boundary @"uploadBoundary" [request setValue:[NSString stringWithFormat:@"multipart/form-data; charset=utf-8; boundary=%@", boundary]
      forHTTPHeaderField:@"Content-Type"]; // 设置上传的文件数据 NSMutableData *formDataM = [NSMutableData data]; // 添加文件 [formDataM appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
      [formDataM appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data;
      name=\"%@\";
      filename=\"%@\"\r\n", @"userfile", @"test.jpg"]
      dataUsingEncoding:NSUTF8StringEncoding]];
      [formDataM appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", @"image/jpeg"]
      dataUsingEncoding:NSUTF8StringEncoding]]; [formDataM appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // 本地待上传的文件
      [formDataM appendData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"HQ_0005" ofType:@"jpg"]]];
      [formDataM appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // 添加文本 [formDataM appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
      [formDataM appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data;
      name=\"%@\"\r\n\r\n%@\r\n", @"username", @"qian"]
      dataUsingEncoding:NSUTF8StringEncoding]]; // 添加结束分隔符 [formDataM appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [[[NSURLSession sharedSession] uploadTaskWithRequest:request
      fromData:formDataM
      completionHandler:^(NSData * _Nullable data,
      NSURLResponse * _Nullable response,
      NSError * _Nullable error) { if (error != nil || data == nil) return; id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
      [self refreshUI:result];
      }] resume];
    • 单文件上传简单封装

      • 文件数据封装使用到第三方框架 QExtension,具体实现代码见 GitHub 源码 QExtension
      	// NSData+FormData.m
      。。。。。 // 设置上传的文件数据 #define boundary @"uploadBoundary" NSMutableData *formDataM = [NSMutableData data]; [formDataM q_setHttpHeaderFieldWithRequest:request fileBoundary:boundary]; // 设置请求头 [formDataM q_appendPartWithFileURL:[NSURL URLWithString:[[NSBundle mainBundle] pathForResource:@"HQ_0005" ofType:@"jpg"]]
      fileBoundary:boundary name:@"userfile"
      fileName:nil
      mimeType:nil]; // 添加文件 [formDataM q_appendPartWithText:@"qian" textName:@"username" fileBoundary:boundary]; // 添加文本 [formDataM q_appendPartEndingWithFileBoundary:boundary]; // 添加结束分隔符
    • 单文件上传封装

      • 文件数据封装使用到第三方框架 QExtension,具体实现代码见 GitHub 源码 QExtension
      	// NSData+FormData.m
      。。。。。 // 设置上传的文件数据 // 指定文件数据方式 NSData *filedata = [[NSData alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"HQ_0003" ofType:@"jpg"]]; NSData *formData = [NSData q_formDataWithRequest:request
      fileData:filedata
      name:@"userfile"
      fileName:@"HQ_0003.jpg"
      mimeType:@"image/jpeg"]; NSData *formData = [NSData q_formDataWithRequest:request
      text:@"qian"
      textName:@"username"
      fileData:filedata
      name:@"userfile"
      fileName:@"HQ_0003.jpg"
      mimeType:@"image/jpeg"]; // 指定文件路径方式 NSURL *fileURL = [NSURL URLWithString:[[NSBundle mainBundle] pathForResource:@"HQ_0005" ofType:@"jpg"]]; NSData *formData = [NSData q_formDataWithRequest:request
      fileURL:fileURL
      name:@"userfile"
      fileName:@"test.jpg"
      mimeType:@"image/jpeg"]; NSData *formData = [NSData q_formDataWithRequest:request
      text:@"qian"
      textName:@"username"
      fileURL:fileURL
      name:@"userfile"
      fileName:@"test.jpg"
      mimeType:@"image/jpeg"];
    • 多文件上传

      	// 设置网络接口
      NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/upload/upload-m.php"]; // 创建网络请求
      NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
      request.HTTPMethod = @"POST"; // 设置请求头 #define boundary @"uploadBoundary" [request setValue:[NSString stringWithFormat:@"multipart/form-data; charset=utf-8; boundary=%@", boundary]
      forHTTPHeaderField:@"Content-Type"]; // 设置上传的文件数据 NSMutableData *formDataM = [NSMutableData data]; // 添加第一个文件 [formDataM appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
      [formDataM appendData:[[NSString stringWithFormat:@"Content-Disposition:form-data;
      name=\"%@\";
      filename=\"%@\"\r\n", @"userfile[]", @"test1.jpg"]
      dataUsingEncoding:NSUTF8StringEncoding]];
      [formDataM appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", @"image/jpeg"]
      dataUsingEncoding:NSUTF8StringEncoding]];
      [formDataM appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
      [formDataM appendData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"HQ_0003" ofType:@"jpg"]]];
      [formDataM appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // 添加第二个文件 [formDataM appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
      [formDataM appendData:[[NSString stringWithFormat:@"Content-Disposition:form-data;
      name=\"%@\";
      filename=\"%@\"\r\n", @"userfile[]", @"test2.jpg"]
      dataUsingEncoding:NSUTF8StringEncoding]];
      [formDataM appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", @"image/jpeg"]
      dataUsingEncoding:NSUTF8StringEncoding]];
      [formDataM appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
      [formDataM appendData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"HQ_0005" ofType:@"jpg"]]];
      [formDataM appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // 添加文本 [formDataM appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
      [formDataM appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data;
      name=\"%@\"\r\n\r\n%@\r\n", @"username", @"qian"]
      dataUsingEncoding:NSUTF8StringEncoding]]; // 添加结束分隔符 [formDataM appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [[[NSURLSession sharedSession] uploadTaskWithRequest:request
      fromData:formDataM
      completionHandler:^(NSData * _Nullable data,
      NSURLResponse * _Nullable response,
      NSError * _Nullable error) { if (error != nil || data == nil) return; id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
      [self refreshUI:result];
      }] resume];
    • 多文件上传封装

      • 文件数据封装使用到第三方框架 QExtension,具体实现代码见 GitHub 源码 QExtension
      	// NSData+FormData.m
      。。。。。 // 设置上传的文件数据 // 指定文件数据方式 NSData *filedata1 = [[NSData alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"HQ_0003" ofType:@"jpg"]];
      NSData *filedata2 = [[NSData alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"HQ_0005" ofType:@"jpg"]]; NSData *formData = [NSData q_formDataWithRequest:request
      fileDatas:@[filedata1, filedata2]
      name:@"userfile[]"
      fileNames:@[@"test1.jpg", @"test2.jpg"]
      mimeTypes:@[@"image/jpeg", [NSNull null]]]; NSData *formData = [NSData q_formDataWithRequest:request texts:@[@"qian"]
      textNames:@[@"username"]
      fileDatas:@[filedata1, filedata2]
      name:@"userfile[]"
      fileNames:@[@"test1.jpg", @"test2.jpg"]
      mimeTypes:@[@"image/jpeg", [NSNull null]]]; // 指定文件路径方式 NSURL *fileURL1 = [NSURL URLWithString:[[NSBundle mainBundle] pathForResource:@"HQ_0003" ofType:@"jpg"]];
      NSURL *fileURL2 = [NSURL URLWithString:[[NSBundle mainBundle] pathForResource:@"HQ_0005" ofType:@"jpg"]]; NSData *formData = [NSData q_formDataWithRequest:request
      fileURLs:@[fileURL1, fileURL2]
      name:@"userfile[]"
      fileNames:@[@"test1.jpg", [NSNull null]]
      mimeTypes:@[@"image/jpeg", [NSNull null]]]; NSData *formData = [NSData q_formDataWithRequest:request texts:@[@"qian"]
      textNames:@[@"username"]
      fileURLs:@[fileURL1, fileURL2]
      name:@"userfile[]"
      fileNames:@[@"test1.jpg", [NSNull null]]
      mimeTypes:@[@"image/jpeg", [NSNull null]]];

8、文件下载设置

  • Objective-C

    • 获取文件信息

      	// 设置网络接口
      NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/download/file/minion_01.mp4"]; // 创建网络请求
      NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
      request.HTTPMethod = @"HEAD"; [[[NSURLSession sharedSession] dataTaskWithRequest:request
      completionHandler:^(NSData * _Nullable data,
      NSURLResponse * _Nullable response,
      NSError * _Nullable error) { if (error != nil || data == nil) return; NSLog(@"要下载文件的长度 %tu", response.expectedContentLength); }] resume];
    • 基本下载

      	// 设置请求路径
      NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/download/file/minion_01.mp4"]; // 创建请求对象
      NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url]; // 创建会话对象
      NSURLSession *urlSession = [NSURLSession sharedSession]; NSURLSessionDownloadTask *urlSessionDownloadTask = [urlSession downloadTaskWithRequest:urlRequest
      completionHandler:^(NSURL * _Nullable location,
      NSURLResponse * _Nullable response,
      NSError * _Nullable error) { if (error == nil) { // 设置下载的文件存储路径
      NSString *documentsDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]
      stringByAppendingPathComponent:response.suggestedFilename]; // 处理下载的数据
      [[NSFileManager defaultManager] copyItemAtPath:location.path toPath:documentsDirPath error:nil];
      }
      }]; // 执行任务
      [urlSessionDownloadTask resume];

9、文件存储服务器 WebDav 操作

  • Objective-C

    • GET 请求

      	// 设置文件位置
      NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/uploads/123.png"]; [[[NSURLSession sharedSession] downloadTaskWithURL:url
      completionHandler:^(NSURL * _Nullable location,
      NSURLResponse * _Nullable response,
      NSError * _Nullable error) { // 将下载的文件读取到二进制数据
      NSData *data = [NSData dataWithContentsOfURL:location]; dispatch_async(dispatch_get_main_queue(), ^{ // 如果显示比较大的图片,NSURLSession 可以利用
      self.iconView.image = [UIImage imageWithData:data];
      }); // 磁盘缓存直接下载到本地,不会造成内存占用太大 }] resume];
    • PUT 请求

      	// 本地要上传的文件
      NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"minion.mp4" withExtension:nil]; // 123.mp4 保存到服务器的文件名
      NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/uploads/123.mp4"]; // PUT 文件上传,以文件的方式直接写入到 WebDav 服务器中
      NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
      urlRequest.HTTPMethod = @"PUT"; // 服务器验证,用户访问名和密码
      [urlRequest setValue:[@"admin:adminpasswd" q_basic64AuthEncode] forHTTPHeaderField:@"Authorization"]; [[[NSURLSession sharedSession] uploadTaskWithRequest:urlRequest
      fromFile:fileURL
      completionHandler:^(NSData * _Nullable data,
      NSURLResponse * _Nullable response,
      NSError * _Nullable error) { NSLog(@"%@ %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding], response); }] resume];
    • DELETE 请求

      	// 要删除的文件完成路径
      NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/uploads/123.mp4"]; // DELETE 文件删除
      NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
      urlRequest.HTTPMethod = @"DELETE"; // 服务器验证,用户访问名和密码
      [urlRequest setValue:[@"admin:adminpasswd" q_basic64AuthEncode] forHTTPHeaderField:@"Authorization"]; [[[NSURLSession sharedSession] dataTaskWithRequest:urlRequest
      completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"%@ %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding], response); }] resume];