戳我
戳我
文章目录
  1. HTTP请求中的GET和POST
  2. TCP与UDP
    1. 三次握手
    2. 四次挥手
  3. HTTP与HTTPS
  4. 数字签名和数字证书
  5. HTTP如何做缓存
  6. HTTP长连接 vs TCP连接 vs websocket
    1. 长连接
    2. TCP连接
    3. websocket
  7. HTTP2.0针对同一个域名的多个请求, 会建立多少个tcp连接
  8. TCP的拥塞控制机制
  9. HTTP post的body体使用form-urlencoded和multipart/form-data的区别
  10. 网络基本模型
    1. 物理层
    2. 数据链路层
    3. 网络层
    4. 传输层
    5. 会话层
    6. 表示层
    7. 应用层
  11. 完整的网络请求过程解析

iOS开发总结系列-网络编程

这里主要是一些计算机的基础知识部分, 在平常业务中不怎么用, 但是对深入了解计算机原理有很大帮助.

HTTP请求中的GET和POST

GET和POST是网络请求到的两种基本方式.
GET把参数包含在URL中, POST通过request body传递参数.
GET在浏览器回退时是无害, 而POST会再次提交请求.
GET产生的URL地址可以保存为书签, 而POST不可以.
GET请求会被浏览器主动cache, 而POST不会, 除非手动设置.
GET请求只能进行url编码, 而POST支持多种编码方式.
GET请求参数会被完整保留在浏览器历史记录里, 而POST中的参数不会被保留.
GET请求在URL中传送的参数是有长度限制的, 而POST没有.
对参数的数据类型, GET只接受ASCII字符, 而POST没有限制.
GET比POST更不安全, 因为参数直接暴露在URL上, 所以不能用来传递敏感信息.
GET参数通过URL传递, POST放在Request body中.
GET和POST都是HTTP协议中的两种发送请求的方法.HTTP是基于TCP/IP的关于数据如何在万维网中如何通信的协议.HTTP的底层是TCP/IP, 所以GET和POST的底层也是TCP/IP. 也就是说, GET/POST都是TCP链接. GET和POST能做的事情是一样一样的. 你要给GET加上request body, 给POST带上url参数, 技术上是完全行的通的

TCP与UDP

TCP面向连接(如打电话要先拨号建立连接); UDP是无连接的, 即发送数据之前不需要建立连接.
TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
TCP首部开销20字节;UDP的首部开销小,只有8个字节
TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道.
TCP协议会向对方发一个带有ACK标志的空数据包(KeepAlive探针), 对方在收到ACK包以后,如果连接一切正常,应该回复一个ACK;如果连接出现错误了,则应当回复一个RST;如果对方没有回复,服务器每隔intvl的时间再发ACK,如果连续probes个包都被无视了,说明连接被断开了; UDP协议的客户端需要定时向服务器发送心跳包,告诉服务器自己在线.

TCP: 面向连接, 传输可靠(保证数据正确性, 保证数据顺序), 用于传输大量数据(流模式), 速度慢, 建立连接需要开销较多(时间, 系统资源).
UDP: 面向非连接, 传输不可靠, 用于传输少量数据(数据包模式), 速度快.

三次握手

第一次握手: 建立连接, 客户端发送连接请求报文段, 将SYN位置为1, Sequence Number置为x; 然后, 客户端进入SYN_SEND状态, 等待服务器确认.
第二次握手: 服务器收到SYN报文段, 服务器收到客户端的SYN报文段, 需要对这个报文段确认, 设置Acknowledgment Number为x+1(Sequence Number+1); 同时, 自己还要发送SYN请求信息, 将SYN位置为1, Sequence Number为y; 服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中, 一并发送给客户端, 此时服务器进入SYN_RECV状态.
第三次握手: 客户端收到服务器的SYN+ACK报文段, 然后将Acknowledgment Number设置为y+1, 向服务器发送ACK报文段, 这个报文段发送完毕以后, 客户端和服务器端都进入ESTABLISHED状态, 完成TCP三次握手. 完成了三次握手, 客户端和服务器端就可以开始传送数据.

四次挥手

第一次挥手: 主机1(可以使客户端, 也可以是服务端), 设置设置Sequence Number和Acknowledgment Number, 向主机2发送一个FIN报文段; 此时,主机1进入FIN_WAIT_1状态; 这表示主机1没有数据要发送给主机2了.
第二次挥手: 主机2收到了主机1发送的FIN报文段, 向主机1回一个ACK报文段, Acknowledgment Number为Sequence Number加1; 主机1进入FIN_WAIT_2状态; 主机2告诉主机1, 我”同意”你的关闭请求.
第三次挥手: 主机2向主机1发送FIN报文段, 请求关闭连接, 同时主机2进入LAST_ACK状态.
第四次挥手: 主机1收到主机2发送的FIN报文段, 向主机2发送ACK报文段, 然后主机1进入TIME_WAIT状态; 主机2收到主机1的ACK报文段以后, 就关闭连接; 此时, 主机1向等待2MSL后依然没有收到回复, 则证明主机2已经正常关闭, 那么主机1也就此关闭.

HTTP与HTTPS

HTTPS的主要思想是在不安全的网络上创建一安全信道, 并可在使用适当的加密包和服务器证书可被验证且可被信任时, 对窃听和中间人攻击提供合理的防护. HTTPS的信任继承基于预先安装在浏览器中的证书颁发机构.
HTTPS中的S指的就是TLS(SSL), 相当于是HTTP over SSL. 使用SSL/TLS协议进行加解密, 下面我们来详细说一下是如何加密的. HTTP默认端口是80, HTTPS默认端口是443.

  1. 客户端给出协议号和一个随机数A, 以及客户端支持的加密算法.
  2. 服务端确认双方使用的加密算法, 并给出数字证书(包含了网站的地址, 加密用的公钥, 以及证书的颁发机构, 过期时间等), 以及一个服务器生成的随机数B.
  3. 客户端解析证书, 传送加密信息: 确认数字证书有效, 然后生成一个新的随机数C, 并且使用数字证书中的公钥, 加密这个随机数, 发送给服务端.
  4. 服务端解密信息: 服务端使用自己的私钥进行解密, 获取客户端发来的随机数, 使用该随机序列号, 对该消息进行加密, 验证的到的校验值是否与客户端发来的一致.
  5. 客户端和服务端使用约定的加密方法, 使用前面的三个随机数, 生成对话密钥, 用对话密钥来加密接下来的整个过程.

数字签名和数字证书

这里讲的特别详细, 我做一个总结和整理.

  1. 服务端有两把钥匙, 一个公钥一个私钥.
  2. 服务端把公钥发送到客户端.
  3. 客户端向服务端传输信息可以使用公钥来加密, 以达到保密的效果. 服务端收到信息后, 用私钥解密. 只要服务端的私钥不泄密, 意味着这封信就是安全的.
  4. 服务端向客户端发送信息, 对信息Hash化, 生成信息的摘要, 然后再使用私钥对这个摘要进行加密, 加密的结果就是数字签名. 服务端将这个签名附在信息中, 一起下发给客户端.
  5. 客户端收到信息后, 将数字签名用公钥解密, 得到信息的摘要, 这就可以证明这些信息是由服务端下发的.
  6. 客户端再对信息本身Hash化, 将得到的结果与上一步骤中的摘要对比, 如果两者一致, 就说明信息没有被篡改过.
  7. 那么如果有一个第三方, 拦截了客户端与原服务端的传递, 用自己的公钥换走了原服务端的公钥, 那么此时客户端使用的就是第三方的公钥和数字签名, 原服务端给客户端发消息, 就是第三方的假消息, 而不是原服务端的消息.
  8. 这时候我们无法保证公钥属于原服务端还是第三方, 那么我们就要去找证书中心(CA), 为公钥做认证. 证书中心用自己的私钥, 对原服务端的一些相关信息一起加密, 生成数字证书.
  9. 原服务端拿到数字证书后, 再给客户端下发信息, 就可以把数字证书附在信息中.
  10. 客户端收到信息后, 用CA的公钥解开数字证书, 就可以拿到服务端真正的公钥了, 然后就知道数字签名是不是真服务端签的.

举例: 客户端向服务端发出加密请求, 服务端用自己的私钥加密后, 连同数字证书一起发送给客户端. 客户端(浏览器)中内置有证书管理器, 有”受信任的根证书颁发机构”列表. 客户端会根据这张表查看解开数字证书的公钥是否在列表内. 如果数字证书记载的网址与浏览的不一致, 就说明这张证书被冒用, 浏览器会发出警告. 如果这张数字证书不是由受信任的机构颁发的, 浏览器会发出另一种警告(12306)就是第二种警告. 如果数字证书是可靠的, 客户端就可以使用证书中的服务器公钥, 对信息进行加密, 然后与服务器交换加密信息.

HTTP如何做缓存

http1.0 时代给客户端设定缓存方式可通过两个字段: Pragma和Expires来规范.
当该字段值为no-cache的时候(事实上现在RFC中也仅标明该可选值), 会知会客户端不要对该资源读缓存, 即每次都得向服务器发一次请求才行. 有了Pragma来禁用缓存, 自然也需要有个东西来启用缓存和定义缓存时间, 对http1.0而言, Expires就是做这件事的首部字段. Expires的值对应一个GMT(格林尼治时间), 比如Mon, 22 Jul 2002 11:12:01 GMT来告诉浏览器资源缓存过期时间, 如果还没过该时间点则不发请求.

HTTP1.1后, 新增了 Cache-Control 来定义缓存过期时间. 注意: 若报文中同时出现了 Expires 和 Cache-Control, 则以 Cache-Control 为准. 也就是说优先级从高到低分别是 Pragma -> Cache-Control -> Expires.

作为request的header时, cache-directive 的可选值有:
作为request的header
作为response的header时, cache-directive 的可选值有:
作为response的header
Cache-Control 允许自由组合可选值.

另外还有两个字段Last-Modified和ETag.
服务器将资源传递给客户端时, 会将资源最后更改的时间以“Last-Modified: GMT”的形式加在实体首部上一起返回给客户端. 格式为: Last-Modified: Fri, 22 Jul 2016 01:47:00 GMT. 客户端会为资源标记上该信息, 下次再次请求时, 会把该信息附带在请求报文中一并带给服务器去做检查, 若传递的时间值与服务器上该资源最终修改时间是一致的, 则说明该资源没有被修改过, 直接返回304状态码, 内容为空, 这样就节省了传输数据量. 如果两个时间不一致, 则服务器会发回该资源并返回200状态码, 和第一次请求时类似. 这样保证不向客户端重复发出资源, 也保证当服务器有变化时, 客户端能够得到最新的资源. 一个304响应比一个静态资源通常小得多, 这样就节省了网络带宽.

为了解决上述Last-Modified可能存在的不准确的问题, Http1.1还推出了 ETag 实体首部字段. 服务器会通过某种算法, 给资源计算得出一个唯一标志符(比如md5标志), 在把资源响应给客户端的时候, 会在实体首部加上”ETag: 唯一标识符”一起返回给客户端.

更多内容可以看这里.

HTTP长连接 vs TCP连接 vs websocket

长连接

长连接: 在HTTP 1.1, 客户端发出请求, 服务端接收请求, 双方建立连接, 在服务端没有返回之前保持连接, 当客户端再发送请求时, 它会使用同一个连接. 这一直继续到客户端或服务器端认为会话已经结束, 其中一方中断连接.

优势: 减少了连接请求, 降低TCP阻塞, 减少了延迟, 实时性较好.
劣势: 可能会影响性能, 因为它在文件被请求之后还保持了不必要的连接很长时间.

HTTP是无状态, 的也就是说, 浏览器和服务器每进行一次HTTP操作, 就建立一次连接, 但任务结束就中断连接(短连接). 当需要建立 HTTP 长连接时, HTTP 请求头将包含如下内容: Connection: Keep-Alive. 如果服务端同意建立长连接, HTTP 响应头也将包含如下内容: Connection: Keep-Alive. 当需要关闭连接时, HTTP 头中会包含如下内容: Connection: Close.

TCP连接

TCP 连接两端好比两个人, 这两个人之间保持通信往来(建立 TCP 连接). 如果他俩经常通信(经常发送 TCP 数据), 那这个 TCP 连接自然是建立着的. 但如果两人只是偶尔通信, 那么, 其中一个人(或两人同时)想知道对方是否还在, 就会定期发送一份邮件(Keep Alive 探测包), 这个邮件没有实质内容, 只是问对方是否还在, 如果对方收到, 就会回复说还在(对这个探测包的 ACK 回应).
TCP Keep Alive 用于探测对端是否存在, 而 HTTP Keep Alive 用于协商以复用 TCP 连接. 即便一个 TCP 连接未启用 Keep Alive 功能, 也不妨碍 HTTP 层面开启长连接.

websocket

很多网站为了实现推送技术, 所用的技术都是轮询. 轮询是在特定的的时间间隔(如每1秒), 由浏览器对服务器发出HTTP请求, 然后由服务器返回最新的数据给客户端的浏览器. 这种传统的模式带来很明显的缺点, 即浏览器需要不断的向服务器发出请求, 然而HTTP请求可能包含较长的头部, 其中真正有效的数据可能只是很小的一部分, 显然这样会浪费很多的带宽等资源. 因此HTML5定义了WebSocket协议, 能更好的节省服务器资源和带宽, 并且能够更实时地进行通讯.

WebSocket 使得客户端和服务器之间的数据交换变得更加简单, 允许服务端主动向客户端推送数据. 在 WebSocket API 中, 客户端发送一次http websocket请求, 服务器响应请求, 浏览器和服务器只需要完成一次握手, 建立持久连接,并进行双向数据传输,后面不进行HTTP连接,而是使用TCP连接.

websocket有以下优点:

  1. 较少的控制开销. 在连接创建后, 服务器和客户端之间交换数据时, 用于协议控制的数据包头部相对较小, 相对于HTTP请求每次都要携带完整的头部, 此项开销显著减少了.
  2. 更强的实时性. 协议是全双工的, 服务端可以随时下发信息到客户端, HTTP请求需要等待客户端发起请求服务端才能响应.
  3. 保持连接状态. Websocket需要先创建连接, 这就使得其成为一种有状态的协议, 之后通信时可以省略部分状态信息. 而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等).
  4. 更好的二进制支持. Websocket定义了二进制帧, 相对HTTP, 可以更轻松地处理二进制内容.
  5. Websocket定义了扩展, 用户可以扩展协议, 实现部分自定义的子协议. 如部分浏览器支持压缩等.
  6. 更好的压缩效果. 相对于HTTP压缩, Websocket在适当的扩展支持下, 可以沿用之前内容的上下文, 在传递类似的数据时, 可以显著地提高压缩率.

HTTP2.0针对同一个域名的多个请求, 会建立多少个tcp连接

这里主要是HTTP2.0的多路复用机制.

在同一个域名下, 开启一个TCP的connection, 每个请求以stream的方式发送, 每个steam有一个唯一标识. connection一旦建立, 后续的请求都可以复用这个connection并且可以同时发送, server端可以根据stream的唯一标识来相应对应的请求.
直到客户端不需要与服务器进行进一步的通信(例如主动用户离开这个页面), 或者服务器主动关闭connection, 这个connection就断开.

stream的基本组成单位是frame(二进制帧), 每种frame又分为很多种类型例如HEADERS Frame(头部帧), DATA Frame(内容帧)等等. 请求头HEADERS Frame组成了resquest, 返回头HEADERS Frame和DATA Frame组成了response, request和response组成了一个stream.

TCP的拥塞控制机制

拥塞控制是TCP协议的意向重要功能, TCP的拥塞机制是从端到端的角度, 推测网络是否发生拥堵, 如果拥堵则降低数据发送效率, 以缓解网络拥塞. 拥塞机制控制算法包括了慢启动, 拥塞避免, 快速重传, 快速恢复四部分.
TCP的拥塞控制采用的是窗口机制, TCP的发送端维持一个称为拥塞窗口cwnd的变量, 单位为字节, 用于表示在未收到接收端确认的情况下, 可以连续发送的数据字节数. 他会动态变化, 与网络拥塞情况成反比.

  • 慢启动指的是, 在TCP的开始传输阶段, 因为不知道网络负荷情况, 因此采用试探的方法, 逐渐增大拥塞窗口.
  • 拥塞避免指的是, 为了防止拥塞窗口增长过快而引起网络拥塞, TCP还需要设置一个慢启动阈值ssthresh, 当拥塞窗口的值增加到阈值时, 就减缓拥塞窗口的增长速度. 当网络拥塞时, 我们重新执行慢启动算法.
  • 快速重传指的是, 接收端每收到一个失序的数据报文段后就立即发出重复确认, 以便更早地通知发送端有丢包的情况发生. 举例: 接收端收到了1, 2两个报文, 接下来应该收到3. 但是, 直接收到了4, 这时就会向发送端发送2号数据报文段的确认, 这个称为重复确认. 继续往下走接收到了5, 6报文, 接收端仍然要向发送端发出2号数据报文段的重复确认. 这时发送端会收到多个2号数据报文段的重复确认, 则认为3号数据报文段发生了丢包, 需要立即向接收端重传3号数据报文段, 而不需要等待重传计时器到期再重传. 快速重传算法中规定如果收到某数据报文段的三个重复确认, 则立即重传下一个数据报文段.
  • 快速重连指的是, 当发送端连续收到三个重复确认时, 就将慢启动阈值ssthresh减半, 以预防网络拥塞的发生, 并且将拥塞窗口cwnd的值置为减半后的ssthresh, 然后开始执行拥塞避免算法, 使得cwnd缓慢地加性增大.

HTTP post的body体使用form-urlencoded和multipart/form-data的区别

  • form-urlencoded是默认的mime内容编码类型, 是通用的, 但是它在传输比较大的二进制或者文本数据时效率极低.
  • multipart/form-data是当上传文件或者二进制数据和非ASCII数据使用.

网络基本模型

OSI 模型(Open System Interconnection model)是一个由国际标准化组织􏰁提出的概念模型,试图􏰁供一个使各种不同的计算机和网络在世界范围内实现互联的标准框架. 它将计算机网络体系结构划分为七层, 每层都可以􏰁供抽象良好的接口. 了解 OSI 模型有助于理解实际上互联网络的工业标准——TCP/IP 协议. OSI 模型从最底层到最高层依次是下面七层结构. 在TCP/IP的四层模型中应用层, 表示层, 会话层统称为应用层. 数据链路层和物理层统称为网络接口层. 见下图:
网络基本模型

物理层

物理层主要是利用传输介质为数据链路层提供物理连接, 实现比特流的透明传输. 规定了激活, 维持, 关闭通信端点之间的机械特性, 电气特性, 功能特性以及过程特性. 为上层协议提供了一个传输数据的物理媒体. 在这一层协议数据单元为比特(bit). 物理层的互联设备包括: 集线器, 中继器等.

数据链路层

控制网络层与物理层之间的通信, 主要作用是在不可靠的物理介质上提供可靠的传输. 数据链路层的作用主要包括: 物理地址殉职, 数据的成帧, 流量控制, 数据的检错, 重发等. 在这一层协议数据单元为帧(frame). 数据链路层的互联设备包括: 网桥, 交换机等.

网络层

网络层的主要作用是将网络地址翻译成对应的物理地址, 并决定如何将数据从发送方路由到接收方, 建立的是主机到主机的通信. 该层的作用包括: 对子网见的数据包进行路由选择, 实现拥塞控制, 网际互联等功能. 在这一层协议数据单元为数据包(packet). 网络层的互联设备包括: 路由器等.

网络层主要解决的问题有: 根据MAC地址寻址. 规定不同的信息交换方式. 根据路由算法将数据分组, 并且将信息星最合适的路径由发送端传送到接收端. 控制从源节点到目的节点间的流量.

网络层的作用是引进一套新的地址, 使得我们能够区分不同的计算机是否属于同一个子网络. 这套地址就叫做”网络地址”, 简称”网址”. 规定网络地址的协议, 叫做IP协议, 他所定义的地址就被称为IP地址.

传输层

传输层是最重要的一部分, 建立端口到端口的通信. 每个数据包都发到主机的特定端口, 所以不同的程序就能取到自己所需要的数据.

传输层的主要功能是负责将上层数据分段并提供端到端的, 可靠活着不可靠的传输. 此外, 传输层还要处理端到端的差错控制和流量控制等问题. 在传输层协议数据单元为数据段(segment). 传输层协议的代表包括: TCP, UDP, SPX等.

会话层

会话层主要是管理主机之间的会话进程, 负责建立, 管理, 终止进程之间的会话. 主要功能是建立通信链接, 保持会话过程通信链接的畅通, 利用在数据中插入校验点来同步两个时间点之间的对话. 决定通信是否被中断以及通信中断是决定从何处重新发送.

表示层

表示层是应用程序和网络之间的翻译官, 负责对上层数据或信息进行变换以保证一个主机应用层信息可以被另一个主机的应用程序理解. 表示层的数据转换包括数据的解密和加密, 压缩格式转换等.

应用层

应用层负责为操作系统或网络应用程序提供访问网络服务的接口, 规定应用程序的数据格式. 应用层提供的服务包括文件传输, 文件管理, 以及电子邮件的信息处理. 在应用层的互联设备主要包括: 网关(Gateway)等.

常见的应用层协议有: 文本传输协议(FTP), 端口号是21. 超文本传输协议(HTTP), 端口是80. 简单网络管理协议(SNMP). 域名服务协议(DNS). 网络文件系统(NFS). 远程终端协议(Telent).

完整的网络请求过程解析

这里讲的挺详细, 大概做下总结.

首先电脑要上网我们必须需要四个参数, 分别是:

  • 本机的IP地址(动态的或者静态的)
  • 子网掩码
  • 网关的IP地址
  • DNS的IP地址

我们在浏览器里面输入了一个网址www.google.com, 这意味着浏览器要向Google发送一个网页请求的数据包.

  1. 我们知道了网址但是不知道IP地址, 那么我们就需要DNS协议帮助我们将网址转换为IP地址. 于是我们想DNS服务器(DNS的IP地址, 端口是53)发送一个DNS数据包, DNS服务器告诉我们Google的IP地址, 于是我们知道了Google的IP地址.
  2. 接下来, 我们要判断Google的IP地址是不是和本机的IP地址在同一个子网络里面, 这里我们要用到子网掩码. 我们通过子网掩码分别对本机IP地址和Google的IP地址做&运算, 如果结果不同就表示不在同一个自网络. 很显然我们和Google的IP地址不在同一个自网络. 因此, 我们要向Google发送数据包, 必须通过网关转发. 也就是说, 接收方的MAC地址将是网关的MAC地址.
  3. 浏览网页用的是HTTP协议, HTTP协议包含一些请求方式, HOST, User-Agent, Accept-Encoding, Accept-Language, Accept-Charset, Cookie等信息.
  4. TCP数据包需要设置端口, Google默认是80端口, 本机随机生成一个端口号. 然后把HTTP的数据包嵌入到TCP的数据包中.
  5. TCP数据包再嵌入IP数据包, IP数据包需要设置双方的IP地址, 这个我们在前面已经解析出来了.
  6. 最后, IP数据包嵌入以太网数据包. 以太网数据包需要设置双方的MAC地址, 发送方为本机的网卡MAC地址, 接收方为网关的MAC地址(通过ARP协议得到).
  7. 以太网数据包的数据部分, 最大长度为1500字节. 因此IP数据包必须分割成若干个包. 每个包都有自己的IP标头(20字节).
  8. 经过多个网关的转发, Google的服务器(解析出来的IP地址), 收到了这若干个以太网数据包. 根据IP标头的序号, Google将四个包拼起来, 取出完整的TCP数据包, 然后读出里面的”HTTP请求”, 接着做出”HTTP响应”, 再用TCP协议发回来. 本机收到HTTP响应以后, 就可以将网页显示出来, 完成一次网络通信.