网络编程
一 基本概念
1,HTTP 超文本传输协议
访问远程的网络资源,规定了客户端和服务端的数据传输格式。格式http://
http1.0和1.1的区别
-
默认长连接(持续连接)
HTTP 1.0需要使用keep-alive参数来告知服务器端要建立一个长连接,而HTTP1.1默认支持长连接。HTTP是基于TCP/IP协议的,创建一个TCP连接是需要经过三次握手的,有一定的开销,如果每次通讯都要重新建立连接的话,对性能有影响。因此最好能维持一个长连接,可以用个长连接来发多个请求。 -
节约带宽
HTTP 1.1支持只发送header信息(不带任何body信息),如果服务器认为客户端有权限请求服务器,则返回100,否则返回401。客户端如果接受到100,才开始把请求body发送到服务器。
这样当服务器返回401的时候,客户端就可以不用发送请求body了,节约了带宽。 -
HOST域
设置虚拟站点是非常常见的,即web server上的多个虚拟站点可以共享同一个ip和端口。HTTP1.0没有host域,HTTP1.1才支持这个参数。
http1.1和2.0的区别
-
多路复用
HTTP2.0使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级。
当然HTTP1.1也可以多建立几个TCP连接,来支持处理更多并发的请求,但是创建TCP连接本身也是有开销的。
TCP连接有一个预热和保护的过程,先检查数据是否传送成功,一旦成功过,则慢慢加大传输速度。因此对应瞬时并发的连接,服务器的响应就会变慢。所以最好能使用一个建立好的连接,并且这个连接可以支持瞬时并发的请求。 -
数据压缩
HTTP1.1不支持header数据的压缩,HTTP2.0使用HPACK算法对header的数据进行压缩,这样数据体积小了,在网络上传输就会更快。 -
服务器推送
当我们对支持HTTP2.0的web server请求数据的时候,服务器会顺便把一些客户端需要的资源一起推送到客户端,免得客户端再次创建连接发送请求到服务器端获取。这种方式非常合适加载静态资源。
服务器端推送的这些资源其实存在客户端的某处地方,客户端直接从本地加载这些资源就可以了,不用走网络,速度自然是快很多的。
发送HTTP请求的方法
http/1.1协议中定义了8中发送http请求的方法,分别是get,post,options,head,put,delete,tarce,connect,patch.一般只用get和post。
GET和POST的区别
- GET 在请求URL后面以?的形式跟上发送给服务器的参数,多个参数用&隔开,但是由于浏览器和服务器对URL长度有限制,因此URL后附带的参数是有限制的,通常不能超过1kb。
- POST 发送给服务器的参数全部放在请求体中,且理论上POST传递的数据没有长度限制。
HTTP通信过程
http协议规定,1个完整的http请求中包含以下内容:
请求头
:包含了对客户端的环境描述,客户端请求信息等
GET: /1.png HTTP/1.1 //包含请求方法,请求资源路径,HTTP协议版本
Host:10.62.232.226:8081 //客户端要访问的服务器主机地址
User-Agent:Mozilla/5.0 //客户端的类型,客户端的软件环境
Accept:text/html, */* //客户端所能接收的数据类型
Accept-Language:zh-cn //客户端的语言环境
Accept-Encoding:gzip //客户端支持的数据压缩格式
请求体
:客户端发服务器的具体数据。只有在POST请求中才使用,GET没有
响应头
:包含了对服务器的描述,对返回数据的描述
HTTP/1.1 200 OK //包含=HTTP协议版本,状态码,状态英文名称
Server:Apache-Coyote/1.1 //服务器类型
Content-Type:image/jpeg //返回数据的类型
Content-Length:56811 //返回数据的长度
Date:Mon,23,Jun,2018 //响应的时间
响应体
:服务器返回给客户端的具体数据,比如文件数据
常见响应状态码
* 200 成功
* 3XX 表示重定向相关
* 400 客户端请求的语法错误,服务器无法解析
* 404 服务器无法根据客户端的请求找到资源
* 500 服务器内部错误,无法完成请求
2,TCP
TCP特点
- TCP是面向连接的
通信前需要建立连接,通信结束需要释放连接。 - TCP提供可靠交付服务
所谓『可靠』指的是:TCP发送的数据无重复、无丢失、无错误、与发送端顺序一致。 - TCP是面向字节流的
所谓『面向字节流』指的是:TCP以字节为单位。虽然传输的过程中数据被划分成一个个数据报,但这只是为了方便传输,接收端最终接受到的数据将与发送端的数据一模一样。 - TCP提供全双工通信
所谓『全双工通信』指的是:TCP的两端既可以作为发送端,也可以作为接收端。 - 一条TCP连接的两端只能有两个端点
TCP只能提供点到点的通信,而UDP可以任意方式的通信。
TCP连接与套接字
-
什么是『TCP连接』?
TCP连接是一种抽象的概念,表示一条可以通信的链路。
每条TCP连接有且仅有两个端点,表示通信的双方。且双发在任意时刻都可以作为发送者和接收者。 -
什么是『套接字』?
一条TCP连接的两端就是两个套接字。
套接字=IP地址:端口号。
因此,TCP连接=(套接字1,套接字2)=(IP1:端口号1,IP2:端口号2)
TCP头部
tcp头部至少由20个字节组成(最多60字节,因为选项字段最多40字节),如下所示
TCP头部
各字段含义
-
源端口和目的端口
传输层和网络层一大重要区别就是传输层指定了数据报发往的应用进程,因此需要端口号标识。 -
序号
当前TCP数据报数据部分的第一个字节的序号。
我们知道,TCP是面向字节的,它会对发送的每一个字节进行编号,而且不同数据报之间是连续编号的。
由于本字段4字节,可以给[0,232-1]个字节进行编号(大约4G),而且序号循环使用,当发送完232-1个字节后,序号又从0开始。
一般来说,当2^32-1个字节被发送的时候,前面的字节早就发送成功了,因此序号可以循环使用。 -
确认号
表示当前主机作为接收端时,期望接收的下一个字节的编号是多少。
也表示,当前主机已经正确接收的最后一个字节序号+1。 -
数据偏移(报头长度)
它表明了数据报头部的长度。 -
保留字段
-
标识符
TCP有7种标识符,用于表示TCP报文的性质。它们只能为0或1。
URG=1
当URG字段被置1,表示本数据报的数据部分包含紧急信息,此时紧急指针有效。
紧急数据一定位于当前数据包数据部分的最前面,紧急指针标明了紧急数据的尾部。
如control+c:这个命令要求操作系统立即停止当前进程。此时,这条命令就会存放在数据包数据部分的开头,并由紧急指针标识命令的位置,并URG字段被置1。
ACK=1
ACK被置1后确认号字段才有效。
此外,TCP规定,在连接建立后传送的所有报文段都必须把ACK置1。
PSH=1
当接收方收到PSH=1的报文后,会立即将数据交付给应用程序,而不会等到缓冲区满后再提交。
一些交互式应用需要这样的功能,降低命令的响应时间。
RST=1
当该值为1时,表示当前TCP连接出现严重问题,必须要释放重连。
SYN=1
SYN在建立连接时使用。
当SYN=1,ACK=0时,表示当前报文段是一个连接请求报文。
当SYN=1,ACK=1时,表示当前报文段是一个同意建立连接的应答报文。
FIN=1
FIN=1表示此报文段是一个释放连接的请求报文。
接收窗口大小
该字段用于实现TCP的流量控制。
它表示当前接收方的接收窗口的剩余容量,发送方收到该值后会将发送窗口调整成该值的大小。发送窗口的大小又决定了发送速率,所以接收方通过设置该值就可以控制发送放的发送速率。
发送方每收到一个数据报都要调整当前的发送窗口。
检验和
用于接收端检验整个数据包在传输过程中是否出错。
紧急指针
用于标识紧急数据的尾部。
选项字段
上述字段都是每个TCP头部必须要有的,而选项字段是可选的,且长度可变,最长40字节。
最常用的选项字段为MMS:最大报文长度。
TCP三次握手
TCP三次握手起初,服务器和客户端都为CLOSED状态。在通信开始前,双方都得创建各自的传输控制块(TCB)。
服务器创建完TCB后遍进入LISTEN状态,此时准备接收客户端发来的连接请求。
第一次握手
客户端向服务端发送连接请求报文段。该报文段的头部中SYN=1,ACK=0,seq=x。请求发送后,客户端便进入SYN-SENT状态。
PS1:SYN=1,ACK=0表示该报文段为连接请求报文。
PS2:x为本次TCP通信的字节流的初始序号。
TCP规定:SYN=1的报文段不能有数据部分,但要消耗掉一个序号。
第二次握手
服务端收到连接请求报文段后,如果同意连接,则会发送一个应答:SYN=1,ACK=1,seq=y,ack=x+1。
该应答发送完成后便进入SYN-RCVD状态。
PS1:SYN=1,ACK=1表示该报文段为连接同意的应答报文。
PS2:seq=y表示服务端作为发送者时,发送字节流的初始序号。
PS3:ack=x+1表示服务端希望下一个数据报发送序号从x+1开始的字节。
第三次握手
当客户端收到连接同意的应答后,还要向服务端发送一个确认报文段,表示:服务端发来的连接同意应答已经成功收到。
该报文段的头部为:ACK=1,seq=x+1,ack=y+1。
客户端发完这个报文段后便进入ESTABLISHED状态,服务端收到这个应答后也进入ESTABLISHED状态,此时连接的建立完成!
为什么一定要是三次握手?
防止失效的连接请求报文段被服务端接收,从而产生错误。
想象这样一种情况,客户端给服务端发送了连接请求报文段,但是因为网络原因,报文段被阻塞,客户端收不到服务端的回应便超时重传,之后连接建立,双方便开始通信,通信结束后释放连接。此时第一次被网络阻塞的请求又到了服务端,因为只有两次握手,服务端以为客户端又重新请求建立连接,便进入连接建立状态等待客户端发送数据,但此时客户端并没有新的数据要发送,是关闭状态的,服务端将会一直等待下去,这样浪费服务端连接资源。
TCP四次挥手
TCP四次挥手TCP连接是双向的,因此在四次挥手中,前两次挥手用于断开一个方向的连接,后两次挥手用于断开另一方向的连接。
第一次挥手
若A认为数据发送完成,则它需要向B发送连接释放请求。该请求只有报文头,头中携带的主要参数为:
FIN=1,seq=u。此时,A将进入FIN-WAIT-1状态。
PS1:FIN=1表示该报文段是一个连接释放请求。
PS2:seq=u,u-1是A向B发送的最后一个字节的序号。
第二次挥手
B收到连接释放请求后,会通知相应的应用程序,告诉它A向B这个方向的连接已经释放。此时B进入CLOSE-WAIT状态,并向A发送连接释放的应答,其报文头包含:
ACK=1,seq=v,ack=u+1。
PS1:ACK=1:除TCP连接请求报文段以外,TCP通信过程中所有数据报的ACK都为1,表示应答。
PS2:seq=v,v-1是B向A发送的最后一个字节的序号。
PS3:ack=u+1表示希望收到从第u+1个字节开始的报文段,并且已经成功接收了前u个字节。
A收到该应答,进入FIN-WAIT-2状态,等待B发送连接释放请求。
第二次挥手完成后,A到B方向的连接已经释放,B不会再接收数据,A也不会再发送数据。但B到A方向的连接仍然存在,B可以继续向A发送数据。
第三次挥手
当B向A发完所有数据后,向A发送连接释放请求,请求头:FIN=1,ACK=1,seq=w,ack=u+1。B便进入LAST-ACK状态。
第四次挥手
A收到释放请求后,向B发送确认应答,此时A进入TIME-WAIT状态。该状态会持续2MSL时间,若该时间段内没有B的重发请求的话,就进入CLOSED状态,撤销TCB。当B收到确认应答后,也便进入CLOSED状态,撤销TCB。
为什么A要先进入TIME-WAIT状态,等待2MSL时间后才进入CLOSED状态?
为了保证服务端能收到客户端的应答。
如果客户端第四次挥手后直接关闭,但是应答丢失了,那么服务端等待超时后会重发请求,但此时客户端已经关闭了,所以服务端无法正常关闭。
TCP可靠传输的实现
TCP采用了流量控制、拥塞控制、连续ARQ等技术来保证它的可靠性。(PS:网络层传输的数据单元为『数据报』,传输层的数据单元为『报文段』,但为了方便起见,可以统称为『分组』。)
停止等待协议(ARQ协议)
TCP保证其可靠性采用的是更为复杂的滑动窗口协议,但停止等待协议是它的简化版,为了方便理解,这里先介绍停止等待协议。
AQR协议
ARQ(Automatic Repeat reQuest)自动重传请求。
顾名思义,当请求失败时它会自动重传,直到请求被正确接收为止。这种机制保证了每个分组都能被正确接收。停止等待协议是一种ARQ协议。
停止等待协议的原理
-
无差错的情况
A向B每发送一个分组,都要停止发送,等待B的确认应答;A只有收到了B的确认应答后才能发送下一个分组。 -
分组丢失和出现差错的情况
发送者拥有超时计时器。每发送一个分组便会启动超时计时器,等待B的应答。若超时仍未收到应答,则A会重发刚才的分组。
分组出现差错:若B收到分组,但通过检查和字段发现分组在运输途中出现差错,它会直接丢弃该分组,并且不会有任何其他动作。A超时后便会重新发送该分组,直到B正确接收为止。
分组丢失:若分组在途中丢失,B并没有收到分组,因此也不会有任何响应。当A超时后也会重传分组,直到正确接收该分组的应答为止。
综上所述:当分组丢失 或 出现差错 的情况下,A都会超时重传分组。 -
应答丢失 和 应答迟到 的情况
TCP会给每个字节都打上序号,用于判断该分组是否已经接收。
应答丢失:若B正确收到分组,并已经返回应答,但应答在返回途中丢失了。此时A也收不到应答,从而超时重传。紧接着B又收到了该分组。接收者根据序号来判断当前收到的分组是否已经接收,若已接收则直接丢弃,并补上一个确认应答。
应答迟到:若由于网络拥塞,A迟迟收不到B发送的应答,因此会超时重传。B收到该分组后,发现已经接收,便丢弃该分组,并向A补上确认应答。A收到应答后便继续发送下一个分组。但经过了很长时间后,那个失效的应答最终抵达了A,此时A可根据序号判断该分组已经接收,此时只需简单丢弃即可。
停止等待协议的注意点
- 每发送完一个分组,该分组必须被保留,直到收到确认应答为止。
- 必须给每个分组进行编号。以便按序接收,并判断该分组是否已被接收。
- 必须设置超时计时器。每发送一个分组就要启动计时器,超时就要重发分组。
- 计时器的超时时间要大于应答的平均返回时间,否则会出现很多不必要的重传,降低传输效率。但超时时间也不能太长。
滑动窗口协议(连续ARQ协议)
连续ARQ协议
在ARQ协议发送者每次只能发送一个分组,在应答到来前必须等待。而连续ARQ协议的发送者拥有一个发送窗口,发送者可以在没有得到应答的情况下连续发送窗口中的分组。这样降低了等待时间,提高了传输效率。
累计确认
在连续ARQ协议中,接收者也有个接收窗口,接收者并不需要每收到一个分组就返回一个应答,可以连续收到分组之后统一返回一个应答。这样能节省流量。
TCP头部的ack字段就是用来累计确认,它表示已经确认的字节序号+1,也表示期望发送者发送的下一个分组的起始字节号。
发送窗口
发送窗口的大小由接收窗口的剩余大小决定。接收者会把当前接收窗口的剩余大小写入应答TCP报文段的头部,发送者收到应答后根据该值和当前网络拥塞情况设置发送窗口的大小。发送窗口的大小是不断变化的。
发送窗口由三个指针构成:
- p1
p1指向发送窗口的后沿,它后面的字节表示已经发送且已收到应答。 - p2
p2指向尚未发送的第一个字节。
p1-p2间的字节表示已经发送,但还没收到确认应答。这部分的字节仍需保留,因为可能还要超时重发。
p2-p3间的字节表示可以发送,但还没有发送的字节。 - p3
p3指向发送窗口的前沿,它前面的字节尚未发送,且不允许发送。
发送者每收到一个应答,后沿就可以向前移动指定的字节。此时若窗口大小仍然没变,前沿也可以向前移动指定字节。
当p2和前沿重合时,发送者必须等待确认应答。
接收窗口
接收者收到的字节会存入接收窗口,接收者会对已经正确接收的有序字节进行累计确认,发送完确认应答后,接收窗口就可以向前移动指定字节。
如果某些字节并未按序收到,接收者只会确认最后一个有序的字节,从而乱序的字节就会被重新发送。
连续ARQ的注意点
-
同一时刻发送窗口的大小并不一定和接收窗口一样大。
虽然发送窗口的大小是根据接收窗口的大小来设定的,但应答在网络中传输是有时间的,有可能t1时间接收窗口大小为m,但当确认应答抵达发送者时,接收窗口的大小已经发生了变化。
此外发送窗口的大小还随网络拥塞情况影响。当网络出现拥塞时,发送窗口将被调小。 -
TCP标准并未规定未按序到达的字节的处理方式。但TCP一般都会缓存这些字节,等缺少的字节到达后再交给应用层处理。这比直接丢弃乱序的字节要节约带宽。
-
TCP标准规定接收方必须要有累计确认功能。接收方可以对多个TCP报文段同时确认,但不能拖太长时间,一般是0.5S以内。
此外,TCP允许接收者在有数据要发送的时候捎带上确认应答。但这种情况一般较少,因为一般很少有两个方向都要发送数据的情况。
流量控制和拥塞控制
- 拥塞控制:拥塞控制是作用于网络的,它是防止过多的数据注入到网络中,避免出现网络负载过大的情况; 通过慢开始和拥塞避免算法来控制
- 流量控制:流量控制是作用于接收者的,它是控制发送者的发送速度从而使接收者来得及接收。 由滑动窗口协议实现。
3,UDP
UDP的特点
UDP只在IP数据报服务的基础上增加了少量的功能:复用与分用、对整个报文的差错检测。
- UDP是无连接的
通信前不需要建立连接,通信结束也无需释放连接。 - UDP是不可靠的
它是尽力而为交付,不能确保每一个数据报都送达。 - UDP是面向报文的
所谓『面向报文』就是指:UDP数据传输的单位是报文,且不会对数据作任何 拆分 和 拼接 操作。
在发送端,应用程序给传输层的UDP什么样的数据,UDP不会对数据进行切分,只增加一个UDP头并交给网络层。
在接收端,UDP收到网络层的数据报后,去除IP数据报头部后遍交给应用层,不会作任何拼接操作。 - UDP没有拥塞控制
UDP始终以恒定的速率发送数据,并不会根据网络拥塞情况对发送速率作调整。这种方式有利有弊。
弊端:网络拥塞时有些报文可能会丢失,因此UDP不可靠。
优点:有些使用场景允许报文丢失,如:直播、语音通话,但对实时性要求很高,此时UDP还是很有用武之地的。 - UDP支持一对一、一对多、多对多、多对一通信
而TCP只支持一对一通信。 - UDP首部开销小,只有8字节。
而TCP头部至少由20字节,相比于TCP要高效很多。
UDP报文头
UDP报文头- 源端口
- 目的端口
- 长度:整个数据报的长度
- 检验和:整个数据报的检验和。
二 iOS中网络请求
1,NSURLConnection
屏幕快照 2018-03-19 下午3.42.49.png同步请求代码示例—GET
//1.确定请求路径
NSURL *url = [NSURL URLWithString:@"http://10.25.226.186:8081/login?username=a&pwd=123&type=XML"];
//2.创建一个请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.把请求发送给服务器
//sendSynchronousRequest 阻塞式的方法,会卡住线程
NSHTTPURLResponse *response = nil;
NSError *error = nil;
/*
第一个参数:请求对象
第二个参数:响应头信息,当该方法执行完毕之后,该参数被赋值
第三个参数:错误信息,如果请求失败,则error有值
*/
//该方法是阻塞式的,会卡住线程
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
//4.解析服务器返回的数据
NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
异步请求代码示例—GET
//1.确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it"];
//2.创建一个请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.把请求发送给服务器,发送一个异步请求
/*
第一个参数:请求对象
第二个参数:回调方法在哪个线程中执行,如果是主队列则block在主线程中执行,非主队列则在子线程中执行
第三个参数:completionHandlerBlock块:接受到响应的时候执行该block中的代码
response:响应头信息
data:响应体
connectionError:错误信息,如果请求失败,那么该参数有值
*/
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc]init] completionHandler:^(NSURLResponse * __nullable response, NSData * __nullable data, NSError * __nullable connectionError) {
//4.解析服务器返回的数据
NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
//转换并打印响应头信息
NSHTTPURLResponse *r = (NSHTTPURLResponse *)response;
NSLog(@"--%zd---%@--",r.statusCode,r.allHeaderFields);
}];
通过代理实现异步请求—GET
1)步骤
1. 确定请求路径
2. 创建请求对象
3. 创建NSURLConnection对象并设置代理
4. 遵守NSURLConnectionDataDelegate协议,并实现相应的代理方法
5. 在代理方法中监听网络请求的响应
2)设置代理的几种方法
/*
设置代理的第一种方式:自动发送网络请求
[[NSURLConnection alloc]initWithRequest:request delegate:self];
*/
/*
设置代理的第二种方式:
第一个参数:请求对象
第二个参数:谁成为NSURLConnetion对象的代理
第三个参数:是否马上发送网络请求,如果该值为YES则立刻发送,如果为NO则不会发送网路请求
NSURLConnection *conn = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
//调用该方法控制网络请求的发送
[conn start];
*/
//设置代理的第三种方式:使用类方法设置代理,会自动发送网络请求
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
//取消网络请求
//[conn cancel];
3)相关的代理方法
/*
1.当接收到服务器响应的时候调用
第一个参数connection:监听的是哪个NSURLConnection对象
第二个参数response:接收到的服务器返回的响应头信息
*/
- (void)connection:(nonnull NSURLConnection *)connection didReceiveResponse:(nonnull NSURLResponse *)response
/*
2.当接收到数据的时候调用,该方法会被调用多次
第一个参数connection:监听的是哪个NSURLConnection对象
第二个参数data:本次接收到的服务端返回的二进制数据(可能是片段)
*/
- (void)connection:(nonnull NSURLConnection *)connection didReceiveData:(nonnull NSData *)data
/*
3.当服务端返回的数据接收完毕之后会调用
通常在该方法中解析服务器返回的数据
*/
-(void)connectionDidFinishLoading:(nonnull NSURLConnection *)connection
/*4.当请求错误的时候调用(比如请求超时)
第一个参数connection:NSURLConnection对象
第二个参数:网络请求的错误信息,如果请求失败,则error有值
*/
- (void)connection:(nonnull NSURLConnection *)connection didFailWithError:(nonnull NSError *)error
异步请求—POST
1)发送POST请求步骤
1. 确定URL路径
2. 创建请求对象(可变对象)
3. 修改请求对象的方法为POST,设置请求体(Data)
4. 发送一个异步请求
5. 补充:设置请求超时,处理错误信息,设置请求头(如获取客户端的版本等等,请求头是可设置可不设置的)
//1.确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
//2.创建请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//2.1更改请求方法
request.HTTPMethod = @"POST";
//2.2设置请求体
request.HTTPBody = [@"username=520it&pwd=520it" dataUsingEncoding:NSUTF8StringEncoding];
//2.3请求超时
request.timeoutInterval = 5;
//2.4设置请求头
[request setValue:@"ios 9.0" forHTTPHeaderField:@"User-Agent"];
//3.发送请求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * __nullable response, NSData * __nullable data, NSError * __nullable connectionError) {
//4.解析服务器返回的数据
if (connectionError) {
NSLog(@"--请求失败-");
}else{
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}
}];
2,NSURLSession
- 使用步骤
使用NSURLSession创建task,然后执行task
- 关于task
NSURLSessionTask是一个抽象类,本身不能使用,只能使用它的子类
NSURLSessionDataTask\NSURLSessionUploadTask\NSURLSessionDownloadTask
- 发送get请求
//1.创建NSURLSession对象(可以获取单例对象)
NSURLSession *session = [NSURLSession sharedSession];
//2.根据NSURLSession对象创建一个Task
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=ss&pwd=ss&type=JSON"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//方法参数说明
/*
注意:该block是在子线程中调用的,如果拿到数据之后要做一些UI刷新操作,那么需要回到主线程刷新
第一个参数:需要发送的请求对象
block:当请求结束拿到服务器响应的数据时调用block
block-NSData:该请求的响应体
block-NSURLResponse:存放本次请求的响应信息,响应头,真实类型为NSHTTPURLResponse
block-NSErroe:请求错误信息
*/
NSURLSessionDataTask * dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error) {
//拿到响应头信息
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
//4.解析拿到的响应数据
NSLog(@"%@\n%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding],res.allHeaderFields);
}];
//3.执行Task
//注意:刚创建出来的task默认是挂起状态的,需要调用该方法来启动任务(执行任务)
[dataTask resume];
- 发送get请求的第二种方式
//注意:该方法内部默认会把URL对象包装成一个NSURLRequest对象(默认是GET请求)
//方法参数说明
/*
//第一个参数:发送请求的URL地址
//block:当请求结束拿到服务器响应的数据时调用block
//block-NSData:该请求的响应体
//block-NSURLResponse:存放本次请求的响应信息,响应头,真实类型为NSHTTPURLResponse
//block-NSErroe:请求错误信息
*/
- (nullable NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error))completionHandler;
- 发送POST请求
//1.创建NSURLSession对象(可以获取单例对象)
NSURLSession *session = [NSURLSession sharedSession];
//2.根据NSURLSession对象创建一个Task
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
//创建一个请求对象,并设置请求方法为POST,把参数放在请求体中传递
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = [@"username=520it&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error) {
//拿到响应头信息
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
//解析拿到的响应数据
NSLog(@"%@\n%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding],res.allHeaderFields);
}];
//3.执行Task
//注意:刚创建出来的task默认是挂起状态的,需要调用该方法来启动任务(执行任务)
[dataTask resume];
3,第三方框架 AFN
将单独写一篇文章来解析AFN源码分析...