计算机网络历史
芯片技术的发展——集成电路使计算机体积更小、
计算机网络理论的发展——封包交换算法等理论、
材料的发展——双绞线、光纤等、
操作系统技术的发展——分时操作系统等、
互联网的发展——互联网传输协议、互联网应用程序等
无线技术的发展:
OSI 7层协议
OSI模型指的是Open System Interconnection Reference Model,即开放式系统互联模型。它是世界上第一个试图在世界范围内规范网络标准的框架。
- 应用层(Application Layer)
应用层位于OSI模型最上方,只关心业务逻辑,不关心数据传输。
- 表示层(Presentation Layer)
负责协商用于传输的数据格式,并转换数据格式
- 会话层(Session Layer)
负责管理两个连网实体件的连接,其功能主要是建立连接,维持通信,释放连接
- 传输层(Transport Layer)
负责将一个数据从一个实体(服务或应用)传输到另一个实体,但不负责数据传输的方式。其主要作用:
- 数据分分隔重组:将数据拆分后按顺序重组
- 纠错:在数据过程中出现问题后采取方式进行纠正
- 管理连接:处理数据的频繁交换
- 流量控制:控制传输数据的速度
- 端口寻址:标明参与传输的实体的端口号
- 网络层(Network Layer)
把一个封包从一个IP地址传输到另一个IP地址。通过路由算法,把一个封包从一个节点传输到下一个节点
- 数据链路层(Data Link Layer)
- 确保两个临近设备间数据的传输,并隐藏底层实现
- 帧同步,两个设备之间传输是协商传输速率
- 数据纠错
- 物理层(Physical Layer)
互联网协议群
OSI协议过于臃肿,无实际可行方案,于是在此基础上提出了TCP/IP协议,
TCP/IP协议群要解决的5个问题:
- 报文拆分
- 数据量大,网络底层不支持一次性传输,需要把数据拆分层更小的数据块
- 通过数据拆分,可以复用传输路径
- 增加协议头
- 数据拆分成更小的数据包进行传输,在接收时需要对数据包进行重组,因此需要石油头部来添加一些描述字段,保证数据的正确重组。
- 数据在相邻设备间数据传递
- 数据传输需要在跨网络边界
- 路由和寻址
- 路由算法:通过算法,从一个节点到达另一个节点
- 数据重组
TCP协议
TCP全名是(Transport Control Protocol),是一个可以提供可靠的、支持全双工、连接导向的协议,因此在客户端和服务端之间传输数据的时候,是必须先建立一个连接的。
- TCP建立的连接,是一个虚拟抽象的概念,也被称为会话(Session),确保两个通信程序都在线,能够加快响应请求速度,通信更稳定安全,但同时也消耗更多资源。
- 全双工是指客户端和服务端任何时刻都能够双向发送数据。
- 可靠性是指数据保证无损传输(数据的拆分、纠错、重组)
TCP建立连接3次握手
TCP建立连接过程:
- 客户端发送了一个TCP数据包,主动发起连接,设置SYN=1标志位,表示发起连接,想要传输数据;客户端初始化了一个数据包的序列号seq=x ,一同发送给对方
- 服务端收到对方的连接的请求,回复对方的确认包,设置回复的标志位ACK=1,表示确认;同时设置ack=x+1 ,表示期待对方下次发送的数据包的序列号为x+1;因为TCP是全双工协议,因此服务端也会设置标志位SYN=1,同时发送seq=y
- 客户端收到对方的确认包后,并回复对象确认,通用设置标志位ACK=1,表示回复确认;同时设置ack=y+1,表示期待对方下一次传递的数据包的序列号为y+1;同时自身的序列号为x+1。当三次握手结束后,双方都可以确认对方都可以正确的收发数据,客户端可以正常的接受tcp数据包,自身还可以正常发送数据包;
建立连接三次握手的原因:
- 确认客户端、服务端都有收发数据的能力。(如果无最后一次客户端发送的ack,服务端无法确认客户端可以接收数据)
- 保证数据传输的序号,保证数据传输的正确性。(如果无最后一次客户端发送的ack,服务端无法确认客户端收到自己的初始序号;互联网是不稳的的,所以建立连接必须确认双方收到了对方初始序列号)
避免历史失效连接请求乱序问题。(如果无最后一次客户端发送的ack,如果有一个失效的老连接,因为网络问题延迟到达服务器,此时建立一个老的ESTABLISHED连接,这是错误的)
TCP释放连接4次挥手
TCP断开连接的过程:客户端发送了FIN数据包,FIN=1,表示不再发送数据了,要断开连接了,但是此时服务端还是可以发送数据的;
- 服务端收到FIN数据包,回复ACK=1,服务端对客户端的FIN进行了确认,此时服务端明确知道了客户端不再发送数据了;
- 服务端此时还可以传输数据,如果有数据没有传输完毕,此时可以继续传输,把剩下的数据传输完毕(TCP协议是全双工协议,服务端可以发送数据,可以接受数据)
- 服务端发送FIN=1数据包,服务端也不在发送数据了
- 客户端收到对方FIN标志后,回复确认;此时客户端也知道了服务端不在发送数据了
- 客户端给服务端Ack回复确认;
- 客户端TIME-WAIT原因: 防止最后一次ack数据包丢失,如果最后一次ack丢包的话,那么就会立马发起数据包重传,防止数据包丢失;
断开连接四次握手的原因:
- 客户端、服务端都必须确保对方不在发送消息了;因此需要4次握手;
- TCP建立连接时候需要3次握手,原因在于第二次握手把SYN、ACK合并为一个数据包发送,因此是三次;对于释放连接来说,无法将第二次和第三次合并,因为中间还需要传输数据,因此必须是4次;
TCP报文结构
在OSI七层网络模型层,数据链路层的数据叫做Frame(帧),网络层的数据叫做packet(包),传输层的数据叫做TCP报文Segment(段),包括首部和数据部分;
TCP报文头部Source Port (源端口):
-
Destination Port(目标端口):
-
Sequence Number(序列号字段):
CP序列号(Sequence Number):占 32 位(4字节)。
- 它表示本报文段所发送数据的第一个字节的编号。
- 在 TCP 连接中,所传送的字节流的每一个字节都会按顺序编号。
- 当SYN标记不为1时,这是当前数据分段第一个字母的序列号;如果SYN的值是1时,这个字段的值就是初始序列值(ISN),用于对序列号进行同步。这时,第一个字节的序列号比这个字段的值大1,也就是ISN加1。
- Acknowledgment Number(确认字段):
- TCP 确认号(Acknowledgment Number,ACK Number):占 32 位。
- 它表示接收方期望收到发送方下一个报文段的第一个字节数据的编号。其值是接收计算机即将接收到的下一个序列号,也就是下一个接收到的字节的序列号加1。
数据偏移字段
TCP 首部长度(Header Length):数据偏移是指数据段中的“数据”部分起始处距离 TCP 数据段起始处的字节偏移量,占 4 位。其实这里的“数据偏移”也是在确定 TCP 数据段头部分的长度,告诉接收端的应用程序,数据从何处开始。
保留字段
保留(Reserved):占 4 位。为 TCP 将来的发展预留空间,目前必须全部为 0。
标志位字段
CWR(Congestion Window Reduce):拥塞窗口减少标志,用来表明它接收到了设置 ECE 标志的 TCP 包。并且,发送方收到消息之后,通过减小发送窗口的大小来降低发送速率。
- ECE(ECN Echo):用来在 TCP 三次握手时表明一个 TCP 端是具备 ECN 功能的。在数据传输过程中,它也用来表明接收到的 TCP 包的 IP 头部的 ECN 被设置为 11,即网络线路拥堵。
- URG(Urgent):表示本报文段中发送的数据是否包含紧急数据。URG=1 时表示有紧急数据。当 URG=1 时,后面的紧急指针字段才有效。
- ACK:表示前面的确认号字段是否有效。ACK=1 时表示有效。只有当 ACK=1 时,前面的确认号字段才有效。TCP 规定,连接建立后,ACK 必须为 1。
- PSH(Push):告诉对方收到该报文段后是否立即把数据推送给上层。如果值为 1,表示应当立即把数据提交给上层,而不是缓存起来。
- RST:表示是否重置连接。如果 RST=1,说明 TCP 连接出现了严重错误(如主机崩溃),必须释放连接,然后再重新建立连接。
- SYN:在建立连接时使用,用来同步序号。当 SYN=1,ACK=0 时,表示这是一个请求建立连接的报文段;当 SYN=1,ACK=1 时,表示对方同意建立连接。SYN=1 时,说明这是一个请求建立连接或同意建立连接的报文。只有在前两次握手中 SYN 才为 1。
FIN:标记数据是否发送完毕。如果 FIN=1,表示数据已经发送完成,可以释放连接。
窗口大小字段
窗口大小(Window Size):占 16 位。它表示从 Ack Number 开始还可以接收多少字节的数据量,也表示当前接收端的接收窗口还有多少剩余空间。该字段可以用于 TCP 的流量控制。
TCP 校验和字段
校验位(TCP Checksum):占 16 位。它用于确认传输的数据是否有损坏。发送端基于数据内容校验生成一个数值,接收端根据接收的数据校验生成一个值。两个值必须相同,才能证明数据是有效的。如果两个值不同,则丢掉这个数据包。Checksum 是根据伪头 + TCP 头 + TCP 数据三部分进行计算的。
紧急指针字段
紧急指针(Urgent Pointer):仅当前面的 URG 控制位为 1 时才有意义。它指出本数据段中为紧急数据的字节数,占 16 位。当所有紧急数据处理完后,TCP 就会告诉应用程序恢复到正常操作。即使当前窗口大小为 0,也是可以发送紧急数据的,因为紧急数据无须缓存。
可选项字段
选项(Option):长度不定,但长度必须是 32bits 的整数倍。
TCP数据传输过程
- 发送者可以发送大小不同的数据包到发送程序
- 对缓存内的数据进行分段,打成TCP数据包在进行发送
- 接收方,数据先进入接收缓存
- 当一定数量的数据包全部到达后,对数据包进行排序重组,以数据流的方式接收
TCP协议传输其他
- 超时重传、立即重传保证数据传输可靠性
-
IP协议
IP协议(Internet Protocal)网络层协议,当前IP协议主要两种架构,IPv4和IPv6。
IPV4协议
工作原理:
分片(Fragmentation)
- 把数据切分成片,适配底层传输网络
- 增加协议头(IP Header)
- Type Of Service:服务的类型,是为了响应不同的用户诉求,用来选择延迟、吞吐量和丢包率之间的关系。
- IHL(Internet Header Length):IP协议头的大小。
- Total Length:报文(封包datagram)的长度
- Identification:报文的ID,发送方分配,代表顺序
- Fragment offset:描述是否要分包(拆分),和如何拆分。
- Time To Live:封包存活的时间。
- Protocol:描述上层的协议,比如TCP=6,UDP=17
- Options:可选项
- Checksum:检验封包的正确性
- 延迟、吞吐量、丢包率
- 延迟:1bit的数据从网络一个终端传输到另一个终端需要的时间
- 吞吐量:单位时间可以传输的平均数据量
- 丢包率:发送出去的封包没有到达目的地的比例
注意:低延迟、高吞吐量、低丢包率,三个条件无法同时满足。
- 寻址(Addressing)
- 给一个地址,然后找到它。IPv4通过子网掩码逐级寻址,然后遭找到设备。
路由(Routing)
地址
- 地址组成数量:IPV4有32位二进制组成,IPV6由128位二进制组成,因此ip地址数量不同。IPV6协议IP地址数量远远大于IPV4协议地址数量。
- IP地址分割符号:IPV4协议IP地址有.分割,如192.0.163.3;IPV6协议IP地址用:分割,如0123:4567:89ab:cdef:0123:4567:89ab:cdef
IPV6地址:
- 对于重复出现的2组0000:0000可以用::代替,同时开头的0也可以省略:
0123:4567:0000:0000:0123:4567:0000:cdef——》123:4567::123:4567:0:cdef
- 省略后面全是0的写法
- 3c4d::**/16 ——》**只有**左边 16 位**有数据,后面是 0
- 1234:5878:abcd**/64 ——》**只有**左边 64 位**有数据,后面是0
- ff00**/8 ——》**只有**左边8位**是有数据,后面是0
- 寻址
- IPV6 全局单播
- 站点前缀(Site Prefix):48bit,一般是由ISP(Internet Service Providor,运营商)或者RIR(Regional Internet Registry, 地区性互联网注册机构)。RIR将IP地址分配给运营商。
- 子网号(Subnet ID):16bit,用于站点内部区分子网。
- 接口号(Interface ID):64bit,用于站点内部区分设备。
- IPV6 全局单播
- IPV6 本地单播 :给定地址,本地网定位设备,如fe80::123e:456d(本地单播必须以fe80开头)
- IPV6 分组多播
- 需要以8个1,也就是 ff00 开头,后面跟上一个分组的编号。
- 所在的网络中已经定义了该分组编号,而且有设备可以识别这个编号。
- 拥有分组下设备的完整清单,并把数据发送给对应的设备们。
- Pv4也支持分组多播,但需要网络配置整体配合。
新设备接入
新设备接入IPv6后,会使用IPv6的邻居发现协议(Neighbour Discover Protocol)为自己申请一个IP地址。当新设备需要发送信息到目的地时,还可以通过ND协议广播查询目标设备。然后如果需要路由,还可以通过ND查找路由器。传统使用ARP协议,每个节点需要存储许多信息;IPV6协议更加无状态化,减少数据冗余带来的风险和负担UDP协议
UDP(User Datagram Protocol)用户数据报协议,在传输层提供直接发送报文的能力(Datagram是数据传输最小单位)。
UDP协议头:
- Source Port:源端口号
- Destination Port:目标端口号。
- Length:消息体长度
- Checksum:检查封包是否出错
- Data octets:一个字节一个字节的数据。Octet是8位。
WebSocket编程——实现HTTP服务器
第一步:简单的Socket
public class RawHttpServer {
public static void main(String[] args) throws IOException {
// 监听8080 端口
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
// 阻塞方法:等待接收客户端的数据,什么时候接收到数据,什么时候程序继续向下执行
Socket socket = serverSocket.accept();
System.out.println("A Socket created");
BufferedReader br = new BufferedReader(new InputStreamReader(new DataInputStream(socket.getInputStream())));
StringBuilder stringBuilder = new StringBuilder();
String line = "";
while (!(line = br.readLine()).isEmpty()) {
stringBuilder.append(line + "\n");
}
String request = stringBuilder.toString();
System.out.println(request);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new DataOutputStream(socket.getOutputStream())));
bw.write("HTTP/1.1 200 ok \n\n <html><head><meta charset=\"UTF-8\"></head><body><h1>这是请求体</h1></body></html>\n");
bw.flush();
socket.close();
}
}
}
第二步:抽象封装
public class Step1HttpServer {
private ServerSocket serverSocket;
private Function<String, String> hander;
public Step1HttpServer(Function<String, String> handler) {
this.hander = handler;
}
public void listen(int port) throws IOException {
serverSocket = new ServerSocket(port);
while (true) {
this.accept();
}
}
private void accept() throws IOException {
Socket socket = serverSocket.accept();
System.out.println("A Socket Created");
BufferedReader br = new BufferedReader(new InputStreamReader(new DataInputStream(socket.getInputStream())));
StringBuilder stringBuilder = new StringBuilder();
String line = "";
while (true) {
line = br.readLine();
if (line == null || line.isEmpty()) {
break;
}
stringBuilder.append(line + "\n");
}
String request = stringBuilder.toString();
System.out.println(request);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new DataOutputStream(socket.getOutputStream())));
bw.write(this.hander.apply(request));
bw.flush();
socket.close();
}
public static void main(String[] args) throws IOException {
Step1HttpServer step1HttpServer = new Step1HttpServer(req ->
{
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "HTTP/1.1 201 OK\n\n GOOD";
});
step1HttpServer.listen(8082);
}
}
第三步:加入多线程
public class Step2HttpServer {
private Function<String, String> handler;
private ServerSocket serverSocket;
public Step2HttpServer(Function<String, String> handler) {
this.handler = handler;
}
public void listen(int port) throws IOException {
serverSocket = new ServerSocket(port);
while (true) {
this.accept();
}
}
private void accept() throws IOException {
Socket socket = this.serverSocket.accept();
new Thread(() -> {
try {
this.handler(socket);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
private void handler(Socket socket) throws IOException {
System.out.println("A Socket Created");
BufferedReader br = new BufferedReader(new InputStreamReader(new DataInputStream(socket.getInputStream())));
StringBuilder stringBuilder = new StringBuilder();
String line = "";
while (true) {
line = br.readLine();
if (line == null || line.isEmpty()) {
break;
}
stringBuilder.append(line + "\n");
}
String request = stringBuilder.toString();
System.out.println(request);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new DataOutputStream(socket.getOutputStream())));
bw.write(this.handler.apply(request));
bw.flush();
socket.close();
}
public static void main(String[] args) throws IOException {
Step2HttpServer step2HttpServer = new Step2HttpServer(req->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "HTTP/1.1 200 OK\n\n GOOD\n";
});
step2HttpServer.listen(8081);
}
}
第四步:封装对象
public class Step3HttpServer {
private Handler handler;
private ServerSocket serverSocket;
public Step3HttpServer(Handler handler) {
this.handler = handler;
}
public void listen(int port) throws IOException {
serverSocket = new ServerSocket(port);
while (true) {
this.accept();
}
}
private void accept() throws IOException {
Socket socket = this.serverSocket.accept();
new Thread(() -> {
try {
this.handler(socket);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
private void handler(Socket socket) throws IOException {
Request request = new Request(socket);
Response response = new Response(socket);
this.handler.handler(request, response);
}
public static void main(String[] args) throws IOException {
Step3HttpServer step2HttpServer = new Step3HttpServer((req,res)->{
System.out.println(req.getHeaders());
res.send("<html><body><h1>Hello World</h1></body></html");
});
step2HttpServer.listen(8081);
}
}
public class Request {
private final String body;
private final String method;
private final Map<String, String> headers;
static Pattern methodRegex = Pattern.compile("(GET|POST|PUT|DELETE|OPTIONS|TRACE|HEAD)");
public Request(Socket socket) throws IOException {
DataInputStream dis = new DataInputStream(socket.getInputStream());
String methodLine = HttpParser.readLine(dis, "UTF-8");
Matcher matcher = methodRegex.matcher(methodLine);
String method1 = "";
if (matcher.find()) {
method1 = matcher.group();
}
Header[] headers = HttpParser.parseHeaders(dis, "UTF-8");
Map<String, String> headersMap = new HashMap<>();
for (Header header : headers) {
headersMap.put(header.getName(), header.getValue());
}
BufferedReader bfr = new BufferedReader(new InputStreamReader(dis));
StringBuilder stringBuilder = new StringBuilder();
char[] buffer = new char[1024 * 16];
while (dis.available() > 0) {
bfr.read(buffer);
stringBuilder.append(buffer);
}
this.body = stringBuilder.toString();
this.headers = headersMap;
this.method = method1;
}
public String getBody() {
return body;
}
public String getMethod() {
return method;
}
public Map<String, String> getHeaders() {
return headers;
}
}
public class Request {
private final String body;
private final String method;
private final Map<String, String> headers;
static Pattern methodRegex = Pattern.compile("(GET|POST|PUT|DELETE|OPTIONS|TRACE|HEAD)");
public Request(Socket socket) throws IOException {
DataInputStream dis = new DataInputStream(socket.getInputStream());
String methodLine = HttpParser.readLine(dis, "UTF-8");
Matcher matcher = methodRegex.matcher(methodLine);
String method1 = "";
if (matcher.find()) {
method1 = matcher.group();
}
Header[] headers = HttpParser.parseHeaders(dis, "UTF-8");
Map<String, String> headersMap = new HashMap<>();
for (Header header : headers) {
headersMap.put(header.getName(), header.getValue());
}
BufferedReader bfr = new BufferedReader(new InputStreamReader(dis));
StringBuilder stringBuilder = new StringBuilder();
char[] buffer = new char[1024 * 16];
while (dis.available() > 0) {
bfr.read(buffer);
stringBuilder.append(buffer);
}
this.body = stringBuilder.toString();
this.headers = headersMap;
this.method = method1;
}
public String getBody() {
return body;
}
public String getMethod() {
return method;
}
public Map<String, String> getHeaders() {
return headers;
}
}
public interface Handler {
void handler(Request request, Response response) throws IOException;
}
DNS and CDN
URL
DNS解析过程
- 先查询浏览器的本地缓存(通常在内存中)
- 本地没缓存,查找操作系统的hosts文件,该文件在linux 中在 /etc/hosts里
- 上述步骤没有找到,DNS会查询本地服务提供商(ISP)
- ISP没找到,请求指向Root根服务器,返回顶级域名服务器地址
- 浏览器发送请求给顶级域名服务器,返回权威域名服务器地址
- 浏览器发送Lookup请求给权威域名服务器,找到具体DNS记录,返回给浏览器
DNS记录
A记录 定义主机的IP地址
定义www.example.com的ip地址:
www.example.com. IN A 139.18.28.5;AAA记录 定义主机IPv6地址
定义www.zhihu.com的ipv6地址:
1251625956.s2txipv6.cdntip.com. 103 IN AAAA 240e:940:401:1:1a::CNAME记录(Canonical Name Record) 定义域名别名
定义www.example.com的别名
www.example.com. IN CNAME example.com.
a.example.com. IN CNAME b.example.com.
www.foo.com. IN CNAME example.com.MX记录(Mail exchanger record) 定义邮件服务器
happy.example.com 作为邮件服务域名 IN MX happy.example.com
A记录描述邮件服务器IP happy.example.com. IN A 123.123.123.123NS记录(Name Server Record) 定义通过DNS信息的服务器
定义为zhihu.com提供dns信息的服务器
zhihu.com. 52908 IN NS ns4.dnsv5.com.
zhihu.com. 52908 IN NS ns3.dnsv5.com.SOA记录(Start of Authority Record) 定义多个ns服务器的主服务器
ns3.dnsv5.com. 是主服务器
IN SOA ns3.dnsv5.com. enterprise3dnsadmin.dnspod.com. 1594718785 3600 180 1209600 180TXT记录 通过文本信息
zhihu.com提供的文本信息
zhihu.com. 600 IN TXT “google-site-verification=q42VyLbU7bjRv5xb2279AX9jJ3Vuxp-e4XG_f1EQRGk”
zhihu.com. 600 IN TXT “m5g7qjk31l5d1hkq6m3zvcf6lg2f0h16”
zhihu.com. 600 IN TXT “v=spf1 include:_spf.google.cominclude:cust-spf.edmsphere.com -all”CDN 内容分发网络(Content Delivery Network)
基于地理位置的分布式代理服务器/数据中心,通过高可用,提升性能,提升体验。CDN实现原理
Http协议
Http协议(Hyper Text Transfer Protocal)是应用广泛的应用层协议,是用于处理客户端与服务端之间的通信。
Http由两部分组成:HTTP请求和响应HTTP请求
<request-line> <headers> <blank line> [<request-body>]
- HTTP请求中,第一行为请求行(
),用来说明请求的类型、要访问的资源以及使用的HTTOP版本。 - 接下来是请求头部信息(
),用来说明服务器要使用的附加信息 - 添加一个空行(
),用于分割请求头和请求体 - 请求体(可选,[
])
GET / HTTP/1.1
Host: www.baidu.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: BAIDUID=21ADE347AA4B28BE2A711549E6670E0F:SL=0:NR=10:FG=1; BIDUPSID=21ADE347AA4B28BE8EDA6B82CB710D59; PSTM=1646574258; BD_UPN=13314752; BDUSS=XNIVH5xRmVaZDlma0p-RTJLbXJlQjExMngtZWRjcndJbFlRa295NEdUYlhSMHhpRVFBQUFBJCQAAAAAAAAAAAEAAACMGXCdsKzQodCh0-O2-QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANe6JGLXuiRic; COOKIE_SESSION=5_4_8_9_8_33_0_0_4_9_23_0_85_172208_0_25_1650216039_1650216509_1650216484%7C9%23172216_3_1650216484%7C3; Hm_lvt_aec699bb6442ba076c8981c6dc490771=1649687930,1650215949; baikeVisitId=bf4c24d8-4f63-47d3-8883-e17de17b8cb5; sug=3; sugstore=1; ORIGIN=0; bdime=0
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
- GET / HTTP/1.1 —— 说明请求方法是GET,/ 表示请求域名的根目录,HTTP/1.1 说明HTTP版本是1.1
- Host: www.baidu.com —— Host 请求的地址
- User-Agent:** —— User-Agent,服务器端和客户端脚本都能够访问它,它是浏览器类型检测逻辑的重要基础
- Accept:** —— 浏览器可接受的MIME类型。
- Accept-Language:** —— 浏览器可接受语言
- Accept-Encoding:** —— 浏览器能够进行解码的数据编码方式
Connection;keep-alive —— 通常将浏览器操作设置为Keep-Alive
常见请求头
Accept:浏览器可接受的MIME类型。
- Accept - Charset:浏览器可接受的字符集。
- Accept - Encoding:浏览器能够进行解码的数据编码方式,比如gzip。Servlet能够向支持gzip的浏览器返回经gzip编码的HTML页面。许多情形下这可以减少5到10倍的下载时间。
- Accept - Language:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。
- Authorization:授权信息,通常出现在对服务器发送的WWW - Authenticate头的应答中。
- Connection:表示是否需要持久连接。如果Servlet看到这里的值为“Keep - Alive”,或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点,Servlet需要在应答中发送一个Content - Length头,最简单的实现方法是:先把内容写入ByteArrayOutputStream,然后在正式写出内容之前计算它的大小。
- Content - Length:表示请求消息正文的长度。
- Cookie:这是最重要的请求头信息之一,参见后面《Cookie处理》一章中的讨论。
- From:请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它。
- Host:初始URL中的主机和端口。
- If - Modified - Since:只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“Not Modified”应答。
- Pragma:指定“no - cache”值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝。
- Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。
- User - Agent:浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用。
UA - Pixels,UA - Color,UA - OS,UA - CPU:由某些版本的IE浏览器所发送的非标准的请求头,表示屏幕大小、颜色深度、操作系统和CPU类型。
常用请求方法
GET:从服务器获取资源
- POST:在服务器创建资源(幂等性)
- PUT:在服务器修改资源
- DELETE:在服务器删除资源
- OPTION :跨域部分讲解
- TRACE :用于显示调试信息 多数网站不支持
- CONNECT: 代理部分讲解
-
Keep-alive断开
单个请求:请求完成后,在timeout时间内没第二个请求进来则会关闭。
多个请求:在一个请求响应之后,在 timeout 时间内有另一个请求进来,就会利用相同的 TCP 连接继续响应这个请求,直到没有更多请求进来,可以通过 max 字段设定最多响应的请求数。
HTTP响应
<status-line> <headers> <blank line> [<response-body>
HTTP响应中,第一行为响应状态(
),用来说明响应的状态信息 - 接下来是响应头部信息(
), - 添加一个空行(
),用于分割响应头和响应体 - 响应体(可选,[
]) ```bash HTTP/1.1 200 OK Bdpagetype: 2 Bdqid: 0xbcd16c810002ddf1 Cache-Control: private Connection: keep-alive Content-Encoding: gzip Content-Type: text/html;charset=utf-8 Date: Sat, 23 Apr 2022 08:48:58 GMT Expires: Sat, 23 Apr 2022 08:48:57 GMT Server: BWS/1.1 Set-Cookie: BDSVRTM=83; path=/ Set-Cookie: BD_HOME=1; path=/ Set-Cookie: H_PS_PSSID=36182_36309_34812_36166_34584_36338_36075_36126_36297_26350_36313_36061; path=/; domain=.baidu.com Strict-Transport-Security: max-age=172800 Traceid: 1650703738028897741813605775250569747953 X-Frame-Options: sameorigin X-Ua-Compatible: IE=Edge,chrome=1 Transfer-Encoding: chunked
响应状态码
- 1xx:提供信息
- 100 continue
- 101 切换协议(switch protocol)
- 2xx:成功
- 200 – OK 成功
- 201 – Created 已创建
- 202 – Accepted 已接收
- 203 – Non-Authoritative Information 非权威内容
- 204 – No Content 没有内容
- 205 – Reset Content 重置内容
- 206 – Partial Content 服务器下发了部分内容(range header)
- 3xx:重定向
- 300 – Multiple Choices 用户请求了多个选项的资源(返回选项列表)
- 301 – Moved Permanently 永久转移
- 302 – Found 资源被找到(以前是临时转移)
- 303 – See Other 可以使用GET方法在另一个URL找到资源
- 304 – Not Modified 没有修改(缓存部分特别说明)
- 305 – Use Proxy 需要代理
- 307 – Temporary Redirect 临时重定向
- 308 – Permanent Redirect 永久重定向
补充:客户端收到308请求后,延用旧的method(POST/GET/PUT)到新地址;
客户端收到301请求后,通常用户会向新地址发起GET请求
- 4xx:客户端错误
- 400 – Bad Request 请求格式错误
- 401 – Unauthorized 没有授权
- 402 – Payment Required 请先付费
- 403 – Forbidden 禁止访问
- 404 – Not Found 没有找到
- 405 – Method Not Allowed 方法不被允许
- 406 – Not Acceptable 服务端可以提供的内容和客户端期待的不一样
- 5xx:服务端错误
- 500 – Internal Server Error(内部服务器错误)
- 501 – Not Implemented(没有实现)
- 502 – Bad Gateway(网关错误)
- 503 – Service Unavailable(服务不可用)
- 504 – Gateway Timeout(网关超时)
- 505 – HTTP Version Not Supported(版本不支持)
HTTP缓存
http协议网络结构三种实体:web服务器、浏览器、代理
通过将数据存储起来,提升访问速度
缓存条目:通常以key-value形式进行存储,value不仅仅包含数据,还会包含一些描述字段,如失效时间
缓存置换:缓存满了后,每次穿件新的缓存条码,换删除旧的缓存条目。LRU(Least recently used)缓存置换算法,最近最少使用。
http缓存最重要的配置项为Cache-Control: private返回头。
浏览器和服务器之间的HTTP代理服务器也可以缓存,代理分为正向代理和反向代理
例:Cache-Control:public,max-age:10000 设为public允许所有中间方进行缓存