1、网络请求
1.1 网络通讯三要素
- 1、IP 地址(主机名):
- 网络中设备的唯一标示。不易记忆,可以用主机名(域名)。
- 1) IP V4:
- 0~255.0~255.0~255.0~255 ,共有 2^8^4 = 2^32 = 42 亿。
- 2) 本地回环地址:
- 每台机器都有自己的本地回环地址,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:传输控制协议:
- 建立连接,形成传输数据的通道(建立连接的三次握手,断开连接的四次握手)。
- 在连接中进行大数据传输,数据大小不收限制。
- 通过三次握手完成连接,是可靠协议,数据安全送达。
- 必须建立连接,效率会稍低。
- 2) UDP:用户数据报协议:
- 只管发送,不确认对方是否接收到。
- 不需要建立连接,将数据及源和目的封装成数据包中,每个数据报的大小限制在 64K 之内。
- 因为无需连接,因此是不可靠协议。
- 不需要建立连接,速度快。
- 应用场景:多媒体教室/网络流媒体。
- 3) 常见网络协议:
应用层协议 端口 说明 HTTP 80 超文本传输协议 HTTPS 443 HTTP+SSL,HTTP 的安全版 FTP 20, 21, 990 文件传输 POP3 110 邮局协议 SMTP 25 简单邮件传输协议 telnet 23 远程终端协议
- 1) TCP:传输控制协议:
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 请求的一部分所以提交的数据量不能太大。
- GET 方法是默认的 HTTP 请求方法,通过请求 URL 得到资源。我们日常用 GET 方法来提交表单数据,然而用 GET 方法提交的表单数据只经过了简单的编码,同时它将作为 URL 的一部分向 Web 服务器发送,因此,如果使用 GET 方法来提交表单数据就存在着安全隐患上。例如:
- 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 服务器。
- 请求头包含许多有关客户端环境和请求体的有用信息。例如,请求头可以声明浏览器所用的语言,请求体的长度等。当我们打开一个网页时,浏览器要向网站服务器发送一个 HTTP 请求头,然后网站服务器根据 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。
- HTTP 应答码也称为状态码,它反映了 Web 服务器处理 HTTP 请求状态。HTTP 应答码由 3 位数字构成,其中首位数字定义了应答码的类型:
- (3) 状态码描述:
- 用来解析状态码的状态。
- 如:
- 200 OK :请求已成功,请求所希望的响应头或数据体将随此响应返回。
- 404 Not Found :请求失败,请求所希望得到的资源未被在服务器上发现。
- HTTP 响应的第一行类似于 HTTP 请求的第一行,其组成为:协议/版本 状态码 状态码描述
- 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
所有内容都不会被缓存
- 这个是非常重要的规则。这个用来指定 Response-Request 遵循的缓存机制。各个指令含义如下
- (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>
- 响应正文就是服务器返回的 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 身份验证失败
- GET:
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 多用于向后台提交数据。
- GET 请求:
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
property url description URL https://swiftinaction.com/example?section=5&ie=utf-8 URL 路径 scheme https 协议,http、https、ftp、file(本地文件)、data(数据) host swiftinaction.com 主机,可以利用 Reachability 框架判断能否连接到要访问的主机 port nil 端口,nil 使用默认端口 80 path example 路径,不包含协议头/主机地址/端口/参数 parameters nil 参数,变量名和变量值成对出现,使用 = 分隔,多个参数使用 & 连接 query section=5&ie=utf-8 查询参数 fragment nil 信息片段 absoluteString https://swiftinaction.com/example?section=5&ie=utf-8 完整的字符串 resourceSpecifier //swiftinaction.com/example?section=5&ie=utf-8 不包含协议头的剩余内容 baseURL nil 参照路径,默认为 nil - 2) 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];
- 对整个 URL 进行转义:
- 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
- 2) NSURLRequest (NSHTTPURLRequest):
- 1> HTTPMethod:请求方法
- HTTP 访问方法,如 GET(默认)、POST ...
- 2> HTTPBody:请求体
- 提交给服务器的二进制数据。
- 3> HTTPShouldHandleCookies:是否需要处理 Cookies
- 默认为 YES,需要处理 Cookie。
- 4> allHTTPHeaderFields:所有 HTTP 请求头字典
- 1> HTTPMethod:请求方法
- 3) 请求行:
- 包含了请求方法、请求资源路径、HTTP协议版本。
- 4) 请求头:
- 对客户端的环境描述、客户端请求的主机地址等信息。
- 5) 请求体:
- 客户端发给服务器的具体数据。
- 要发送给服务器的请求。
- 3、response:NSURLResponse
- 从服务器返回的响应。
void (^)(NSURLResponse* __nullable response, NSData* __nullable data, NSError* __nullable connectionError)
- 1) 参数:
- 1> response:服务器返回的响应
- 包含状态行和响应头。
- 一般只有在开发下载的时候,才会使用 response。
- 2> data
- 服务器返回的数据实体,需要处理的二进制数据,最重要的。
- 3> onnectionError
- 访问服务器过程中,出现的连接错误。
- 所有网络访问都有可能出现错误,在商业开发中,一定要做错误处理。
- 1> response:服务器返回的响应
- 2) 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+
- UTF8: UNICODE 是一个字符编码标准,UTF8 是 UNICODE 的一个子集。
- 1> statusCode:服务器返回的状态码
- 3) 状态行:
- 包含了 HTTP 协议版本、状态码、状态英文描述。
- 4) 响应头:
- 包含了对服务器的描述、对返回数据的描述。
- 5) 响应体:
- 服务器返回给客户端的具体二进制数据。
- 从服务器返回的响应。
- 4、请求结果处理:
- 1) 服务器返回给客户端的数据样式:
- 主要格式:字符串、JSON、XML、PList
- 其他格式:文件下载可以返回任意格式、图像、zip
- 2) 序列化/反序列化:
- 序列化:向服务器发送请求前,需要把提交给服务器的 OC 对象(NSArray / NSDictionary)转换成二进制数据。
- 反序列化:从服务器接收到数据后,需要将服务器返回的二进制数据转换成 OC 对象(NSArray / NSDictionary)。
- 3) 错误处理标准代码:
// 有的时候,没有错误,但是也获取不到数据 if (error != nil || data == nil) { // 不要告诉用户太准确的错误信息 NSLog(@"您的网络不给力,请稍候再试 !"); return; } NSLog(@"网络请求成功 %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
- 1) 服务器返回给客户端的数据样式:
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。
- 设置代码如下:
<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>
- 2、设置 http 请求例外:
- 设置如下,NSIncludeSubdomains 顾名思义是包括子域的意思。
- 设置代码如下:
<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 发送的数据长度。
- 2) 实现代码:
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
- 2) 文件上传参数设置:
- name (file) :后台规定的文件字段名,是负责上传文件的脚本中的字段名,开发的时候,可以咨询后端程序员。对文件上传时是数组的形式,如:userfile[]
- filename :将文件保存在服务器上的文件名称。
- Content-Type:客户端告诉服务器上传文件的文件类型。
- 文件类型:
image/jpeg 图片文件 image/png 图片文件 image/gif 图片文件 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) :后台规定的文本内容字段名,是负责上传文件的脚本中的字段名
- 3) 文件上传数据封装:
- 每一行末尾需要有一定的 \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
- 4) 关于第三方框架:
- AFN 能够同时实现上传 "一个文件",有些格式的文件,用 AFN 无法上传。
- ASI 能够同时实现上传多个文件,是 MRC 的,2012 年就停止更新了,设计的目标平台,iOS 2.0/iOS 3.0 用的。
- 5) 为什么要同时上传多个文件:
- 以前很多服务器对上传文件的大小有限制,PHP 限制是 2M。
- 目前很多服务器不仅不限制大小,而且鼓励上传多个文件。
- 云服务器的普及。
- 软件商希望获得更多的用户数据。
- 3、RESTful 设计风格
- RESTful 的设计风格,让后端的设计更加直观,解读起来非常容易。目前在国际上非常流行,国内也开始逐渐普及。
- 主要用在后端开发的,前端程序员只要知道即可。
- 1) 最直观的特点:
- 没有脚本文件的扩展名,直接就是语义的表达。
- 2) 主要的表现形式:
- 使用一个 URL,使用不同的 HTTP 访问方法,表达不同的语义。
- 示例:http://www.xxx.com/product/123
- GET http://www.xxx.com/product/123 语义:从服务器 "获取" 产品代号是123 的产品信息
- POST http://www.xxx.com/product/123 语义:在服务器 "新增" 产品代号是123 的产品记录
- PUT http://www.xxx.com/product/123 语义:在服务器 "修改" 产品代号是123 的产品记录
- DELETE http://www.xxx.com/product/123 语义:在服务器 "删除" 产品代号是123 的产品记录
- POST 提交二进制数据,需要提交一个 JSON 格式的二进制数据,后端程序员,可以直接反序列化,得到 JSON 中的字典信息。
- 分别对应:增,删,改,查
- 3) POST 数据编码类型:
- 1> Content-Type: application/json
- 向服务器发送 JSON 数据。
[p dictionaryWithValuesForKeys:@[@"username", @"age", @"title", @"height"]]; 模型转字典,传入的数组是需要序列化的属性 [NSJSONSerialization isValidJSONObject:obj]; 判断给定的对象能够被序列化 [NSJSONSerialization dataWithJSONObject:obj options:0 error:NULL]; 序列化
- 向服务器发送 JSON 数据。
- 2> Content-Type: text/xml
- 向服务器发送 XML 数据。
- iOS 开发极少用,如果开发中碰到可以使用 GData 框架。
- 1> Content-Type: application/json
1.9 iOS 文件下载设置
- 1、HEAD 方法:
- HEAD 方法通常是用来在下载文件之前,获取远程服务器上的文件信息。与 GET 方法相比,同样能够拿到响应头,但是不返回数据实体,用户可以根据响应头信息,确定下一步操作。
- 用响应头的 response.expectedContentLength 可以获取文件数据的大小。
- 同步方法是阻塞式的,通常只有 HEAD 方法(获得的数据很少,而且后续的下载会依赖)才会使用同步方法。在截取网络数据的时候,最好使用同步方法,而且如果频率太高,需要增加延时,否则会被服务器给加入黑名单。
- 2、文件下载方法:
- 1) 使用 GET 数据请求方法,数据请求完成后,将数据手动写入文件中。
- 2) 使用专门用于下载的 GET 方法,数据请求完成后,将会自动写入指定文件中。
- 3、文件本地保存方法:
- 1) 拼接数据,统一写入磁盘
- 问题:内存峰值可能会很高。
- 2) 每次接收到二进制数据,都直接写入磁盘
- NSFileHandle 文件句柄写入
- NSOutputStream 文件流写入
- 1) 拼接数据,统一写入磁盘
- 4、文件断点下载步骤:
- 1) 检查服务器上的文件信息:
- 文件大小
- 文件名
- 2) 检查本地文件信息:
- 1> 本地没有文件,从头开始下载
- 2> 本地有文件,比服务器小,从本地文件大小开始下载
- 3> 本地有文件,和服务器一样大,表示下载完成
- 4> 本地有文件,比服务器大,删除本地文件,重新下载
- 3) 根据本地文件的长度,从对应 "偏移" 位置开始下载:
- 请求头设置 Range:
- bytes=x-y 从 x 字节开始下载,下载 x-y 个字节
- bytes=x- 从 x 字节开始下载,下载到文件末尾
- bytes=-x 从 文件开始下载,下载 x 字节
- 如:[urlRequest setValue:[NSString stringWithFormat:@"bytes=%lld-", offset] forHTTPHeaderField:@"Range"];
- 请求头设置 Range:
- 1) 检查服务器上的文件信息:
- 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 执行完毕后,会自动停止后台线程的运行循环。
- 2) NSURLSession:
- 文件下载成功后,如果不做任何处理,下载的文件会被自动删除。
- 如果显示比较大的图片,NSURLSession 可以利用磁盘缓存直接下载到本地,不会造成内存占用太大。
- 一般从网络上下载文件,zip 压缩包会比较多。如果是 zip 文件,下载完成后需要:
- 下载压缩包
- 解压缩(异步执行)到目标文件夹
- 删除压缩包
- 下载任务的特点可以让程序员只关心解压缩的工作。
- 3) 启动子线程的运行循环方法:
CFRunLoopRun(); // NSRunLoop 只能启动,没有提供停止的接口 [[NSRunLoop currentRunLoop] run];
- 1) NSURLConnection:
- 6、块代码回调:
- 1) Block 的属性定义:
- 如果本方法能够直接执行到 block,就不需要定义 block 属性。
- 如果本方法不能直接执行 block,就需要定义 block 属性。
- 2) 网络 block 回调的几个 "约定":
- AFN/SDWebImage 等第三方框架,都是这么做的。
- 1> 进度的回调,在异步回调
- 进度的频率非常高,如果在某些 UI 上高频率回调进度,会造成界面卡顿。
- 如果需要,就在主线程更新 UI,如果卡的太厉害,就想别的解决方法,如使用旋转的小菊花等。
- 2> 成功的回调,在主线程回调
- 成功只有一个,在主线程回调,调用方不需要考虑线程间通讯。
- 3> 失败的回调,可以在主线程,也可以在后台线程
- 1) Block 的属性定义:
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]];
- NSURLConnection 同步请求
- 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!))
- NSURLConnection 同步请求
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];
- GET 请求
时间: 2024-09-14 18:55:45