iOS 网络优化和网络基础

7,884 阅读35分钟

网络优化

网络层的优化手段主要从以下三方面考虑:

  1. 针对链接建立环节的优化
  2. 针对链接传输数据量的优化
  3. 针对链接复用的优化

网络优化具体策略

  1. NSCache 缓存、Last-Modified、ETag
  2. DNS 优化
  3. 请求策略优化(失败重发、缓存请求有网发送、节流等)
  4. 弱网:2G、3G、4G、wifi下设置不同的超时时间
  5. 资源优化(数据压缩,如protobuf、WebP)
  6. TCP对头阻塞:GOOGLE 提出 QUIC 协议,相当于在 UDP 协议之上再定义一套可靠传输协议

一、缓存

GET 网络请求缓存

POST请求不能被缓存,只有 GET 请求能被缓存。因为从数学的角度来讲,GET 的结果是幂等的,就好像字典里的 key 与 value 就是幂等的,而 POST 不是幂等 。缓存的思路就是将查询的参数组成的值作为 key ,对应结果作为 value。

设置缓存步骤:

  1. 使用 GET 请求。
  2. GET 请求,iOS 系统 SDK 已经帮你做好了缓存。你仅仅需要设置内存缓存大小、磁盘缓存大小、以及缓存路径。甚至这两行代码不设置也是可以的,会有一个默认值。
NSURLCache *urlCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024 diskCapacity:20 * 1024 * 1024 diskPath:nil];
[NSURLCache setSharedURLCache:urlCache];

文件缓存:借助 ETag 或 Last-Modified 判断文件缓存是否有效

控制缓存的有效性:

  1. 指定超时时间。
  2. 借助 ETag 或 Last-Modified 判断文件缓存是否有效。
Last-Modified

Last-Modified: 是资源最后修改的时间戳,往往与缓存时间进行对比来判断缓存是否过期。

具体分析: 在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是你请求的资源,同时有一个Last-Modified的属性标记此文件在服务期端最后被修改的时间,格式类似这样:Last-Modified: Fri, 12 May 2006 18:53:33 GMT

客户端第二次请求此URL时,根据 HTTP 协议的规定,浏览器会向服务器传送 If-Modified-Since 报头,询问该时间之后文件是否有被修改过: If-Modified-Since: Fri, 12 May 2006 18:53:33 GMT

如果服务器端的资源没有变化,则自动返回 HTTP 304 (Not Changed.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。

ETag

HTTP 协议规格说明定义ETag为“被请求变量的实体值”。 另一种说法是,ETag是一个可以与Web资源关联的记号(token)。它是一个 hash 值,用作 Request 缓存请求头,每一个资源文件都对应一个唯一的 ETag 值。

通过 ETagIf-None-Match 判断本地缓存数据是否发生变化。如果ETag没改变,则返回状态304不返回,和Last-Modified一样。

ETag 是的功能与 Last-Modified 类似:服务端不会每次都会返回文件资源。客户端每次向服务端发送上次服务器返回的 ETag 值,服务器会根据客户端与服务端的 ETag 值是否相等,来决定是否返回 data,同时总是返回对应的 HTTP 状态码。客户端通过 HTTP 状态码来决定是否使用缓存。比如:服务端与客户端的 ETag 值相等,则 HTTP 状态码为 304,不返回 data。服务端文件一旦修改,服务端与客户端的 ETag 值不等,并且状态值会变为200,同时返回 data。

因为修改资源文件后该值会立即变更。这也决定了 ETag 在断点下载时非常有用。

Last-Modified 和 ETag 比较

在官方给出的文档中提出 ETag 是首选的方式,优于 Last-Modified 方式。因为 ETag 是基于 hash ,hash 的规则可以自己设置,而且是基于一致性,是“强校验”。 Last-Modified 是基于时间,是弱校验,弱在哪里?比如说:如果服务端的资源回滚客户端的 Last-Modified 反而会比服务端还要新。

虽然 ETag 优于 Last-Modified ,但并非所有服务端都会支持,而 Last-Modified 则一般都会有该字段。大多数情况下需要与服务端进行协调支持 ETag ,如果协商无果就只能退而求其次。

其他
  • 值得注意的一点是: 如果借助了 Last-Modified 和 ETag,那么缓存策略则必须使用 NSURLRequestReloadIgnoringCacheData 策略,忽略缓存,每次都要向服务端进行校验。

  • 一些建议: 如果是 file 文件类型,用 Last-Modified 就够了。即使 ETag 是首选,但此时两者效果一致。九成以上的需求,效果都一致。
    如果是一般的数据类型--基于查询的 get 请求,比如返回值是 data 或 string 类型的 json 返回值。那么 Last-Modified 服务端支持起来就会困难一点。因为比如你做了一个博客浏览 app ,查询最近的10条博客, 基于此时的业务考虑 Last-Modified 指的是10条中任意一个博客的更改。那么服务端需要在你发出请求后,遍历下10条数据,得到“10条中是否至少一个被修改了”。而且要保证每一条博客表数据都有一个类似于记录 Last-Modified 的字段,这显然不太现实。
    如果更新频率较高,比如最近微博列表、最近新闻列表,这些请求就不适合,更多的处理方式是添加一个接口,客户端将本地缓存的最后一条数据的的时间戳或 id 传给服务端,然后服务端会将新增的数据条数返回,没有新增则返回 nil 或 304。

二、DNS 优化

DNS 主要解决三类问题

  1. LocalDNS劫持
  2. 平均访问延迟下降
  3. 用户连接失败率下降
  • 预防DNS劫持
    DNS劫持指的是改变DNS请求的返回结果,将目的ip指向另一个地址。一般有两种方式,一是通过病毒的方式改变本机配置的DNS服务器地址,而是通过攻击正常DNS服务器而改变其行为。不管是哪种方式,都会影响app本身的业务请求。如果遇到恶意的攻击还会衍生出各种安全问题。客户端自己做DNS与ip地址的映射就跨过了解析,让劫持者无从下手。

运营商DNS劫持和故障实例图:

  • 很多三四级运营商会把运营解析指向他们的缓存服务器上,并把网页里面的广告替换成他们自己的,或者内嵌他们自己的广告。运营商DNS流量劫持,具体表现在你的H5网页莫名其妙的被加了广告(关于这个问题,也可以做域名白名单,非本域名资源禁止请求,或者H5方面做处理)
  • DNS服务商解析出现故障造成的大批量用户无法正常使用App,按天计算。。
  • DNS解析延迟过高造成的加载超时导致用户体验差

使用 HttpDNS 优化 DNS 解析和缓存

  App内用域名发送请求都要经过DNS解析出ip,然后再根据ip去拿对应的资源,这个过程中,如果LocalDNS中存在这个域名对应的ip,就会直接返回这个ip,类似于App内做缓存。如果不存在,才会去权威DNS查询改访问哪个ip,然后查询到的ip会在LocalDNS中做缓存。

HTTPDNS的实现,根据各自团队的情况可以选择自建或者第三方SDK的方案。根据目前DNS劫持和故障的严重程度,以及实现方案的成本对比。
HTTPDNS集成整体简图:

  • HttpDNS原理:
    A、客户端直接访问 HttpDNS 接口,获取业务在域名配置管理系统上配置的访问延迟最优的 IP。(基于容灾考虑,app内肯定是需要保留使用运营商 LocalDNS 解析域名方式的。)
    B、客户端获取到的 IP 后就直接往此IP发送业务协议请求。以 Http 请求为例,通过在 header 中指定 host 字段,向 HttpDNS 返回的 IP 发送标准的 Http 请求即可。
    总的来说,采用 HttpDNS 来解析域名,就绕过了三四级运营商解析域名会出现的问题,在 HttpDNS 返回了正确的 IP 之后,我们是直接采用 IP 去进行 Http 请求,只需要关注通信内容的安全即可。

HttpDNS 实际使用

接口层

接口层主要为了对外提供简洁的接口,降低使用者的接入成本,提高开发效率,如接口层提供的部分接口如下:

/// 开启HTTPDNS服务
- (void)startHTTPDNS;
/// 白名单列表,如果设置了白名单,则只有在白名单内域名走httpdns服务
@property (nonatomic, copy) NSArray *whiteDomainList;
/// 黑名单列表,如果设置了黑名单,黑名单内域名都不走httpdns,黑名单的优先级最高
@property (nonatomic, copy) NSArray *blackDomainList;
/// 是否允许缓存ip,允许缓存的情况下,在通过第三方服务无法获取ip的情况下,允许使用上次解析成功的ip进行请求,默认YES
@property (nonatomic, assign) BOOL enableCachedIP;
策略层
  • 容灾策略:SDK内部优先使用HTTPDNS服务,当HTTPDNS服务不可用时,即无法获得有效ip时,服务自动降级为运营商的LocalDNS服务,确保不受HTTPDNS服务不可用时导致系统故障无法发出网络请求。注:目前阶段没有接入内置ip策略,后续会考虑。
  • 黑白名单策略:APP内的网络请求域名众多,目前并不是所有的网络请求都走HTTPDNS服务,设置了白名单或者黑名单后,会根据黑白名单中的域名去执行HTTPDNS,如果设置了白名单,则只有白名单内的域名走HTTPDNS服务;如果设置了黑名单,黑名单内的域名不走HTTPDNS服务,黑名单的优先级高于白名单。
  • 缓存策略:缓存策略除了基础服务层中腾讯云HTTPDNS SDK提供的基于TTL的缓存策略外,我们自己封装的接入层SDK中还存在一份内存缓存和本地化持久缓存,持久化缓存主要用来解决启动APP时无法获取HTTPDNS中的IP的问题,内存缓存主要为查询策略提供服务。当某个基于HTTPDNS的IP地址导致请求失败后,会清除当前域名和IP的缓存数据。同时外部可控制是否使用缓存。
  • 查询策略:查询策略主要是为了解决,短时间内同一个域名多次调用基础服务层的域名查询服务,当状态是正在查询中时,后来者不再调用查询服务,直接从缓存策略中的内存缓存中读取可用的IP,如果缓存内也无可用的IP,则直接降级为运营商的LocalDNS查询。查询策略可在确保服务可用的同时,有效减少和HTTPDNS服务器交互的次数。

最佳实践小结
因此,如果你对性能有这很高的要求,同时又需要处理SNI场景的问题,我建议不要直接主动使用HTTPDNS,而是在运营商LocalDNS获取的IP请求失败的情况下,可以在底层直接使用基于CFNetwork的网络请求进行重试,这样就能在请求DNS劫持和性能中间得到一个平衡,既能保证在运营商的LocalDNS解析出现问题时能够走HTTPDNS,保证成功率和可用性;同时又能够在运营商的LocalDNS可用时,使用基于NSURLSession的请求,享受系统实现的HTTP2.0特性带来的性能提升。

备选LocalDNS原因
如果之前访问api.weibo.cn的是联通用户,现在新用户使用电信来访问api.weibo.cn,由于localDNS缓存的存在,不会去查询新浪的权威DNS,这样返回的ip是联通这个运营商的ip,从而会使得用户出现访问变慢等状况。缓存还会导致一点就是,当权威DNS将域名与ip的映射发生改变之后,由于LocalDNS缓存没有及时改变,用户就会访问到错误的服务器,或者直接访问不到资源。

基于HttpDNS扩展

HttpDNS部署等有难度,替代方案:APP内置severs IP list,ping出最合适的Sever address。省去DNS查询时间。

  1. 在App内维护一个Serve IP List。把每次App从HttpDNS取到的ip存储进入该数组,并设置权重,理论上来说从HttpDns解析下来的ip权重是最大的。这个List可以在App启动的时候,进行更新,同时取出本地缓存的Serve IP List的权重最大的ip进行数据的初始化操作(如果第一次启动,没有该List的话,就使用LocalDNS进行解析)。 Serve IP List里面的权重设置机制,很明显的一点就是从DNS解析出来的ip具有最大的权重,每次从List里面取ip应该要取权重最大的ip。列表中的ip也是需要可以动态更新配置的,根据连接或者服务的成功失败来进行动态调整,这样即使DNS解析失败,用户在一段时间后也会取到合适的ip进行访问。
  2. 对ip进行数据统计。在所有app内统计每个ip进行请求所需平均时间、最长时间、最短时间、请求成功次数、失败次数,需要注意的是,要区分网络环境进行统计,Wifi、4G、3G,对在不同的网络环境下数据优秀的ip进行存储,下发到App里面使用起来。这样每次启动App时可以对收集起来的ip根据不同的网络环境进行测速,选择最好的ip进行请求。需要注意的是,在网络环境切换的时候,必须要重新进行速度测试。做到这一步,可以节约DNS解析时间,以及劫持的问题。
  3. 将图片、音频等资源放到单独的服务器里面,与其他资源分开。 第一个是多个域名可以增加并行下载条数,因为客户端对同一个域的域名下载条数是有限制的,所以多个域就会增加并行下载条数,从而加快加载速度。当然二级域名也不能使用太多,因为太多要考虑到dns的解析花费的时间。 第二个是方便管理,一般来说,图片在站点的加载中是最占带宽的,可以用独立服务器方便后期管理;还可以使用异步加载的方式,增强用户体验。同时是图片多是静态内容,可以更好的使用CDN加速。 第三是如果使用了独立服务器的话,在安全设置上可以有差别的针对设置,很是方便。
  4. 在防止劫持这一块,需要注意把资源的后缀名去掉,比如说.mp3.json这样的后缀,以免击中运营商的拦截。

三、失败重发、缓存请求有网发送、节流等

失败重发

和后台确定一些错误码,根据错误码类型判断是否需要重发会比较合理。一般3次的重试基本可以排除网络抖动的情况。三次失败之后即可认为请求失败。

百分百送达服务器

对于百分百送达服务器的请求,第一步并不是直接发送,而是存入本地数据库中,这样即使杀掉进程、断电、重启等极端操作,请求数据也依旧存在,我们可以在App重启或者进入该业务界面时,还原请求数据到内存中,重新进行发送。

节流

使用一定的节流策略,如延迟请求、取消当前请求、取消所有请求等

四、弱网优化

www.52im.net/thread-2479…

五、资源优化

资源优化基本就是尽可能的缩小传输数据的大小,首先是图片大小的解决方案。

方案: 在一定程度上使用webp来代替jpg、png图片

需要注意的是,webp的图片要通过解析才能成为可用的jpg图片,在iOS开发中,可以使用SDWebImage框架进行解析,webp->NSData->Image,app内解析是肯定需要花费一定时间和性能的。 在wifi条件下,超过300300的图片使用webp图片,解析时间+下载时间是比直接使用jpg\png图片要快的,并且在流量方面也是消耗很小。低于300300的可以直接下载使用jpg\png图片。在4G条件下,用户可能会对流量比较敏感,建议都走webp图片。

网络基础

一、HTTPS

HTTPS 是在 HTTP 基础上增加了 SSL 层,传递加密的内容,比较安全。

HTTPS 请求过程

  1. 客户端发起HTTPS请求
  2. 服务端的配置数字证书,这套证书其实就是一对公钥和私钥。
  3. 服务器端返回数字证书的公钥。公钥包含了很多信息,如证书的颁发机构,过期时间等等。
  4. 客户端解析证书。由客户端的TLS来完成的,首先会验证公钥是否有效,如颁发机构,过期时间等。如果发现异常,则会弹出一个警告框,提示证书存在问题。如果证书没有问题,那么就生成一个随即值,然后用证书对该随机值进行加密。
  5. 客户端发送加密信息。这部分传送的是用证书加密后的随机值,目的就是让服务端得到这个随机值,以后客户端和服务端的通信就通过这个随机值来进行加密解密。
  6. 服务段解密信息。用私钥解密客户端传过来的随机值(私钥),然后把内容通过该值进行对称加密。
  7. 服务器端向客户端传输加密后的信息。
  8. 客户端解密信息。客户端用之前生成的私钥解密服务段传过来的信息,于是获取了解密后的内容。

对称加密: 采用单钥加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密。由于其速度快,对称性加密通常在消息发送方需要加密大量数据时使用。

非对称加密算法需要两个密钥:公钥和私钥。如果用公钥对数据进行加密,只有用对应的私钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。

HTTPS 连接,从输入账号密码点击登陆按钮到服务器返回这个请求前,这期间经历了什么?
  1. 客户端打包请求。
    其中包括URL、端口、账号和密码等。使用账号和密码登陆应该用的是POST方式,所以相关的用户信息会被加载到body中。这个请求应该包含3个方面:网络地址、协议和资源路径。注意:这里用的是HTTPS,即HTTP+SSL/TLS,在HTTP上又加了一层处理加密信息的模块(相当于加了一个锁)。这个过程相当于客户端请求钥匙。
  2. 服务器端接受请求。
    一般客户端的请求会先被发送到DNS服务器中。DNS服务器负责将网络地址解析成IP地址,这个IP地址对应网上的一台计算机。这其中可能发生Hosts Hijack和ISP failure的问题。过了DNS这一关,信息就到服务器端,此时客户端和服务端的端口之间会建立一个socket连接。socket一般都是以file descriptor的方式解析请求的。这个过程相当于服务器端分析是否要想客户端发送钥匙模板。
  3. 服务器端返回数字证书。
    服务器端会有一套数字证书(相当于一个钥匙模板),这个证书会先被发送个客户端。这个过程相当于服务端向客户端发送钥匙模板。
  4. 客户端生成加密信息。
    根据收到的数字证书(钥匙模板),客户端就会生成钥匙,并把内容锁起来,此时信息已经被加密。这个过程相当于客户端生成钥匙并锁上请求。
  5. 客户端发送加密信息。
    服务器端会收到由自己发送的数字证书加密的信息。这个时候生成的钥匙也一并被发送到服务端。这个过程相当于客户端发送请求。
  6. 服务端解锁加密信息。
    服务端收到加密信息后,会根据得到的钥匙进行解密,并把要返回的数据进行对称加密。这个过程相当于服务器端解锁请求,生成、加锁回应信息。
  7. 服务器端向客户端返回信息。
    客户端会收到相应的加密信息。这个过程相当于服务器端向客户端发送回应信息。
  8. 客户端解锁返回信息。
    客户端会用刚刚生成的钥匙进行解密,将内容显示在浏览器上。

HTTPS 和 HTTP 区别

  1. https协议需要到ca申请证书,一般免费证书很少,需要交费。
  2. http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议。
  3. http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  4. http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

HTTPS 的产生 => HTTP的缺点

  1. 传输过程中使用明文,内容可以被窃听。
  2. 无法验证对方身份,可能遭遇到伪装。
  3. 不知道报文的完整性,有可能被篡改。

二、HTTP2.0 新特性浅析

HTTP2.0 新特性

二进制分帧 首部压缩 多路复用 服务器推送 请求优先级

HTTP2.0 的优势

HTTP/2 主要是 HTTP/1.x 在底层传输机制上的完全重构,相比 HTTP/1.x,HTTP/2 在底层传输做了很大的改动和优化:

  1. HTTP/2 采用二进制格式传输数据,而非 HTTP/1.x 的文本格式。二进制格式在协议的解析和优化扩展上带来更多的优势和可能。
  2. HTTP/2 对消息头采用 HPACK 进行压缩传输,能够节省消息头占用的网络的流量。而 HTTP/1.x 每次请求,都会携带大量冗余头信息,浪费了很多带宽资源。头压缩能够很好的解决该问题。
  3. 多路复用,直白的说就是所有的请求都是通过一个 TCP 连接并发完成。HTTP/1.x 虽然通过 pipeline 也能并发请求,但是多个请求之间的响应会被阻塞的,所以 pipeline 至今也没有被普及应用,而 HTTP/2 做到了真正的并发请求。同时,流还支持优先级和流量控制。
  4. Server Push:服务端能够更快的把资源推送给客户端。例如服务端可以主动把 JS 和 CSS 文件推送给客户端,而不需要客户端解析 HTML 再发送这些请求。当客户端需要的时候,它已经在客户端了。

三、HTTP、TCP、UDP、IP

1. HTTP

HTTP,超文本传输协议。是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式。由于其简捷、快速的方式,适用于分布式超媒体信息系统,是现今在WWW上应用最多的协议。

HTTP 由来

我们在传输数据时,可以只使用(传输层)TCP/IP协议,但是那样的话,如果没有应用层,便无法识别数据内容,如果想要使传输的数据有意义,则必须使用到应用层协议,应用层协议有很多,比如HTTP、FTP、TELNET等,也可以自己定义应用层协议。WEB使用HTTP协议作应用层协议,以封装HTTP文本三/IP做传输层协议将它发到网络上。

HTTP 协议的作用

HTTP 的全称是 Hypertext Transfer Protocol,超文本传输协议。
规定客户端和服务器之间的数据传输格式。
让客户端和服务器能有效地进行数据沟通。

HTTP 请求的方法

发送HTTP请求的方法在HTTP/1.1协议中,定义了8种发送http请求的方法 :GET、POST、PUT、DELETE、HEAD、OPTIONS、TRACE、CONNECT、PATCH。

  • POST、GET 的区别

    • GET 方法
      GET 方法提交数据不安全,数据置于请求行,客户端地址栏可见;
      GET 方法提交的数据大小有限,不过因为浏览器不同,一般限制在2~8K之间
      GET 方法不可以设置书签
      GET 请求能够被缓存
    • POST 方法
      POST 方法提交数据安全,数据置于消息主体内,客户端不可见
      POST 方法提交的数据大小没有限制。靠服务器限制。
      POST 方法可以设置书签
      POST 请求不能够被缓存
  • POST、PUT 的区别

    • PUT
      文件大小无限制
      可以覆盖文件
    • POST
      通常有限制2M
      新建文件,不能重名
HTTP 请求头
GET /home.html HTTP/1.1 Host: developer.mozilla.org 
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 
Accept-Encoding: gzip, deflate, br Referer: https://developer.mozilla.org/testpage.html 
Connection: keep-alive 
Upgrade-Insecure-Requests: 1 
If-Modified-Since: Mon, 18 Jul 2016 02:36:04 GMT 
If-None-Match: "c561c68d0ba92bbeb8b0fff2a9199f722e3a621a" 
Cache-Control: max-age=0
HTTP 相应头
Access-Control-Allow-Origin: *
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Mon, 18 Jul 2016 16:06:00 GMT
Etag: "c561c68d0ba92bbeb8b0f612a9199f722e3a621a"
Keep-Alive: timeout=5, max=997
Last-Modified: Mon, 18 Jul 2016 02:36:04 GMT
Server: Apache
Set-Cookie: mykey=myvalue; expires=Mon, 17-Jul-2017 16:06:00 GMT; Max-Age=31449600; Path=/; secure
Transfer-Encoding: chunked
Vary: Cookie, Accept-Encoding
x-frame-options: DENY
HTTP 协议的主要特点
  1. 支持客户/服务器模式。
  2. 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
  3. 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
  4. 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
  5. 无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
常见状态码

200:请求成功。
400:客户端请求语法的错误,服务器无法解析。
404:服务器无法通过客户端的请求找到资源。
500:服务器内部错误无法完成请求。

2. TCP 连接的三次握手和四次挥手

  • 三次握手
    1. 客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
    2. 服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
    3. 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

三次握手目的是确认双方的接收与发送能力是否正常。

理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次挥手”。

  • 三次握手过程中可以携带数据吗?
    握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。
    但其实第三次握手的时候,是可以携带数据的,第一次、第二次握手不可以携带数据。
    假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病。

  • 四次挥手

    1. 客户端发送一个 FIN 报文(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认。
    2. 服务端收到 FIN,发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了(ACK=1,确认号ack=u+1,序号seq=v),服务端进入CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的连接释放报文段。
    3. 如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态,即服务端没有要向客户端发出的数据,服务端发出连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),服务端进入LAST_ACK(最后确认)状态,等待客户端的确认。
    4. 客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态,服务端收到 ACK 报文之后,关闭连接,处于 CLOSED 状态。

3. TCP 和 UDP 的区别

TCP:传输控制协议。
是一种面向连接(连接导向)的、可靠的、基于字节流的运输层(Transport layer)通信协议。
TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。

UDP:用户数据包协议。
是一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。
UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。

面向连接:是指通信双方在通信时,要事先建立一条通信线路,其有三个过程:建立连接、使用连接和释放连接。电话系统是一个面向连接的模式,拨号、通话、挂机;TCP协议就是一种面向连接的协议。

面向无连接:是指通信双方不需要事先建立一条通信线路,而是把每个带有目的地址的包(报文分组)送到线路上,由系统自主选定路线进行传输。邮政系统是一个无连接的模式,天罗地网式的选择路线,天女散花式的传播形式;IP、UDP协议就是一种无连接协议。

4. HTTP、TCP、UDP 三者的关系

都是通信协议,也就是通信时所遵守的规则,只有双方按照这个规则“说话”,对方才能理解或为之服务。

HTTP 本身就是一个协议,是从Web服务器传输超文本到本地浏览器的传送协议。HTTP协议是基于TCP连接的。主要解决如何包装数据。

TCP/IP 是个协议组,分为四个层次:网络接口层、网络层、传输层和应用层。主要解决数据如何在网络中传输。
网络层:IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。
传输层:TCP协议与UDP协议。
应用层:FTP、HTTP、TELNET、SMTP、DNS等协议。

四、Socket

网页(网址以http://开头)都是 http 协议传输到你的浏览器的,而 http 是基于 socket 之上的。socket 是一套完成 tcp,udp 协议的接口。

TCP和UDP协议属于传输层 。而http是个应用层的协议

什么是socket?

Socket 是对 TCP/IP 协议的封装,Socket 本身并不是协议,而是一个调用接口(API),通过 Socket,我们才能使用 TCP/IP 协议。Socket 的出现只是使得程序员更方便地使用 TCP/IP 协议栈而已,是对 TCP/IP 协议的抽象,从而形成了我们知道的一些最基本的函数接口。

socket连接和http连接的区别?

http 连接:http连接就是所谓的短连接,即客户端向服务器端发送一次请求,服务器端响应后连接即会断掉;
socket连接:socket连接就是所谓的长连接,理论上客户端和服务器端一旦建立起连接将不会主动断掉;但是由于各种环境因素可能会断开,比如:服务器或客户端主机 down 了,网络故障,或者两者之间长时间没有数据传输,网络防火墙可能会断开该连接以释放网络资源。所以当一个 socket 连接中没有数据的传输,那么为了维持连接需要发送心跳消息,具体心跳消息格式是开发者自己定义的。

利用Socket建立网络连接的步骤:

建立Socket连接至少需要一对套接字,一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket。
套接字之间的连接过程分为三个步骤:

  1. 服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
  2. 客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
  3. 连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

五、URL

  • 什么是URL URL 的全称是 Uniform Resource Locator(统一资源定位符)。 通过1个URL,能找到互联网上唯一的1个资源。 URL 就是资源的地址、位置,互联网上的每个资源都有一个唯一的URL。

  • URL 的基本格式 = 协议://主机地址/路径 协议:不同的协议,代表着不同的资源查找方式、资源传输方式 主机地址:存放资源的主机的IP地址(域名) 路径:资源在主机中的具体位置

  • URL 中常见的协议 http 协议是在网络开发中最常用的协议 超文本传输协议,访问的是远程的网络资源,格式是 http:// file 访问的是本地计算机上的资源,格式是 file://(不用加主机地址) mailto 访问的是电子邮件地址,格式是 mailto: FTP 访问的是共享主机的文件资源,格式是 ftp://

六、其他

如果在网络数据处理过程中,发现一处比较卡,一般怎么解决?

  1. 检查网络请求是否被放在主线程了
  2. 看看异步请求的数量是否太多了(子线程的数量)
  3. 数据量是否太大?如果太大,先清除一些不必要的对象(看不见的数据、图片)
  4. 手机的CPU使用率和内存问题

iOS 网络编程层次结构分为三层:

Cocoa 层: NSURL,Bonjour,Game Kit,WebKi。这层是最上层的基于 Objective-C 的 API,比如 URL 访问,NSStream,Bonjour,GameKit等,这是大多数情况下我们常用的 API。Cocoa 层是基于 Core Foundation 实现的。(可触摸层)。
Core Foundation 层: 基于 C 的 CFNetwork 和 CFNetServices。因为直接使用 socket 需要更多的编程工作,所以苹果对 OS 层的 socket 进行简单的封装以简化编程任务。该层提供了 CFNetwork 和 CFNetServices,其中 CFNetwork 又是基于 CFStream 和 CFSocket。(核心服务层)。
OS 层: 基于 C 的 BSD socket。(核心操作系统层)

数据安全

网络数据加密

加密对象:隐私数据,比如密码、银行信息 加密方案:

  1. 提交隐私数据,必须用POST请求
  2. 使用加密算法对隐私数据进行加密,比如MD5
  3. 加密增强:为了加大破解的难度 对明文进行2次MD5 : MD5(MD5(pass))先对明文撒盐,再进行MD5MD5(pass)) 先对明文撒盐,再进行MD5 : MD5(pass.$salt)
  4. BASE 64:是网络传输中最常用的编码格式,用来将二进制的数据编码成字符串的编码方式。

本地存储加密

加密对象:重要的数据,比如游戏数据

代码安全问题

现在已经有工具和技术能反编译出源代码:逆向工程

  • 反编译出来的都是纯 C 语言的,可读性不高
  • 最起码能知道源代码里面用的是哪些框架

解决方案:发布之前对代码进行混淆

  • 混淆之前
@interface HMPerson :NSObject
- (void)run;
- (void)eat;
@end
  • 混淆之后
@interface A :NSObject
- (void)a;
- (void)b;
@end

文件解压缩

技术方案

  1. 第三方框架:SSZipArchive
  2. 依赖的动态库:libz.dylib

压缩方法

  1. 第一个方法 zipFile :产生的zip文件的最终路径 directory : 需要进行的压缩的文件夹路径 [SSZipArchive createZipFileAtPath:zipFile withContentsOfDirectory:directory];

  2. 第一个方法 zipFile :产生的zip文件的最终路径 files : 这是一个数组,数组里面存放的是需要压缩的文件的路径 files = @[@"/Users/apple/Destop/1.png", @"/Users/apple/Destop/3.txt"] [SSZipArchive createZipFileAtPath:zipFile withFilesAtPaths:files];

解压缩

zipFile :需要解压的zip文件的路径 dest : 解压到什么地方 [SSZipArchive unzipFileAtPath:zipFile toDestination:dest];

参考文章

APP网络优化之DNS优化实践 APP域名容灾方案