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.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 身份验证失败
- 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 多用于向后台提交数据。
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];
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 请求头字典
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
- 访问服务器过程中,出现的连接错误。
- 所有网络访问都有可能出现错误,在商业开发中,一定要做错误处理。
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 的一个子集。
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.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]; 序列化
2> Content-Type: text/xml
向服务器发送 XML 数据。
iOS 开发极少用,如果开发中碰到可以使用 GData 框架。
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];
6、块代码回调:
1) Block 的属性定义:
- 如果本方法能够直接执行到 block,就不需要定义 block 属性。
- 如果本方法不能直接执行 block,就需要定义 block 属性。
2) 网络 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];