[后台开发工程师总结系列] 4..计算机网络

计算机网络 复习

七层网络模型

国际标注化组织ISO于1981年 正式推荐了一个网络体系结构–七层参考模型,也被叫做开放系统互连模型1546400647297

这七层网络模型在传输过程中还会对数据进行封装,过程如图所示

1546400692624

在ISO七层网络模型中,当一台主机需要传送用户数据时,数据先进入应用层。在应用层中,数据被加上应用层报头(AH),形成应用层协议数据单元(PDU),然后被递交到表示层。表示层不关心上层应用数据格式而是把整个数据包看成一个整体(应用层数据)进行封装,及加上表示层报头(PH)。下层分别加上自己的报头,其中数据链路层还会封装一个链尾,形成一帧数据。

当一帧数据通过物理层传输到目标主机物理层时,主机递交到数据链路层,同样经历上述相反的过程一层一层解包拿到数据。

2 五层网络模型

大学教科书一般采用了一个五层的网络模型

1 应用层 : 确定进程之间的通信性质以满足用户需求。

2 运输层:负责主机间不同进程的通信。即TCP与UDP

3 网络层:负责分组交换不同主机间的通信 IP数据报

4 数据链路层:负责将网络层的IP数据报组装成帧

5 物理层:透明的传输比特流

3 四层网络模型

不论是七层还是五层,都是学术上的概念,实际应用的网络模型是一个四层的TCP/IP模型。

1546401865049

TCP|IP 被组织成四个概念层,其中三个与ISO中模型对应。TCP、IP协议簇不包含链路层和物理层,其需要与其他协议协同工作。

  1. 网络接口层

网络接口层包括用于IP数据在已有网络介质上的传输协议。实际上TCP、IP并不定义与ISO链路物理层相对应的功能,相反它定义了ARP这样的协议,提供TCP、IP与硬件间的接口

  1. 网间层

网间层对应网络层,本层包含IP、RIP协议,负责数据包装、寻址、路由

  1. 传输层

网间层对应OSI参考模型的传输层,它提供两种端到端的通信服务。其中TCP提供可靠的数据流传输服务,UDP提供不可靠的数据报服务。

  1. 应用层

应用层对应应用层和表示层。

TCP头部

1546432241898

TCP头部里每一个字段都为管理TCP连接和控制起了重要作用

  1. 16位端口号:告知报文段来自哪里,传到哪里。进行TCP通信时,客户端通常使用临时端口号,服务器则使用知名端口号
  2. 32位序号:一次TCP通信(TCP连接建立到断开)过程中传输字节流中字节的标号,序号值被初始化为某随机值,之后该传输方向上TCP报文的序号值加上该随机值的偏移
  3. 32位确认号:用作对一方发来TCP报文段的相应。其值是TCP报文段值加1
  4. 4位头部长度 标识TCP头部有多少(4字节),这样TCP首部最大是60字节
  5. 6位标志位包含如下几项

1)URG 紧急指针是否有效

2)ACK 确认号是否有效

3)PSH 提示接收端立即从缓冲区读走数据

4)RST 表示要求重新建立连接

5) SYN 标识请求开始一个连接 (同步报文段)

6)FIN标志 表示通知对方要关闭连接了 (结束报文段)

  1. 16位窗口大小 TCP控制流量的手段,接受窗口(告诉对方TCP缓冲区还能容纳的字节数)
  2. 16位验校和 检验TCP报文段在传输过程中是否损坏
  3. 16位紧急指针 正偏移量

有几点需要注意 TCP包没有IP地址,这属于网络层、一个TCP连接需要4个元组(略)来保证是同一连接。 SequenceNubmer是包序号,来解决网络包乱序问题ACK用于确认不丢包 Window是滑动窗口用来解决流控问题

TCP状态流转

首先了解注明的三次握手与四次挥手

1546434083082

  1. 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状态

  1. TCP断开连接

TCP有个特殊的概念叫半关闭,这个概念是说TCP连接是全双工连接,因此关闭连接必须关闭两个方向上的连接。客户机给服务器发送一个FIN的TCP报文,然后服务器回一个ACK报文,并且发送一个FIN报文,之后客户端再回ACK就结束了。

在建立连接的时候,通信双方要确认最大报文长度,一般这个SYN是MTU减去IP和TCP的首部长度,对于以太网一般可以达到1460字节,当然非本地IP只有536字节,中间传输的MSS更小的话这个值更小

1546435092120

2MSL 等待状态 (TIMEWAIT)发送了最后一个ACK后,防止最后一次的数据报没有达到对方那里。这个状态保证了双方都可以正常结束,由于socket(IP和端口对)使得应用程序在这个时间(2MSL)无法使用同一个服务。这对于客户端无所谓,但是服务程序(httpd)总要用一个端口来服务。而这个时间,启动httpd会出现错误(插口被使用)。为了避免这个错误,服务器给出了平静时间的概念,虽然可以重新启动服务器,但是要平静的等待2MSL才能进行下一次连接

FIN_WAIT_2 状态 这是注明的半关闭状态,这个状态应用程序还可可以接受数据但是不能发送数据。还有一种可能是 客户端一直 FIN_WAIT_2 ,服务器一直 CLOSE_WAIT 状态 直到应用层来关闭这个状态

RST 同时打开和同时关闭,概率很小

TCP超时重传

本节讨论异常网络状况下,TCP如何控制数据保证其承诺的可靠服务

  1. 数据顺利到对端,对端顺利响应ACK
  2. 数据包中途丢失
  3. 数据包顺利达到,但是ACK报文丢失
  4. 数据报数据达到,但是对异常未响应

1546517441968

出现这些异常情况时,TCP就会超时重传。TCP每发一个报文段,就对这个报文段设一个计时器,如果确认的时间到了而没有收到确认,就会重传报文段,这被称为超时重传

利用tcpdump调试出以下信息

1546518775080

可以看出重传时间为2、4、8、6(可能是被终止了)

客户端一直没有得到ACK报文,客户端会一直重传,影响重传效率的是RTO。RTO指发送数据后,传送数据等待ACK的时间。RTO非常重要。

  1. 设长了,重发慢,没有效率
  2. 设短了,重发快,网络拥塞

如果底层传输特性已知,则重传相对简单,但是TCP体层完全异构,因此TCP必须适应时延差异。

RFC973规定了经典的自适应算法

1
2
SRTT = α * SRTT + (1 - α) * RTT
RTO = min(UBOUND, max(LBOUND,β * SRTT))

但是这个算法有个问题,主要是由于ACK传输导致的RTT多义性问题

1987年出现了一种carn算法, 忽略重传,不采样重传的RTT。

该算法规定,一但发生重传,就对现有的RTO翻倍。

当不发生重传时,才根据上式计算平均往返时间RTT和重传时间

TCP滑动窗口

TCP滑动窗口主要有两个作用 1. 提供TCP的可靠性 2. 提供TCP的流控特性 同时滑动窗口还体现了TCP面向字节流的设计

对于TCP会话的发送方,任何时候其缓存数据可以分为四类:

  1. 已经发送并受到对方的ACK
  2. 已经发送但未收到ACK
  3. 未发送但是对方允许发送
  4. 不允许发送

其中,已经发送还未收到ACK和未发送但是对方允许发送的部分称为发送窗口

1546519840167

当对方接收到ACK后续的确认字节时,便会滑动

1546519870527

对于TCP的接收方,某一时刻其有三种状态 1 已接收 2 未接受准备接受 3 未准备接受

TCP是双工的协议,会话的双方可以同时接受、发送数据。TCP会话双方都各自维护一个发送窗口和接收窗口。滑动窗口实现面向流的可靠性来源于“确认重传机制”,TCP滑动窗口的可靠性也来源于确认重传。发送窗口只有收到对方对于本段ACK的确认,才会移动左边界。前面还有字节未接受的情况下,窗口不会移动。

TCP拥塞控制

计算机中带宽、交换节点中的缓存、处理机都是网络资源。某段时间,网络需求超过了可用部分,网络性能就会变坏,这被称为拥塞。拥塞控制就是防止过多的网络流量注入到网络中。TCP拥塞控制由四个核心算法组成:慢开始、拥塞避免、快速重传和快速恢复

慢开始和拥塞避免

发送方维持一个拥塞窗口的状态变量,拥塞窗口取决于网络的拥塞程度,发送方让自己的发送窗口等于拥塞窗口

慢开始的思路就是一开始不发送大量的数据,先探测网络的拥塞程度。由小至大的增加拥塞窗口

  1. 当主机发送数据时,如果将较大的发送窗口全部注入到网络中,可能引起拥塞
  2. 可以试探一下,由小至大增大拥塞窗口的数量

(这里用报文段说明,实际上拥塞窗口以字节为单位)

慢开始从1开始指数增长,为了防止其增长过大, 设置一个门限,当其达到门限时,变为拥塞避免算法

1546521264844

拥塞避免算法是使得拥塞窗口缓慢增长,每经过一个RTT就将 拥塞窗口加一

  1. TCP连接初始化,拥塞窗口设为1
  2. 执行慢开始算法,cwind指数增长,直到cwind=ssthress时,开始拥塞避免算法
  3. 当网络拥塞时, 将ssthress设为当前的一半,cwind重新设为1 开始

1546521436409

快重传和快恢复

快重传要求接收方收到一个失序的报文段后立即发出重复确认(为使发送方尽早知道报文段未传到对方)快重传规定只有一连收到3个重复确认就立即重传对方尚未收到的报文段,而不必继续等待。

1546521564754

快重传还配合有快恢复,主要思想包括

  1. 一旦收到三个重复确认,执行乘法减小,ssthresh门限减半,但是并不执行慢开始
  2. cwind设为ssthresh, 然后执行拥塞避免算法

1546521705851

整体上,TCP拥塞窗口的原则是加法增大、乘法减小。可以看出TCP较好的保证了流之间的公平性,一旦丢包就减半退让。

为什么TCP进行三次握手?

本质上TCP协议的三次握手需要解决这样一个问题:在不可靠的信道上(IP数据报尽最大努力交付)完成可靠的数据传输。而全双工的连接建立需要双方的连接请求和确认,这样最少需要使用三次握手才能建立连接

至于为什么三次是最少,客户端服务器二者最少都需要向对方发送一个同步报文(SYN),但是如果只有这两次握手,服务器就只能接受连接,无法确认连接;设想如果服务器接受一个SYN报文就建立连接,那客户端因为阻塞原因重发了N个SYN同步报文 ,服务器每接受到一个就需要建立一次连接,这是不堪设想的。所以只有当服务器接收到客户端第二次的ack确认报文后才会建立连接

为什么需要四次握手

TCP是全双工的协议,通信中双方都需要知道对方的存在,而在结束时,双方也同时需要发送断开与确认对方的断开信息。当主机1发送FIN希望断开连接时,主机1已经没有要发送的数据了,但是其还是有可能接受主机2发送的数据,此时单方面的连接断开了,这时处于半连接状态。而只有主机2也向主机1发送断开请求并确认,双方才完全的断开。

TCP的半打开状态

如果TCP连接中一方已经关闭或异常终止另一方还不知道,这样的连接称为半打开状态。任何一端的主机都可能检测到这一情况,如果双方没有在半打开的连接上传输数据,双方就无法获悉异常。

半打开的一个常见原因是一方程序的非正常结束(断电、断网)如果A已经没有向B发送的数据,则B永远无法获悉A是否已经消失了。而当一方获取到异常的数据连接后(比如重启)直接进行复位(RST)处理

同时打开与同时关闭

两个程序的同时打开与同时关闭是有可能的,例如A:port1 向B:port2 发送SYN同步信息的同时,B:port2 也向A:port1发送了一个SYN同步信息,此时双发收到对方的SYN后各自向对方回一个ack表示,确认,连接就正常建立了,这样一个打开需要四个报文段。

而同时关闭同理,也是双方同时发送FIN报文段,双方在ack确认,这样还是使用4个报文段双方完成了连接的关闭只不过此时双方都跳过了FINWAIT2阶段

大量的Time-wait怎么办

Time-wait 状态不能消除 只能快速的回收或重用。

reuse: 处于timewait状态的字段可以被后续连接重用

recycle 开启服务器对于time-wait的快速回收

time-out 修改系统默认的TIMEOUT时间

UDP

UDP简介

UDP只做传输协议能做的最少工作,只在IP数据服务上增加了两个最基本的服务:复用和分用 以及差错检测

UDP首部只有8个字节,分为四个字段:源端口、目的端口、UDP长度、UDP校验和

  • UDP无需建立连接
  • UDP不维护连接状态
  • UDP分组首部开销小
  • UDP没有拥塞控制,适合容许一些数据丢失但是不允许有较大时延的应用

TCP和UDP的区别

TCP和UDP协议特性的区别,主要从连接性、可靠性、有序性、拥塞控制、传输速度、头部大小来讲

  1. TCP面向连接,UDP无连接。TCP3次握手建立连接,UDP发送前不需要建立连接
  2. TCP可靠,UDP不可靠,TCP丢包有确认重传机制,UDP不会
  3. TCP有序,会对报文进行重排;而UDP无序,后发送信息可能先到达
  4. TCP必须进行数据验校,UDP的校验可选
  5. TCP有流量控制(滑动窗口)和拥塞控制,UDP没有
  6. TCP传输慢,UDP传输快,因为TCP要建立连接、保证可靠有序,还有流量、拥塞控制
  7. TCP包头较大(20字节)UDP较小(8字节)
  8. 基于TCP协议:HTTP/HTTPS、Telnet、FTP、SMTP 基于UDP的协议 DHCP、DNS、SNMP、TFTP、BOOTP

TCP 网络编程API

网络通信概要

网络进程如何通信?首要解决如何标识一个进程,本地可以用PID来解决,而网络中用IP+端口号来唯一标识一个进程。这样利用(IP、端口号、PID)可以唯一标识一个网络中的进程。

socket起源于UNINX,UNINX哲学之一就是一切皆文件,有一个打开、读写、关闭的模式来操作。socket就是该模式的一个实现,socket就是一种特殊的文件。

使用TCP/IP的协议应用程序采用应用编程接口 UNINX BSD关键字来实现网络进程通信,目前几乎所有的应用程序都是使用socket,网络通信无处不在。其基本模式包括:

1546948869237

1551422937287

  1. 服务器根据地址类型(IPV4、IPV6)创建socket
  2. 服务器为socket绑定IP地址和端口号
  3. 服务器socket监听端口号请求,随时准备接受客户端的连接,这时候服务器socket并未打开
  4. 客户端创建socket
  5. 客户端打开socket,根据服务器IP和端口号试图连接服务器socket
  6. 服务器socket接收到客户端socket请求,被动打开开始接受客户端请求,直到客户端返回连接信息。这时候socket进入阻塞状态
  7. 客户端连接成功,向服务器发送连接状态信息
  8. 服务器accept方法返回,连接成功。
  9. 服务器向socket写入信息
  10. 服务器读取信息
  11. 客户端关闭
  12. 服务器关闭

仔细研究会发现,服务器和客户端连接的部分就是三次握手。

网络编程API

1. socket函数

1
int socket(int domain, int type, int protocol)

socket 函数对应于普通文件的打开操作,普通文件返回一个描述字,而socket创建一个socket描述符,它唯一标识一个socket。

当调用socket时, 返回的描述他存在与协议族空间中,但是没有具体地址,如果想要赋予地址需要使用bind()函数。如果不绑定,系统会在使用时随机生成一个。

1551422541401

2. bind函数

bind() 函数把一个地址族中特定的地址赋给socket

1
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

1551422630112

3. listen 和 connect 函数

这两个函数相对应,服务器调用listen函数来监听端口,而客户端调用connect函数去发出连接请求

1
2
int listen(int sockfd, int backlog)
int connect(int sockfd, const struct sockaddr *addr, docklen_t addrlen)

4. accept 函数

TCP 服务器依次调用socket()、bind()、listen()之后,就会监指定的socket地址了。而TCP客户端调用socket()、connect()之后就会向TCP服务器发送一个连接请求。TCP连接接收到这个请求之后,就会调用accept()函数接受请求,这样连接就建立了。

1
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)

注意:accept第一个参数为socket描述字,服务器调用socket生成的,称为监听socket关键字;而accept返回的是已连接socket关键字。一个服务器通常只创建一个监听socket描述字,这个描述子在服务器生命周期一直存在。内核为每个服务器接受的客户创建一个已连接socket关键字,当完成服务后就会关闭。

5. read 和 write 函数

read()函数从fd中读取内容,write写入内容。成功时返回字节数

6. close函数

1
int close(int fd);

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

#define MAXLINE 4096

int main(int argc, char** argv){
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[4096];
int n;

if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
return 0;
}

memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(6666);

if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
printf("bind socket error: %s(errno: %d)\n", strerror(errno),errno);
return 0;
};
if(listen(listenfd, 10)==-1){
printf("listen error: %s(errno: %d)\n", strerror(errno),errno);
return 0;
}

printf("=====waiting for client's request=====\n");
while(1){
if(connfd = accept(listenfd, (struct sockaddr*)NULL, NULL) == -1){
printf("accept socket error: %s(errno: %d)\n", strerror(errno),errno);
continue;
}
n = recv(connfd, buff, MAXLINE, 0);
buff[n] = '\0';
printf("recv msg from client: %s\n", buff);
close(connfd);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define MAXLINE 4096

int main(int argc, char** argv){
int sockfd, n;
struct sockaddr_in servaddr;
char recvline[4096], sendline[4096];

if(argc != 2){
printf("usage: ./client <ip address>\n");
return 0;
}

if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
return 0;
}

memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(6666);

if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){
printf("inet_pton error for %s\n", argv[1]);
return 0;
}

if(connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
printf("connect error: %s(errno: %d)\n", strerror(errno),errno);
return 0;
}

printf("send msg to server: \n");
fgets(sendline, 4096, stdin);
if(send(sockfd, sendline, strlen(sendline),0) < 0){
printf("send msg error: %s(errno: %d)\n", strerror(errno),errno);
return 0;
}
close(sockfd);
return 0;
}

1551422832133

TCP 协议选项

1. SO_REUSEADDR

一般来说一个端口释放后,大约两分钟才能再次使用(处于TIME_WAIT状态),而使用该选项可以使端口被释放后立即被使用(处于TIME_WAIT状态的端口)。

2. TCP_NODELAY/TCP_CHORK

网络拥塞领域,有一个非常著名的算法叫做Nagle算法, 这是以其发明人的名字命名的。John Nagle 首次用该算法解决福特公司的网络拥塞问题。该问题具体描述是:如果应用程序每次产生1Byte的数据,而这个1Byte的数据又以数据报的形式发送给网络服务器,那么很容易使得网络过载。所以传送1Byte的包却要花费40Byte的包头(IP20字节、TCP20字节)这种有效载荷利用低下的情况被称为愚蠢窗后症候群。

针对这问题,Nagle算法改进:如果发送少量字符包(小于MSS的包被称为小包,大于MSS的包被称为大包)发送端只会发送第一个小包,将后面的小包缓存起来。直到接收到前一个数据报的ACK为止,或当前字符较紧急,积攒了较多的数据。

TCP中Nagle算法默认启用,但是不使用任何情况。而TCP_NODELAY/TCP_CHORK字段控制了包的Nagle化。例如TCP_NODELAY便是直接把包发出去,这样用户体验会更好。

3. SO_LINGER

linger是延缓的意思,这里的延缓指close操作。默认close立即返回,但是当缓冲区还有部分数据时,系统会尝试将数据发送给对方。

4. TCP_DEAFER_ACCPET

实际上是接收到第一个数据包后,才会创建连接. 常被用来防御空连接攻击。

5.SO_KEEPALIVE

SO_KEEPALIVE用来检测对方主机是否崩溃,避免服务器永远阻塞于TCP连接的输入

设置该选项后,如果2h内任何一方没有数据交换,TCP就会自动向对方发送一个保持存活探测,

  1. 对方接受正常,以ACK回应
  2. 对方已崩溃且重新启动,以RST响应,套接口错误置为restart,套接口本身被关闭
  3. 对方无响应,再次发送8个探测分节,11min15s 后无响应就放弃。

网络字节序与主机序

关于字节序再次讨论。不同的CPU有不同的字节序类型,这些字节序是整数在内存中的保存顺序,称为主机序。最常见的两种 1 小端, 低序的字节存在起始位置 2 大端 , 高字节的存在起始位置

小端法 地址低位存在值的低位,高位存值的高位。这种方式符合人的思维方式。

大端法 地址低位存高位的值。它很直观不需要考虑对应关系,只需要内存地址写出去即可

1547125473936

为什么要注意字节问题呢?C++编译平台的储存是由编译平台的CPU确定的,而JAVA编写的程序唯一采用大端法。所有网络协议都是大端法。其也被称为网络字节序。

封包和解包

TCP是一个流协议,所谓流就行没有界限的一串数据。但是通信程序是需要独立的数据包发送。

设想这样几种情况

  1. 先接收到data1 后接到 data2
  2. 先接收到data1 的部分数据,后接到data1的余下部分与 data2全部
  3. 先接收到了data1全部数据和部分data2数据,后接受到data余下数据
  4. 一次接受data1和data2全部数据

1是理想情况 而2、3、4即使粘包的情况,这时就需要拆包将受到的数据拆成独立的数据包。这种情况可能有这么几种原因。

1 由Nagle算法造成的粘包

2 接收端不及时接受造成的粘包

具体的解决办法便是封包和拆包。封包是给一段数据加上包头,这样数据就分为包头、包体两部分。包头包含了一个结构体成员变量表示了包体的长度,这样另一方便可以通过这个长度进行拆包;

HTTP 复习

协议是计算机通信网络中两台计算机之间进行通信所必须共同遵守的规定或规则。HTTP协议是一种详细规定了浏览器和万维网服务器之间相互通信的规则,通过因特网传送万维网文档和数据的协议。HTTP协议可以使浏览器更加高效的运行,使网络的传输效率更高。它不仅保证计算机快速正确的传输超文本文档,还确定哪一部分先显示。

HTTP由于其灵活简单快速的特点,应用非常广泛。浏览器网页是HTTP主要的应用,但其不只是用于网页浏览。只要通信的双方都使用他就可以通信。比如QQ软件就用到了HTTP协议。

HTTP工作流程

在网路的七层模型中,HTTP在应用层也就是传输层以上,其基于TCP协议。

HTTP的默认端口号为80, 而HTTPS的默认端口号是443

HTTP是基于传输层的TCP协议,而TCP是一个端到端面向连接的协议。所谓的端到端可以理解为进程到进程的通信,所以HTTP在开始传输之前,首先要建立TCP连接,而TCP连接的过程需要三次握手。在TCP的三次握手后,建立了TCP连接。此时HTTP就可以进行传输了。一个HTTP操作称为一个事务,可以分为以下几步:

1551424535395

  1. 首先客户机与服务器需要建立连接。只要单机某个超级链接,HTTP工作即开始
  2. 建立连接后,客户机发送一个请求给服务器,请求的格式为:统一资源标识符(URL),协议版本号,后面是MIME信息,包括(请求修饰符、客户机信息和可能的内容)
  3. 服务器接到请求后,给与一个相应的响应信息,其格式为一个状态行、包括信息的协议版本号、一个成功或错误的状态码、后面是MIME信息(服务器信息、实体信息、可能的内容)
  4. 客户端接受服务器返回的信息通过浏览器返回在用户显示屏上,然后客户机与服务器断开连接。

如果上述几步中某一步出现错误,那么产生的错误信息返回客户端,由显示屏输出。HTTP协议永远都是客户端发起请求,服务器响应。

HTTP 协议结构

HTTP 协议无论是请求报文还是回应报文,都包含以下四个部分。

  1. 报文头: GET http://www.baidu.com HTTP/1.1 只占一行
  2. 请求头 Accept-Language: en
  3. 空行
  4. 可选的消息体

HTTP协议是基于行的协议,每一行以\r\n 为分隔符。报文头通常表明报文的类型,且报文头只占一行;请求头附带一些特殊信息,每一个请求头占一行 name:value

HTTP请求方法

HTTP/1.1 协议中定了 9 种方法来表明Request-URL 指定资源的不同的操作方式

  1. OPTIONS 返回服务器针对特定资源的HTTP请求方法
  2. HEAD 向服务器索要与GET请求一致的相应,而响应体不会被返回
  3. GET 向特定资源发出请求(GET可能会被爬虫随意访问)
  4. POST 向指定资源提交数据处理的请求(提交表单、上传文件)数据被包含在请求体中。POST可能会导致新建资源或已有资源的修改
  5. PUT 向指定资源上传最新内容
  6. DELETE 请求服务器删除 REQUEST_URL 所标识的资源
  7. TRACE 回显服务器收到的请求
  8. CONNECT 预留给连接方式改为管道的代理服务器
  9. PATCH 局部修改某一资源

当请求的资源不支持请求方法时, 服务器返回405(Method Not Allowed)服务器不认识或不支持方法时, 返回501(Not Implemented)

常见的请求头

Host:(发送请求,请求头是必须的)主要用于执行被请求资源的Internet主机和端口号,它从HTTP的URL中提取出来,

Connection: 它的值通常有两个,keep-alive 和close。 HTTP 是一个请求响应模式的典型范例,即客户端向服务器发送一个请求信息,服务器来响应这个信息。在之前的HTTP版本中,每个请求被创建一个新的到服务器端的连接,在这个连接上发送请求,然后接受请求。keep-alive被用来解决效率了低的问题。keep-alive 使客户端到服务端的连接持续有效,当出现了后续请求时, keep-alive避免了建立或者重新建立。对于市面上大部分服务器keep-alive都被支持,但是对于负担较重的网站,保留的客户端会影响性能。

Accept:浏览器可以接受的MIME类型。 Accept:text/html, 如果不能接受将返回406错误

Cache-control: 指定请求和响应遵循缓存机制。缓存指令是单向的,且独立。

Accept-Encoding:浏览器生命可接受的编码方法,通常说明是否支持压缩。

Accept-Language:浏览器声明接受语言

Accept-Charset:浏览器可以接受的字符集,默认任何字符集都可以接收

User-agent:用于告诉HTTP服务器,客户端使用操作系统浏览器的名称和版本。

HTTP回应报文

第一行是报头,第一个字段表明HTTP协议版本们可以直接以请求报文为准;第二个字段是status code, 也就是返回码,相当于请求结果

HTTPS

在网络上,两个实体间的通信容易被窃听。

当你学习网络安全后、你发现网络再也不安全了

初级的防御手段,是双方都约定一个加密的算法,把加密后的数据进行传输,收到数据方再进行解密。这里涉及一个概念,对称加密和非对称加密

  1. 对称加密:对称加密是指加密秘钥和解密秘钥是一样的,通常有AES和TEA加密算法,它的特点是计算量小,有又一定的破解门槛。
  2. 非对称加密:加密的秘钥和解密的秘钥不一样,秘钥成对出现。加密解密使用不同的秘钥(公钥加密需要私钥解密,私钥加密需要公钥加密)它的特点是计算量大,常用的有RSA、ECC等算法。基于性能考虑,一般使用非对称加密得到秘钥, 再用对称秘钥进行加密。

HTTP协议可以轻松抓包并获取协议,是不安全的协议。而HTTPS是安全为目标的HTTP通道,简单来说是HTTP的安全版。

1551426186821

1.client向server发送请求https://baidu.com,然后连接到server的443端口。

2.服务端必须要有一套数字证书,可以自己制作,也可以向组织申请。区别就是自己颁发的证书需要客户端验证通过,才可以继续访问,而使用受信任的公司申请的证书则不会弹出提示页面,这套证书其实就是一对公钥和私钥。

3.送证书
这个证书其实就是公钥,只是包含了很多信息,如证书的颁发机构,过期时间、服务端的公钥,第三方证书认证机构(CA)的签名,服务端的域名信息等内容。

4.客户端解析证书
这部分工作是由客户端的TLS来完成的,首先会验证公钥是否有效,比如颁发机构,过期时间等等,如果发现异常,则会弹出一个警告框,提示证书存在问题。如果证书没有问题,那么就生成一个随即值(秘钥)。然后用证书对该随机值进行加密。

5.传送加密信息
这部分传送的是用证书加密后的秘钥,目的就是让服务端得到这个秘钥,以后客户端和服务端的通信就可以通过这个随机值来进行加密解密了。

6.服务段加密信息
服务端用私钥解密秘密秘钥,得到了客户端传过来的私钥,然后把内容通过该值进行对称加密。

7.传输加密后的信息
这部分信息是服务端用私钥加密后的信息,可以在客户端被还原。

8.客户端解密信息

客户端用之前生成的私钥解密服务端传过来的信息,于是获取了解密后的内容。

HTTP和HTTPS有以下区别

  1. HTTPS协议需要CA申请证书,需要交费
  2. HTTP是超文本传输协议,明文传输, HTTPS是ssl加密传输
  3. 80 和443
  4. HTTP连接很简单,无状态, HTTPS经过SSL+HTTP协议构建的,加密传输、身份认证。
  5. HTTPS耗性能,安全性要求低用HTTP

CGI

CGI 是HTTP 中重要的技术之一,有着不可替代的作用。CGI是一个web服务器的标注接口。通过CGI接口Web服务器就能获取客户端提交的信息转交给服务器端的CGI程序处理,最后结果返回给客户端。

1550404398950

服务器和客户端之间的通信,是浏览器和服务端web服务器的HTTP通信,所以只需知道浏览器执行哪个CGI程序即可。

GET与POST的区别

作用

GET用于获取资源,POST用于传输实体主体

参数

GET和POST都能使用额外的参数,但是GET参数以查询字符串的形式出现在URL中,如http://127.0.0.1/Test/login.action?name=admin&password=admin 这个过程用户可见。而POST的参数存储在实体主体中。通过HTTP的POST机制。POST参数可以通过一些抓包工具(Fiddler)查看

因为URL只支持ASCLL码,因此GET参数中文会被编码,空格被编码为%20

安全

安全的HTTP方法不会改变服务器状态,可以说他是只读的

GET方法安全的,POST不是,POST方法传输主体内容,这个内容可能是某个表单数据,服务器可能把其存入数据库中,这样状态就发生了改变

幂等性

幂等性的HTTP方法,同样的请求被执行一次和连续执行多次的效果是一样的,服务器状态也是一样的。

所有安全的方法是幂等的,GET、HEAD、PUT、DELETE方法都是幂等的

而POST方法不是幂等的

XMLHTTPRequest

XMLHTTP Request是一个API,它为客户端和服务器之间传输数据的功能。他提供了一个URL来获取数据的简单方式,而且不会使整个页面刷新。这使得网页只更新一部分不会打扰到用户,在AJAX中被大量使用

xmlhttprequest中POST会先发送header再发送data(当然也和浏览器的做法相关)

而get方法会一起发送

其他

Get传输数据量小,因为受URL的限制,但是效率高;POST可以传输大量数据,文件只能通过POST传递

Get方式只支持ASCII字符,向服务器传输的中文字符可能会乱码。POST支持标准字符集,可以正确的传递中文字符

HTTP 首部 Request

Header 解释 某度首页示例
Accept 客户端能够接受的内容类型 text/html
Accept-Encoding 浏览器可以支持的web服务器返回内容压缩编码类型 gzip
Accept-Language 浏览器可以接受的语言 zh-CN
Cache-Control 指定请求和相应遵循的缓存机制 max-age=0
Connection 表示是否需要持久连接 keep-alive
Cookie HTTP请求发送时,会把保存在请求域名下的所有cookie值一起发送给web服务器 键值对
Host 请求服务器的域名和端口号 www.baidu.com
Upgrade-Insecure-Requests 浏览器可以处理HTTPS协议 1
User-Agent 发出请求的用户信息 Mozilla/5.0

HTTP 首部 Response

Header 解释 某度首页示例
Cache-Control 告诉所有的缓存机制是否可以缓存及缓存哪种类型 private
Connection 是否保持持久连接 keep-alive
Content-Encoding 返回内容压缩编码类型 gzip
Content-type 返回内容的MIME类型 text/html charset=utf-8
Date 原始服务器消息发出时间 Wed 03 Oct2018 12:04:45 GMT
Expires 响应过期的时间 Wed 03 Oct2018 12:04:45 GMT
Server Web服务器软件名称 BWS1.1
Set-Cookie 设置浏览器缓存 BDSVRTM=114; path=/
Transfer-Encoding 文件传输编码 chunked

HTTP状态码

HTTP中状态码大致分为五大类:

100-199 信息性状态码

  • 100 continue: 收到了请求的初始部分,请客户端继续

200-299 成功状态码

  • 200 OK:请求被正常处理
  • 204 No Content: 请求被接受,但是响应报文只有首部和状态行,没有实体部分
  • 206 Partial Content: 客户端只请求了一部分的资源,服务器只针对请求的部分资源进行返回

300-399 重定向状态码

  • 301 Moved Permanently: 永久重定向
  • 302 Found: 临时重定向,资源被临时移动了
  • 303 See Other: 表示用户请求的资源代表着另一个URI,应该使用GET去获取资源
  • 304 Not Modified: 当客户端发送的请求附带条件时,服务器找到资源但未符合请求
  • 307 Temporary Redirect: 同302,临时重定向

400-499 客户端错误状态码

  • 400 Bad Request: 告知客户端它发送了一个错误的请求
  • 401 Unauthorized: 请求需进行认证
  • 403 Forbidden: 请求被服务器拒绝了
  • 404 Not Found:服务器无法找到对应资源
  • 405 Method Not Allowed:请求中带有不支持的方法

500-599 服务器错误状态码

  • 500 Internet Server Error: 服务器内部错误
  • 502 Bad GateWay: 代理或网关服务器从下一条链路收到了伪响应
  • 503 Server Unavailable: 服务器正忙
  • 504 GateWay Timeout: 一个代理网关等待另一服务器时超时了

持续性连接和非持续性连接

1. 定义

非持续连接:每个请求、相应都经一个而单独的TCP连接发送

持续连接:所有的请求相应通过相同的TCP连接发送

2. 区别

如果采用非持续连接,打开包含一个HTML文件和10个内联对象网页,HTTP需要建立11次TCP连接才能把文件从服务器传到客户机。而如果采用持续连接,HTTP建立一次TCP就把文件从服务器传到客户机

  • 每次TCP连接必需建立和断开(通过三次握手建立、四次挥手断开),这都需要占用CPU资源,这样占用客户机和服务器的CPU时间大大减少
  • 每次连接,客户端和服务器都必须分配发送和接收缓存,这意味着影响服务器和客户机的资源,着同样要占用CPU时间
  • 对于大量对象组成的文件,TCP低速启动算法会限制服务机向客户机传送对象的速度。使用HTTP/1.1后,大多数对象以最大速率传输

3. HTTP/1.0 + keep-alive

HTTP/1.1 中默认保持持久连接,但是1.0版本的HTTP需要设置 connection:keep-live

connection:keep-live 是HTTP1.0 浏览器和服务器的实验性扩展

由于HTTP是无状态协议,为了保持客户端与服务器的一些关系,便有了cookie和session

cookie是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器。网络服务器用HTTP头想客户端发送cookies, 在客户终端,浏览器解析这些cookies 并将它们保存为一个本地文件,它们在会在下一次对服务器的请求时附上这些cookies。

内容

过期时间

会话cookie:若不设置过期时间,则表示这个cookie的生命周期为浏览器会话期间,若关闭浏览器,cookie就会消失。这种生命周期的cookie被称为会话cookie

持久cookie:若设置了过期时间,浏览器会把cookie存储到硬盘上(当然用户可以选择拒绝),关闭后再打开这些cookie仍然有效

2.session

session机制是一种服务端的机制,服务器利用一种类似于散列表的结构来保存信息

当程序需要为某个客户端的请求创建session时,服务器检查这个客户端是否包含了一个session标志,称为session_id, 如果检测到说明该客户曾创建过ID,服务器会把这个ID检索出来使用(或者未检测到新建一个),session_id 既不会重复也不容易被找到仿造。

1541333885553

session_id的储存
  1. 保存这个session_id可以采用cookie,这样交互过程中浏览器可以把这个标志返回给服务器。一般该变量名与session有关,如github的session ID即名为user_session
  2. 由于cookie可以被认为的禁止,必须有其他机制保证session_id传回服务器,经常使用的一种方法是URL重写,即直接把session_ID附在URL后面。作为路径的附加信息或查询字符
  3. 另一种技术是表单隐藏字段,服务器自动修改表单加入一个隐藏字段,便于传回session_id

3. cookie与session的区别

存取方式不同

cookie只能保存ASCII字符,Unicode及二进制数据需要编码,cookie不能直接存取java对象,存储略微复杂的信息较为艰难。而session中能够存取任何类型的数据,包括不限于string、integer、list、map,而且可以直接存取java的类对象,十分方便

隐私策略不同

cookie存储在客户端阅读器中,对客户端可见,客户端可以窥探甚至是修改cookie内容。而session存储在服务器上对用户透明,不存在泄露风险。cookie可以像googlebaidu一样将敏感信息加密后保存,在服务器上进行解密。

有效时间不同

由于session依赖于session_id的cookie,而session_id过期时间默许为-1,关闭浏览器即消失。而cookie可以设置长期的保存

服务器压力不同

由于不同的储存方式,储存在客户点的cookie不会给服务器造成压力,而session由于存在服务器上,对服务器压力较大

浏览器支持不同

cookie是需要客户端浏览器支持的,假如客户端禁用了cookie或者不支持cookie,则会话跟踪会失效。

假如客户端不支持cookie,就需要运用session及url地址重写。需要注意的是一切用到session的程序url都需要进行重写,否则session会话还是会失效

跨域支持不同

cookie支持跨域名访问,一切以相同后缀的域名均可以访问该cookie,跨域名cookie被广泛应用

session仅在当前域名有效

一个web页面的请求过程

1.DHCP配置主机信息(找本机IP)

  • 主机生成一个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地址和子网掩码,再经过层层封装到MAC帧中
  • 该帧的目的地址是主机的mac地址,主机收到该帧后分解得DHCP报文,之后配置IP地址,子网掩码、DNS服务器IP地址,安装默认网关

2.ARP解析网关MAC地址(找网关MAC地址)

  • 主机通过浏览器生成一个TCP套接字,为了发送HTTP请求,需要知道网站对应的IP地址
  • 生成一个DNS查询报文,端口53(DNS服务器)
  • DNS查询报文放入目的地址为DNS服务器IP地址的IP数据报中
  • IP数据报放入一个以太网帧中,发送至网关路由器
  • DHCP过程只知道网关的IP地址,为了获取网关的MAC地址,需要用ARP协议
  • 主机生成一个目的地址为网关路由器IP的ARP查询报文,放入一个广播帧中,并发送这个以太网帧,交换机将其发送给所有的连接设备
  • 网关接收到该帧后,分解得到ARP报文,发现IP地址与自己想匹配,发送一个ACK报文回应自己的MAC地址

3.DNS解析域名(找服务器IP)

  • 知道了DNS的MAC地址后就可以继续DNS解析过程
  • 网关接收到DNS查询报文后,抽出IP数据报,并根据该表选择该转发的路由器
  • 路由器根据内部网关协议(RIP、OSPF)和外部网关协议(BGP)配置路由器到DNS的路由表项
  • 之前的DNS报文到DNS服务器后,照常依次抽出报文,在DNS库中查找解析域名
  • 找到DNS记录后发送DNS回答报文,然后将其放入UDP报文段、IP数据报,通过路由器反转发回网关路由器,经过交换机到主机

4. HTTP请求页面

  • 有了HTTP服务器的IP地址后,主机便可以生成TCP套接字,向web服务器发送HTTP get报文
  • 建立HTTP连接前需要进行TCP连接,进行三次握手,过程略
  • 建立连接后发送HTTP的GET报文,交付给HTTP服务器
  • HTTP服务器从TCP中读出报文,生成HTTP相应报文,将web页面放入HTTP报文主体中发挥主机
  • 浏览器收到HTTP相应报文后抽取WEB页面内容进行渲染,显示web页面

1551437344892

1 DNS解析

什么是DNS解析?当用户输入一个网址并按下回车键的时候,浏览器得到了一个域名。而在实际通信过程中,我们需要的是一个IP地址。因此我们需要先把域名转换成相应的IP地址,这个过程称作DNS解析。

1) 浏览器首先搜索浏览器自身缓存的DNS记录。

2) 如果浏览器缓存中没有找到需要的记录或记录已经过期,则搜索hosts文件和操作系统缓存。

3) 如果在hosts文件和操作系统缓存中没有找到需要的记录或记录已经过期,则向域名解析服务器发送解析请求。

4) 如果域名解析服务器也没有该域名的记录,则开始递归+迭代解析。

5) 获取域名对应的IP后,一步步向上返回,直到返回给浏览器。

至此,浏览器就得到了url的IP地址。

2 发起TCP请求

建立TCP连接的过程就是三次握手过程。
这里简述一下三次握手的过程:

  1. 客户端向服务器端发送连接请求的报文;
  2. 服务器端收到请求后,同意建立连接,向客户端发送确认报文;
  3. 客户端收到服务器端的确认报文后,再次向服务器端发出报文,确认已收到确认报文。

至此,浏览器与服务器已经建立了TCP连接,开始进行通信。

3 建立TCP连接后,浏览器向服务器发送http请求

例如:浏览器发出取文件指令GET

4 负载均衡

什么是负载均衡?当一台服务器无法支持大量的用户访问时,将用户分摊到两个或多个服务器上的方法叫负载均衡。
1) 一般,如果我们的平台配备了负载均衡的话,前一步DNS解析获得的IP地址应该是我们Nginx负载均衡服务器的IP地址。所以,我们的浏览器将我们的网页请求发送到了Nginx负载均衡服务器上。
2) Nginx根据我们设定的分配算法和规则,选择一台后端的真实Web服务器,与之建立TCP连接、并转发我们浏览器发出去的网页请求。
3) Web服务器收到请求,产生响应,并将网页发送给Nginx负载均衡服务器。
4) Nginx负载均衡服务器将网页传递给filters链处理,之后发回给我们的浏览器。

5 服务器响应http请求,将请求的指定资源发送给浏览器

6 浏览器释放TCP连接

建立TCP连接的过程就是四次挥手过程。

这里简述一下四次挥手过程:
1.浏览器向服务器发送释放连接报文;
2.服务器收到释放报文后,发出确认报文,然后将服务器上未传送完的数据发送完;
3.服务器数据传输完成后,向浏览器发送释放连接请求;
4.浏览器收到报文后,发出确认,然后等待一段时间后,释放TCP连接。

7 浏览器渲染

1) 浏览器根据页面内容,生成DOM Tree。根据CSS内容,生成CSS Rule Tree(规则树)。调用JS执行引擎执行JS代码。

2) 根据DOM Tree和CSS Rule Tree生成Render Tree(呈现树)
3) 根据Render Tree渲染网页