『计算机网络』 应用层

本文是《计算机网络:自顶向下方法》 应用层这一章节的读书笔记,主要包括网络应用原理、HTTP、STMP、DNS 以及简单的Socket 编程。

网络应用原理

网络应用体系

客服端-服务端(Client-Server)

  • 总有一个服务端正在运行等待或者正在被大量应用端程序连接
  • 服务器具有固定的ip地址
  • 托管大量服务器主机的地方被称为数据中心,大型互联网公司都有自己的数据中心,比如Google,微软,Netflix等

P2P 体系架构

  • 具体应用就是迅雷下载(:

进程通信

  • 在两个不同系统上的进程,通过计算机网络交换报文而互相通信。发送进程生成并向网络中发送报文;接收进程接受这些报文并可能通过返回报文进行响应。

  • 每对通信进程,我们会分为客户进程和服务进程。发起通信的为客户进程,响应等待的为服务进程。

  • 进程通过socket 进行发送和接收报文

  • 通过IP 地址 和端口号(唯一值) 进行识别接受进程

Transport Services Available to Applications

从四个方面对运输服务进行分类

  • 可靠的数据传输,选择可靠的传输协议,TCP
  • 吞吐量,带宽决定,简单来说就是加钱,拉专线….
  • 定时,发送socket byte 到接受的时间不大于固定的时间(网络延迟)
  • 安全性:在运输服务中提供

互联网提供的运输服务

TCP服务

  • 面向连接的
  • 可靠的
  • 拥塞控制

UDP 服务

  • 无连接的
  • 不可靠的
  • 无序的

不过TCP 和UDP 都不提供安全性,安全性是通过TLS 进行实现的。TLS 不是运输层协议

HTTP

HTTP, 超文本传输协议,Web 协议。HTTP 定义了web client 向web server 请求web页面的方式和server 向client 传输web 页面的方式。

Web 浏览器(实现了HTTP 客户端)

HTTP 是无状态的,服务端不会记录客户端请求了多少次,所以每次请求返回的处理过程都是一样的

持续连接与非持续连接

持续连接:每个请求/相应对都使用相同的TCP 连接传输
非持续连接:每个请求/相应对都使用单独的TCP 连接传输

RTT(Round-Trip Time) 往返时间: 一个短分组从client 到setver然后server在返回到client 所花费的时间,RTT包括分组传播时延、分组中间路由器和交换机上的排队时延以及分组处理时延。

TCP 三次握手的 前两次所耗费的时间占用了一个RTT,client 结合三次握手的确认部分向该TCP连接发送了一个HTTP 请求报文,一旦该请求报文到达服务器,服务器就会返回响应。这里有用了一个RTT;

[image-20240706104719731.png]

持续连接的HTTP, 在建立了连接之后,客服端向服务端请求获取文件可以通过一个建立的连接进行通信,避免了每次请求资源都要先消耗一次RTT,大大提升了请求效率;

TODO 使用wireshark 去观察持续连接和非持续连接

HTTP 报文格式

请求报文

[image-20240706110358779.png]

第一行为请求行,包括三个字段

  • 方法字段:GET, POST,HEAD,PUT,DELETE 等
  • URL 字段: 请求对象的表示
  • HTTP 版本字段: 当前HTTP 的版本,浏览器自实现决定

后面的行叫首部行

  • 首部行的格式为 header field name : value 这样的K-V 格式
  • 使用 Connection: close, 告诉服务端响应完这次请求之后就关掉连接
  • 其他的还有很多,比如token 也是放在header line 里面

空格之后是Entity Body

  • 通常为用户表单里面输入的内容

HTTP 请求方式

  • GET
  • POST
  • HEAD
  • PUT
  • DELETE

响应报文

[image-20240706111323717.png]

第一行为状态行,包括三个部分

  • HTTP 协议版本
  • HTTP 状态码
  • HTTP 对应状态信息
    后面的行为Header Lines
  • 与请求报文类似,但是内容不一样,与sever 的信息有关
  • 如果请求的时候只想要返回header lines 而不要下面的body ,可以使用HEAD 请求

空行之后就是响应体,浏览器处理返回的响应体

  • 返回数据

HTTP 状态码

  • 200 OK :请求成功
  • 301 :对应已经被永久转移
  • 400 :请求不能被服务器理解
  • 404 :请求不存在
  • 505:服务器不支持当前HTTP版本
  • ps, 200 为正常,其他情况都为异常,4开头与client 有关,5开头与sever 有关

HTTP 是无状态的,所以无法识别用户,为了识别用户,HTTP 在客户端使用了 cookie。

[image-20240706112130875.png]

Set-cookie:1678 : web sever 就能通过分配的唯一表示识别client

应用场景:

  • 用户偏好设置,语言、颜色等
  • 非登录状态的购物车
  • 广告追踪

HTTP 缓存服务器

[image-20240706113129339.png]

web cache 也叫web proxy,代表初始web 服务器来满足HTTP 请求的实体,比如CDN

减少对初始web 服务器的请求,降低响应时延。

[image-20240706113504834.png]

HTTP/2

HTTP/2 改变的是数据格式化方法以及client 和server 之间传输的方式,针对大流量,HOL 的解决方案是将每个报文分成小帧,在相同的TCP 连接上交错传输和响应报文。

将每一个HTTP 报文分成独立的帧、交错发送并在接收端将其装配起来成一个完成的报文,是HTTP/2 最大的改进

HTTP/2 允许一个客户请求发送多个响应。

TODO 使用代码和wireshark 进行观测复现

SMTP

简单邮件传输协议:

  • 用户代理
  • 邮件服务器
  • 简单邮件传输协议
    [image-20240706115635251.png]

STMP 基于TCP 进行可靠数据传输。每台邮件服务器即使client 又是server

[image-20240706115832486.png]

邮件访问协议

  1. 发送邮件: HTTP 或SMTP
  2. 访问收到的邮件:通过HTTP 或IMAP 进行访问

如果用户都使用的是gmail,那么就会在服务提供商的内网进行转发;

DNS

DNS(Domain Name System)

  • 分层的DNS 服务器实现分布式数据库
  • 是主机能够查询分布式数据库的应用层协议
  • DNS 服务器通常是允许BIND 软件的Unix 机器
  • DNS 运行在UDP 上,使用53 端口
  • DNS 通常是由其他应用层协议使用的,包络HTTP 和SMTP

DNS 主要功能

  • 主机名到ip 地址的转换
  • 主机别名
  • 邮件服务器别名
  • 负载分配

[image-20240706121141873.png]

DNS 迭代查询
DNS 递归查询
DNS 缓存

[image-20240706121527285.png]

CDN

HTTP 与DASH

  • 根据客户可用带宽视频不同的的分辨率流量,比如1080p,2k,4k,
  • 视频网站都是自适应,也可以自己手动切换

CDN
将用户请求的内容放在用户最近或者最佳的数据中心处

服务器安置分为两种方式

  • 深入安置,深入ISP 的接入网,与用户靠的最佳
  • 邀请做客,自建数据中心,然后邀请ISP 接入自建的数据中心,第三方CDN 使用的是这种方式
    [image-20240706122318447.png]

CDN 集群选择策略(chatGPT)

  1. 轮询(Round Robin): 依次将请求分配给集群中的各个服务器。
  2. 加权轮询: 根据服务器的处理能力分配不同的权重,权重高的服务器获得更多请求。
  3. 最少连接: 将新请求分配给当前连接数最少的服务器。
  4. 源IP哈希: 根据客户端IP地址的哈希值选择服务器,可以保证同一客户端总是访问同一服务器。
  5. 随机: 随机选择一个服务器处理请求。
  6. 地理位置: 根据用户地理位置选择最近的服务器。
  7. 响应时间: 选择响应时间最短的服务器。

TCP Socket 编程

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
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class HTTPServer {


public static void main(String[] args) {
start0();
}

private static void start0() {
try {
ServerSocketChannel open = ServerSocketChannel.open();
open.bind(new InetSocketAddress(8080));
while (true) {
SocketChannel accept = open.accept();
new Thread(() -> {
try {
Socket socket = accept.socket();
byte[] bytes = new byte[1024];
socket.getInputStream().read(bytes);
// 创建 JSON 数据
String jsonResponse = "{\"message\": \"Hello, World!\", \"status\": \"success\"}";

// 发送 HTTP 响应头
String response = "HTTP/1.1 200 OK\r\n" +
"Content-Type: application/json\r\n" +
"Content-Length: " + jsonResponse.length() + "\r\n\r\n" + jsonResponse;
OutputStream outputStream = socket.getOutputStream();
outputStream.write(response.getBytes());
outputStream.flush();
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}).start();
}

} catch (IOException e) {
throw new RuntimeException(e);
}

}
}