# 浏览器访问资源
# URL
统一资源定位符(Uniform Resource Locator)
# 格式
# HTTP 协议
http://user:password@www.example.com:80/dir/file1.htm
- 协议
- 用户名、密码,可省略
- web 服务器域名
- 端口,可省略
- 文件路径名
# FTP 协议
ftp://user:password@ftp.example.com:21/dir/file1.htm
- 协议
- 用户名、密码,可省略
- FTP 服务器域名
- 端口,可省略
- 文件路径名
# 本地文件
file://localhost/c:/path/file1.zip
- 协议
- 计算机名,可省略
- 文件的路径名
# 电子邮件
mailto:toUser@example.com
# 新闻
news:comp.protocols.tcp-ip
# 生成 HTTP 请求消息
请求信息
<方法><空格><URI><空格><HTTP版本> | |
<字段名>:<字段值> | |
... | |
... | |
<空行> | |
<消息体> |
# HTTP 响应信息
响应信息
<HTTP版本><空格><状态码><空格><响应描述> | |
<字段名>:<字段值> | |
... | |
... | |
<空行> | |
<消息体> |
# DNS 查询服务器 IP 地址
# IP
# 表示方式
# 点分十进制
每 8 bit 用。分隔。
10.11.12.13
# 子网掩码
10.11.12.13/255.255.255.0
# 比特数的子网掩码
10.11.12.13/24
前 24 位全为 1,剩余为 0,表示主机数。
- IP 可以得出网络号
- 除了网络号,剩余的位数,继续用子网掩码划分
- 子网掩码 + 网络号,得出子网号,剩余的才是这个子网的主机号。
# 代表整个子网
10.11.12.0/24
# 子网内广播
10.11.12.255/24
# DNS 解析
DNS(Domain Name System)查询 IP 地址的操作,称为域名解析,负责执行此操作的为解析器。
# 查询条件
- 域名
- Class:目前只固定 IN
- 记录类型:A - 地址;MX - Mail eXchange
域名 Class 记录类型 响应数据 | |
www.lab.glasscom.com IN A 192.0.2.226 | |
glasscom.com IN MX 10 mail.glasscom.com | |
mail.glasscom.com IN A 192.0.2.227 |
邮箱还有一个优先级
# 域名分层
使用英文句点来划分,靠右层级越高。
例如: www.lab.glasscom.com
- 一级域名:com
- 二级域名:glasscom
- 三级、四级:lab、www
# 根域服务器
它没有名字,如果要表示,就在域名最右边添加句点,例如:
www.lab.glasscom.com.
全球目前只有 13 台 根域服务器,这些都配置在主机中的。
当然还有成千上万台镜像 DNS 服务器,会定时更新根域服务器的数据。
# 查找过程
www.lab.glasscom.com. 域名的查找过程
- 从主机配置的 DNS 服务器查找;配置是系统 TCP/IP 自动配置好的,或者手动配置。
- 查找不到了,再从最高级别查起,即根域或其镜像 DNS 服务器查找
- 没有再从 com 的 DNS 服务器查找
- 然后一级一级的查询,直到查询或到达最低级域名 DNS 为止。
当然,DNS 服务器可能包含多级的域名注册信息,因此不用查询多次。以及 DNS 服务器有缓存。
低级的域名都会注册到高级的域名服务器中。
例如:一级域名 com、cn 等的域名都注册到根域服务器中。
# DNS 使用 TCP 还是 UDP?
两者都使用:
- 在局域网中使用 UDP
- 在跨越较远的根服务器、一级等,用 TCP
# 委托协议栈发送信息
得到对方的 IP 后,就可以委托系统的 Socket 库来收发数据。
Socket 建立两者之间的通道,数据在通道中流动。
流程:
- 双方创建套接字(创建阶段)
- 客户端连接服务器的套接字上(连接阶段)
- 收发数据(通信阶段)
- 断开管道并删除套接字(断开阶段)
应用程序
// DNS | |
<内存地址> = gethostbyname("www.lab.glasscom.com"); | |
// 创建阶段 | |
<描述符> = socket(<IPv4>, <流模式>, ...); | |
// 连接阶段 | |
connect(<描述符>, <服务器的 IP 地址和端口>, ...); | |
// 通信阶段 | |
write(<描述符>, <发送数据>, <数据长度>); | |
<接收数据长度> = read(<描述符>, <接收缓冲区>, ...); | |
// 断开阶段 | |
close(<描述符>); |
# Socket、socket、套接字区别:
- Socket 代表库,包含多种组件
- socket 指的是创建套接字的 socket 程序组件
- 套接字:指的是双方创建的通道
# 创建阶段
使用 socket 组件,创建套接字,协议栈会返回描述符号。
描述符储存在内存中,就类似一个 ID,用于区分是哪个套接字。
# 连接阶段
使用 connect 组件;需要指定描述符、服务器 IP 地址、端口号。
IP 能识别到某台主机,但不知道对方的哪个套接字,因此需要端口号。
端口号能识别到对方的某个套接字
因此连接阶段,通过 IP 和端口号,能连接到对方主机的套接字,完成套接字的连接,形成通道。
# 为什么不使用套接字的描述符代表端口呢?
因为描述符代表的是本机的套接字,是本机识别本机的套接字的;而对方的套接字无法知道。
因此,使用双方约定好的端口号,作为对方的套接字的识别。
# 通信阶段:传递消息
通过 write 和 read 来读写消息,实现传递消息。
例如,HTTP,生成 HTTP 请求报文,然后调用 write 写入通道中,服务器通过 read 读取通道的数据。
# 断开阶段
使用 close 组件,断开和删除套接字,通道关闭。
HTTP 协议规定,web 服务器发送完响应消息后,先断开;然后浏览器知道断开后,也会调用 close 断开。
不同协议,客户端和服务器端哪一方先执行 close 都有可能。