参考引用
- UNIX 环境高级编程 (第3版)
- 黑马程序员-Linux 网络编程
1. 协议的概念
1.1 什么是协议
-
从应用的角度出发,协议可理解为 “规则”,是数据传输和数据解释的规则
- 假设,A、B双方欲传输文件,规定:
- 第一次,传输文件名,接收方接收到文件名,应答 OK 给传输方
- 第二次,发送文件大小,接收方接收到该数据再次应答一个 OK
- 第三次,传输文件内容。同样,接收方接收数据完成后应答 OK 表示文件内容接收成功
- 由此,无论 A、B 之间传递何种文件,都是通过三次数据传输来完成。A、B 之间形成了一个最简单的数据传输规则。双方都按此规则发送、接收数据。A、B 之间达成的这个相互遵守的规则即为协议
- 假设,A、B双方欲传输文件,规定:
-
这种仅在 A、B 之间被遵守的协议称之为原始协议。当此协议被更多的人采用,不断的增加、改进、维护、完善。最终形成一个稳定的、完整的文件传输协议,被广泛应用于各种文件传输过程中,该协议就成为一个标准协议,最早的 ftp 协议就是由此衍生而来
TCP 协议注重数据的传输,http 协议着重于数据的解释
1.2 典型协议
- 传输层:常见协议有 TCP/UDP 协议
- TCP 传输控制协议(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议
- UDP 用户数据报协议(User Datagram Protocol)是 OSI 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务
- 应用层:常见的协议有 HTTP 协议,FTP 协议
- HTTP 超文本传输协议(Hyper Text Transfer Protocol)是互联网上应用最为广泛的一种网络协议
- FTP文件传输协议(File Transfer Protocol)
- 网络层:常见协议有 IP 协议、ICMP 协议、IGMP 协议
- IP 协议是因特网互联协议(Internet Protocol)
- ICMP 协议是Internet控制报文协议(Internet Control Message Protocol)它是 TCP/IP 协议族的一个子协议,用于在 IP 主机、路由器之间传递控制消息
- IGMP 协议是 Internet 组管理协议(Internet Group Management Protocol),是因特网协议家族中的一个组播协议,该协议运行在主机和组播路由器之间
- 网络接口层:常见协议有 ARP 协议、RARP 协议
- ARP 协议是正向地址解析协议(Address Resolution Protocol),通过已知的 IP,寻找对应主机的 MAC 地址
- RARP 是反向地址转换协议,通过 MAC 地址确定 IP 地址
2. 网络应用程序设计模式
2.1 C/S 模式
- 传统的网络应用设计模式,客户机 (client) /服务器 (server) 模式。需要在通讯两端各自部署客户机和服务器来完成数据通信
2.2 B/S 模式
- 浏览器 (browser) /服务器 (server) 模式。只需在一端部署服务器,而另外一端使用每台 PC 都默认配置的浏览器即可完成数据的传输
2.3 优缺点分析
- C/S 模式的优点
- 客户端位于目标主机上可以保证性能,将数据缓存至客户端本地,从而提高数据传输效率
- 一般来说客户端和服务器程序由一个开发团队创作,所采用的协议相对灵活,可以在标准协议的基础上根据需求裁剪及定制
- 例如,腾讯公司所采用的通信协议,即为 ftp 协议的修改剪裁版
因此,传统的网络应用程序及较大型的网络应用程序都首选 C/S 模式进行开发。如知名的网络游戏魔兽世界。3D 画面,数据量庞大,使用 C/S 模式可以提前在本地进行大量数据的缓存处理,从而提高观感
- C/S 模式的缺点
- 由于客户端和服务器都需要有一个开发团队来完成开发。工作量将成倍提升,开发周期较长
- 从用户角度出发,需要将客户端安插至用户主机上,对用户主机的安全性构成威胁
- B/S 模式的优点
- 由于它没有独立的客户端,使用标准浏览器作为客户端,其工作开发量较小
- 由于其采用浏览器显示数据,因此移植性非常好,不受平台限制
- 如早期的偷菜游戏,在各个平台上都可以完美运行
- B/S 模式的缺点
- 由于使用第三方浏览器,因此网络应用支持受限
- 没有客户端放到对方主机上,缓存数据不尽如人意,从而传输数据量受到限制,应用的观感大打折扣
- 必须与浏览器一样,采用标准 http 协议进行通信,协议选择不灵活
3. 分层模型
3.1 OSI 七层模型
- 物理层
- 主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用是传输比特流(就是由 1、0 转化为电流强弱来进行传输,到达目的地后再转化为 1、0,即数模与模数转换)
- 数据链路层
- 定义了如何让格式化数据以帧为单位进行传输,以及如何让控制对物理介质的访问。这一层通常还提供错误检测和纠正,以确保数据的可靠传输
- 如:串口通信中使用到的 115200、8、N、1
- 网络层
- 在位于不同地理位置的网络中的两个主机系统之间提供连接和路径选择
- 传输层
- 定义了一些传输数据的协议和端口号(WWW 端口 80 等)
- 如:TCP(传输控制协议,传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报协议,用于传输可靠性要求不高,数据量小的数据,如 QQ 聊天数据)
- 主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组,常常把这一层数据叫做段
- 会话层
- 通过传输层 (端口号:传输端口与接收端口) 建立数据传输的通路
- 主要在你的系统之间发起会话或者接受会话请求(设备之间需要互相认识可以是 IP 也可以是 MAC 或者是主机名)
- 表示层
- 可确保一个系统的应用层所发送的信息可以被另一个系统的应用层读取
- 例如,PC 程序与另一台计算机进行通信,其中一台计算机使用扩展二一十进制交换码(EBCDIC),而另一台则使用美国信息交换标准码(ASCII)来表示相同的字符。如有必要,表示层会通过使用一种通格式来实现多种数据格式之间的转换
- 应用层
- 是最靠近用户的 OSI 层
- 这一层为用户的应用程序(例如电子邮件、文件传输和终端仿真)提供网络服务
3.2 TCP/IP 四层模型
- TCP/IP 网络协议栈分为应用层(Application)、传输层(Transport)、网络层(Network)和链路层(Link)
4. 通信过程
- 链路层有以太网、令牌环网等标准,链路层负责网卡设备的驱动、帧同步(即从网线上检测到什么信号算作新帧的开始)、冲突检测(如果检测到冲突就自动重发)、数据差错校验等工作
- 交换机是工作在链路层的网络设备,可在不同的链路层网络之间转发数据帧(如十兆以太网和百兆以太网之间、以太网和令牌环网之间),由于不同链路层的帧格式不同,交换机要将进来的数据包拆掉链路层首部重新封装之后再转发
- 网络层的 IP 协议是构成 Internet 的基础
- Internet 上的主机通过 IP 地址来标识,Internet 上有大量路由器负责根据 IP 地址选择合适的路径转发数据包,数据包从 Internet 上的源主机到目的主机往往要经过十多个路由器
- 路由器是工作在第三层的网络设备,同时兼有交换机的功能,可在不同的链路层接口之间转发数据包,因此路由器要将进来的数据包拆掉网络层和链路层两层首部并重新封装
IP 协议不保证传输的可靠性,数据包在传输过程中可能丢失,可靠性可以在上层协议或应用程序中提供支持
点到点和端到端的区别
- 网络层负责点到点(point-to-point)的传输(这里的 “点” 指主机或路由器),而传输层负责端到端(end-to-end) 的传输(这里的 “端” 指源主机和目的主机),传输层可选择 TCP 或 UDP 协议
- TCP 是一种面向连接的、可靠的协议,有点像打电话,双方拿起电话互通身份之后就建立了连接,然后说话就行了,这边说的话那边保证听得到,并且是按说话的顺序听到的,说完话挂机断开连接
- 也就是说 TCP 传输的双方需要首先建立连接,之后由 TCP 协议保证数据收发的可靠性,丢失的数据包自动重发,上层应用程序收到的总是可靠的数据流,通讯之后关闭连接
- UDP 是无连接的传输协议,不保证可靠性,有点像寄信,信写好放到邮筒里,既不能保证信件在邮递过程中不会丢失,也不能保证信件寄送顺序
- 使用 UDP 协议的应用程序需要自己完成丢包重发、消息排序等工作
- TCP 是一种面向连接的、可靠的协议,有点像打电话,双方拿起电话互通身份之后就建立了连接,然后说话就行了,这边说的话那边保证听得到,并且是按说话的顺序听到的,说完话挂机断开连接
路由器和交换机的区别
- 功能:路由器是网络层设备,主要用于将数据包在不同的网络之间进行转发,以连接不同的网络,并使其能够相互通信。交换机是数据链路层设备,主要用于在局域网内部传输数据,它根据 MAC 地址在局域网上的不同设备之间转发数据包
- 范围:路由器连接不同的网络,例如连接家庭网络和互联网、连接不同办公室的网络等。交换机通常用于局域网,在局域网内部连接设备
- 通信方式:路由器使用广播和多播技术在不同网络之间转发数据包,支持不同类型的通信需求。交换机使用单播技术在局域网中直接将数据包从源设备发送到目标设备
- 目的主机收到数据包后,如何经过各层协议栈最后到达应用程序呢?其过程如下图所示
-
以太网驱动程序首先根据以太网首部中的 “上层协议” 字段确定该数据帧的有效载荷(payload,指除去协议首部之外实际传输的数据)是 IP、ARP 还是 RARP 协议的数据报,然后交给相应的协议处理
- 假如是 IP 数据报,IP 协议再根据 IP 首部中的 “上层协议” 字段确定该数据报的有效载荷是 TCP、UDP、ICMP 还是 IGMP,然后交给相应的协议处理
- 假如是 TCP 段或 UDP 段,TCP 或 UDP 协议再根据 TCP 首部或 UDP 首部的 “端口号” 字段确定应该将应用层数据交给哪个用户进程
IP 地址是标识网络中不同主机的地址,而端口号就是同一台主机上标识不同进程的地址,IP 地址和端口号合起来标识网络中唯一的进程
- 假如是 IP 数据报,IP 协议再根据 IP 首部中的 “上层协议” 字段确定该数据报的有效载荷是 TCP、UDP、ICMP 还是 IGMP,然后交给相应的协议处理
-
局域网内两台机器如何利用 TCP/IP 通信?
- 在局域网内进行 TCP/IP 通信,需要知道对方机器的 IP 地址和所使用的端口号。然后可以利用网络编程提供的 API 进行编程操作进行通信
- 以下是一个简单的示例,假定计算机 A 要向计算机 B 发送一条 “Hello” 的消息
- 1、确认计算机 B 的 IP 地址和使用的端口号
- 2、在计算机 A 上通过编程语言的网络编程 API 创建一个客户端 Socket 对象
- 3、通过 Socket 对象将消息 “Hello” 发送到计算机 B 的指定端口上;
- 4、计算机 B 接收到来自计算机 A 的消息,通过编程语言的网络编程 API 创建一个服务器端 Socket 对象,并在指定端口上侦听客户端连接
- 5、计算机 A 从计算机 B 的指定端口上接收到来自计算机 B 的响应消息
5. 协议格式
5.1 数据包封装
- 传输层及其以下的机制由内核提供,应用层由用户进程提供,应用程序对通讯数据的含义进行解释,而传输层及其以下处理通讯的细节,将数据从一台计算机通过一定的路径发送到另一台计算机
- 应用层数据通过协议栈发到网络上时,每层协议都要加上一个数据首部(header),称为封装
- 不同的协议层对数据包有不同的称谓,在传输层叫做段(segment),在网络层叫做数据报(datagram),在链路层叫做帧(frame)
- 数据封装成帧后发到传输介质上,到达目的主机后每层协议再剥掉相应的首部,最后将应用层数据交给应用程序处理
- 网络传输数据封装流程
- 数据没有封装之前,不能在网络中传递
5.2 以太网帧格式
- 其中的源地址和目的地址是指网卡的硬件地址(也叫 MAC 地址),长度是 48 位,是在网卡出厂时固化的
- 可在 shell 中使用 ifconfig 命令查看,“HWaddr 00:15:F2:14:9E:3F” 部分就是硬件地址。协议字段有三种值,分别对应 IP、ARP、RARP。帧尾是 CRC 校验码
5.3 ARP 数据报格式
-
在网络通讯时,源主机的应用程序知道目的主机的 IP 地址和端口号,却不知道目的主机的硬件地址,而数据包首先是被网卡接收到再去处理上层协议的,如果接收到的数据包的硬件地址与本机不符,则直接丢弃
- 因此在通讯前必须获得目的主机的硬件地址,ARP 协议就起到这个作用
-
每台主机都维护一个 ARP 缓存表,可以用 arp -a 命令查看。缓存表中的表项有过期时间(一般为 20 分钟),如果 20 分钟内没有再次使用某个表项,则该表项失效,下次还要发 ARP 请求来获得目的主机的硬件地址
-
源 MAC 地址、目的 MAC 地址在以太网首部和 ARP 请求中各出现一次,对于链路层为以太网的情况是多余的,但如果链路层是其它类型的网络则有可能是必要的
-
硬件类型指链路层网络类型,1 为以太网,协议类型指要转换的地址类型,0x0800 为 IP 地址,后面两个地址长度对于以太网地址和 IP 地址分别为 6 和 4 (字节),op 字段为 1 表示 ARP 请求,op 字段为 2 表示 ARP 应答
5.4 IP 数据报格式
- 版本:IPv4、IPv6 – 4 位
- TTL:time to live
- 设置数据包在路由节点中的跳转上限
- 每经过一个路由节点,该值 -1,减为 0 的路由有义务将该数据包丢弃
- 这个生存时间的单位不是秒,而是跳(hop)
- 源 IP:32 位,4 字节
- 192.168.1.108 — 点分十进制 IP 地址(string)
- 目的 IP:32 位,4 字节
5.5 UDP 数据报格式
- 16 位源端口号,16 位目的端口号
- 2^16 = 65536
- IP 地址:可以在网络环境中,唯一标识一台主机
- 端口号:可以在一台主机上,唯一标识一个进程
- IP 地址 + 端口号:可以在网络环境中,唯一标识一个进程
5.6 TCP 数据报格式
6. TCP 协议
6.1 TCP 通信时序
首先客户端主动发起连接、发送请求,然后服务器端响应请求,然后客户端主动关闭连接。双方发送的段按时间顺序编号为 1-10,各段中的主要信息在箭头上标出,例如段 2 的箭头上标着 SYN, 8000(0), ACK 1001:表示该段中的 SYN 位置 1,32 位序号是 8000,该段不携带有效载荷(数据字节数为 0),ACK 位置 1,32 位确认序号是 1001,带有一个 mss(Maximum Segment Size,最大报文长度)选项值为 1024
-
建立连接(三次握手)的过程
- 客户端发出段 1,SYN 位表示连接请求,序号是 1000,这个序号在网络通讯中用作临时的地址,每发一个数据字节,这个序号要加 1,这样在接收端可以根据序号排出数据包的正确顺序,也可以发现丢包的情况,另外规定 SYN 位和 FIN 位也要占一个序号,这次虽然没发数据,但是由于发了 SYN 位,因此下次再发送应该用序号 1001。mss 表示最大段尺寸,如果一个段太大,封装成帧后超过了链路层的最大帧长度,就必须在 IP 层分片,为了避免这种情况,客户端声明自己的最大段尺寸,建议服务器端发来的段不要超过这个长度
- 服务器发出段 2,也带有 SYN 位,序号是 8000,同时置 ACK 位表示确认,确认序号是 1001,表示 “我接收到序号 1000 及其以前所有的段,请你下次发送序号为 1001 的段”,也就是应答了客户端的连接请求,同时也给客户端发出一个连接请求,同时声明最大段尺寸为 1024
- 客户端发出段 3,对服务器的连接请求进行应答,确认序号是 8001。这个过程中,客户端和服务器分别给对方发了连接请求,也应答了对方的连接请求,其中服务器的请求和应答在一个段中发出,因此一共有三个段用于建立连接,称为 “三次握手”。在建立连接的同时,双方协商了一些信息,例如双方发送序号的初始值、最大段尺寸等
在 TCP 通讯中,如果一方收到另一方发来的段,读出其中的目的端口号,发现本机并没有任何进程使用这个端口,就会应答一个包含 RST 位的段给另一方。例如,服务器并没有任何进程使用 8080 端口,却用 telnet 客户端去连接它,服务器收到客户端发来的 SYN 段就会应答一个 RST 段,客户端的 telnet 程序收到 RST 段后报告错误
-
数据传输的过程
- 客户端发出段 4,包含从序号 1001 开始的 20 个字节数据,确认序号是 8001
- 服务器发出段 5,确认序号为 1021,对序号为 1001-1020 的数据表示确认收到,同时请求发送序号 1021 开始的数据,服务器在应答的同时也向客户端发送从序号 8001 开始的 10 个字节数据
- 客户端发出段 6,对服务器发来的序号为 8001-8010 的数据表示确认收到,请求发送序号 8011 开始的数据
-
关闭连接(四次握手)的过程
- 客户端发出段 7,FIN 位表示关闭连接的请求,序号是 1021,确认序号是 8011
- 服务器发出段 8,应答客户端的关闭连接请求,确认序号是 1022(FIN 位也要占一个序号)
- 服务器发出段 9,其中也包含 FIN 位,向客户端发送关闭连接请求,序号是 8011,确认序号是 1022
- 客户端发出段 10,应答服务器的关闭连接请求,确认序号是 8012(FIN 位也要占一个序号)
6.2 滑动窗口:TCP 流量控制
如果发送端发送的速度较快,接收端接收到数据后处理的速度较慢,而接收缓冲区的大小是固定的,就会丢失数据,TCP 协议通过 “滑动窗口(Sliding Window)”机制解决这一问题
- 下图在接收端用小方块表示 1K 数据,实心的小方块表示已接收到的数据,虚线框表示接收缓冲区,因此套在虚线框中的空心小方块表示窗口大小,从图中可以看出,随着应用程序提走数据,虚线框是向右滑动的,因此称为滑动窗口
- 滑动窗口具体调控流程
- 1、发送端发起连接,声明最大段尺寸是 1460,初始序号是 0,窗口大小是 4K(4096),表示“我的接收缓冲区还有 4K 字节空闲,你发的数据不要超过 4K”。接收端应答连接请求,声明最大段尺寸是 1024,初始序号是 8000,窗口大小是 6K,发送端应答,三方握手结束
- 2、发送端发出段 4-9,每个段带 1K 的数据,发送端根据窗口大小知道接收端的缓冲区满了,因此停止发送数据
- 3、接收端的应用程序提走 2K 数据,接收缓冲区又有了 2K 空闲,接收端发出段 10,在应答已收到 6K 数据的同时声明窗口大小为 2K
- 4、接收端的应用程序又提走 2K 数据,接收缓冲区有 4K 空闲,接收端发出段 11,重新声明窗口大小为 4K
- 5、发送端发出段 12-13,每个段带 2K 数据,段 13 同时还包含 FIN 位
- 6、接收端应答接收到的 2K 数据(6145-8192),再加上 FIN 位占一个序号 8193,因此应答序号是 8194,连接处于半关闭状态,接收端同时声明窗口大小为 2K
- 7、接收端的应用程序提走 2K 数据,接收端重新声明窗口大小为 4K
- 8、接收端的应用程序提走剩下的 2K 数据,接收缓冲区全空,接收端重新声明窗口大小为 6K
- 9、接收端的应用程序在提走全部数据后,决定关闭连接,发出段 17 包含 FIN 位,发送端应答,连接完全关闭
6.3 TCP 状态转换
- Linux网络编程(一):网络基础(上)
只有主动关闭连接的一方需要等待 2MSL 时长才能结束
6.4 半关闭
为什么 TCP 关闭连接需要四次挥手?
- 因为存在半关闭状态
- 当 TCP 链接中 A 发送 FIN 请求关闭(A 端进入 FIN_WAIT_1 状态),B 端回应 ACK 后(A 端进入 FIN_WAIT_2 状态),B 没有立即发送 FIN 给 A 时,此时 A 可以接收 B 发送的数据,但是 A 已不能再向 B 发送数据,此时称 A 方处在半链接(关闭)状态
- 从程序的角度,可以使用 API 来控制实现半连接状态
- 使用 close 中止一个连接,但它只是减少描述符的引用计数,并不直接关闭连接,只有当描述符的引用计数为 0 时才关闭连接
- shutdown 不考虑描述符的引用计数,直接关闭描述符,也可选择中止一个方向的连接,只中止读或只中止写
#include <sys/socket.h> /* sockfd: 需要关闭的 socket 的描述符 how: 允许为 shutdown 操作选择以下几种方式: SHUT_RD(0):关闭 sockfd 上的读功能。此选项将不允许 sockfd 进行读操作 该套接字不再接受数据,任何当前在套接字接受缓冲区的数据将被无声的丢弃掉 SHUT_WR(1): 关闭 sockfd 的写功能。此选项将不允许 sockfd 进行写操作,进程不能在对此套接字发出写操作 SHUT_RDWR(2): 关闭 sockfd 的读写功能。相当于调用 shutdown 两次:首先是以 SHUT_RD,然后 SHUT_WR */ int shutdown(int sockfd, int how);
如果有多个进程共享一个套接字,close 每被调用一次,计数减 1,直到计数为 0 时,也就是所用进程都调用了 close,套接字将被释放;在多进程中,如果一个进程调用 shutdown(sfd, SHUT_RDWR) 后,其它的进程将无法进行通信,但如果一个进程 close(sfd) 将不会影响到其它进程
6.5 2MSL
- 2MSL(Maximum Segment Lifetime,2 倍的最大分段生存期时间)是 TCP 协议中的一个参数,它指的是一个分段在网络中最长的生存时间,MSL 的具体取值通常是由操作系统决定的,并且通常在几十秒到几分钟之间
- 探索TCP的2MSL:网络通信中的重要时间参数