一直以来感觉学的都很浮躁,所以最近开始补基础知识。重新看了下TCP/IP协议 下面都是自己的一些理解吧 整理下方便以后复习。
内容大多来自《Linux高性能服务器编程》一书,书已经停刊。
某宝上找了个店家印了个复印版的!!!质量还算满意 当然肯定比不了原装书 莫强求
TCP: Transmission Control Protocol 传输控制协议
IP: Internet Protocol 因特网协议
TCP/IP协议族是一个四层协议系统 从上而下分别是
- 应用层
- 传输层
- 网络层
- 数据链路层
最上面的一个应用层是在用户空间的
下面3层是在内核空间的
数据链路层
数据链路层主要实现了网络驱动程序,以处理数据在物理媒介(主要有 以太网 令牌环等)上的传输.
数据链路层主要有2个主要的协议 分别是 ARP(Address Resolve Protocol) 地址解析协议
和 RARP(Reserve Address Resolve Protocol) 逆地址解析协议
他们主要实现了IP地址和主机地址(通常是MAC地址 以太网等)之间的互相转换
网络层
网络层主要实现了数据包的选路和转发。因为在WAN(Wide Area NetWork 广域网)中2台主机通常不是直接连接的,而是通过N多的分散的中间节点(也可以叫路由器吧)来连接的。那么网络层的作用就是在这些中间节点中选择合适的 路径 来确定2台主机之间的通信连接
网络层最核心的协议就是 IP(因特网协议)
。IP协议根据数据包的目的IP地址来决定如何投递它。如果数据包不能直接发送给目标主机,IP协议就会为它寻找一个适合的下一跳路由器 并把数据包都交付给该路由器来转发。就这样重复 直到数据包到达目标主机
网络层另外一个重要的协议就是 ICMP(Internet Control Message Protocol) 因特网控制报文协议
他是IP协议的补充 主要用来检测网络连接 应用层的 ping 其实就是使用了ICMP协议
传输层
传输层主要用来为2台主机上的应用程序提供端到端的通信,它只关心通信的起始端和目的端,而不关心中间的数据包转发过程。
传输层的主要协议有三个
- TCP(Transmission Control Protocol)协议 传输控制协议
- UDP(User Datagram Protocol)协议 用户数据报协议
- SCTP(Stream Control Transmission Protocol)协议 流控制传输协议
TCP协议为应用层提供 可靠的 、面向连接 、基于流(stream) 的服务。它使用 超时重传、数据确认 等方式来确保数据被正确的发送到目的端,因为TCP服务是可靠的。因为他的输出是基于流的 所以的传输长度是没有限制的
UDP协议与TCP协议相反,它提供 不可靠的、无连接、基于数据报 的服务。它无法保证数据正确的传送到目的端,如果数据在中途丢失,或者目的端通过校验发现数据错误而将其丢弃,则UDP协议只是简单的通知应用程序发送失败而已。因为他是无连接的 所以通信双发无法保持一个长久的连接
应用层
应用层主要用来处理应用程序的逻辑。一般都是放在用户空间来实现的
应用层的协议有很多 下面简单列出几个
- telnet协议 远程登录协议
- OSPF(Open Shortest Path First) 开放最短路径优先协议 动态路由更新协议
应用层协议(也可能是应用程序)可能跳过传输层直接使用网络层提供的服务。
数据传递
封装
上层协议是如何使用下层协议的服务,主要是通过封装(Encapsulate)来实现的。应用程序的数据在发送到物理网络上之前,将沿着协议栈从上往下依次传递,每一层协议都讲在上一层协议的基础上加上自己的头部信息,以实现该层的功能 这个过程就是封装。如下图
首先应用程序发送数据 会先给传输层之间建立一个TCP连接,
当应用程序使用 send
或者 write
函数向TCP连接写入数据时,
内核中的TCP模块会先将这些数据复制到与该连接对应的 TCP内核发送缓冲区 中,
然后带上自己的 TCP头部信息 组成 TCP报文段
之后调用网络层的 IP 模块的服务把数据传输给网络层。
这里还需要补充一个UDP的封装 经过UDP封装后的数据称为 UDP数据报 它与TCP类似 只是他不需要为数据保存副本 因为他提供的服务是不可靠 当他在把UDP数据报发送成功后 他就把UDP内核缓冲区中的该数据给删除了
数据通过IP协议来到网络层后,经过IP的封装后的数据称为 IP数据报 (其实他也是拿了上一层的数据拼上自己的头信息,这里上一层的数据也就是上面的TCP报文段或者UDP数据报了)。最后IP把IP数据报通过以太网驱动程序等(也可以是别的 具体还有啥我理解的不是很详细了~)把数据传递给了数据链路层
数据链路层封装的数据称为 帧(frame) 其实它也是在上一层数据的基础上拼上了自己的头部信息或者也加上尾部信息。根据传输媒介的不同,帧的类型也不同。比如以太网传输的就是以太网帧,令牌环上的传输就是令牌环帧。帧的最大传输单元 最多能携带的上层协议数据 通常受网络类型的限制 比如以太网帧是最大数据量是1500字节 所以过长的IP数据报可能会被分片(fragment)传输
帧才是最终在物理网络上传送的字节序列,至此封装过程完成
分用
当帧到达目标机的时候,将沿着协议栈从底而上依次传递。各层协议依次处理本层负责的头部信息,获取自己需要的数据,并最终将处理后的帧交给应用目标,这个过程叫 分用。分用是依靠头部信息中的类型字段实现的 下面举例一个以太网帧的 分用过程
因为IP协议、ARP协议和RARP协议都使用帧传输数据,帧的头部提供一个2个字节的类型字段来标识上层协议。同样的因为ICMP协议、TCP协议和UDP协议都使用IP协议 所以IP数据报的头部采用16位的协议字段来区分他们。TCP的报文段和UDP数据报则通过其头部的16位端口号来区分上层应用程序 比如 DNS协议对应的端口是53,HTTP协议对用的端口是80 所有这些知名的应用层协议使用的端口都可以在 etc/services
文件中找到
ARP协议的工作原理
ARP协议能实现任意网络层地址到任意物理地址的转换(一般来说是讲ip地址到MAC地址的转换)。
工作原理:主机向自己所在网络广播一个ARP请求,该请求包含目标机器的网络地址,在此网络上(一般是LAN的局域网吧)的其他机器都会收到这个请求,但是只有被请求的主机才会回应一个ARP应答,其中带上自己的物理地址。
ARP请求和应答的报文详细内容如下图
从上面可以看到一个ARP请求/应答的报文长度为28字节 如果在加上帧的头部和尾部的18字节(上图以太网帧的目的物理地址6字节、源物理地址6字节、类型2字节、CRC4字节)则一个携带ARP请求/应答的以太网帧长度为46字节。不过有些实现要求以太网帧的 数据部分 长度至少要46字节(对应这个例子也就是ARP请求的28个字节长度要填充到46字节 因为他正对应的是帧的数据部分),这种情况下一个携带ARP请求的以太网帧的长度为64字节(46+18)
ARP协议的通信过程
可以使用 tcpdump
命令来进行抓包(对了系统一般不会有这个工具的 需要自己安装) 我在本地搭建了两个虚拟机 一个是Centos系统 ip地址 192.168.234.129
一个是Minix3系统 ip地址是 192.168.234.130
下面来抓包下ARP的通信过程.
因为我是用ip 129的那台机器来请求ip 130的机器 执行步骤如下
先获取以太网接口
$ ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.234.129 netmask 255.255.255.0 broadcast 192.168.234.255
inet6 fe80::20c:29ff:fe6b:c7b1 prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:6b:c7:b1 txqueuelen 1000 (Ethernet)....
上面的这个 ens33 就是了 下面要用到这个
$ arp -d 192.168.234.130 #这里是清除ARP高速缓存中的192.168.234.130对应的项
$ tcpdump -i ens33 -ent '(dst 192.168.234.130 and src 192.168.234.129)or(dst 192.168.234.129 and src 192.168.234.130)'
然后tcpdump就开始监听工作了,这时我们切换另外一个窗口使用telnet
来连接130的主机
$ telnet 192.168.234.130
不用管结果如何,换回tcpdump的窗口我们可以看到监听的信息 大概内容如下
00:0c:29:6b:c7:b1 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.168.234.130 tell 192.168.234.129, length 28
00:0c:29:e4:45:3a > 00:0c:29:6b:c7:b1, ethertype ARP (0x0806), length 60: Reply 192.168.234.130 is-at 00:0c:29:e4:45:3a, length 46
下面来分析数据包
第一个数据包中 ARP通信的源物理地址是 00
29
c7:b1 (也就是那台ip 129的Centos),目标端的物理地址是 ff
ff
ff:ff 这个ff是我瞎写的 真实的不是这样 这个是以太网的广播地址 用来标识整个LAN,该LAN上所有的机器都会收到这个ARP请求并处理这样的帧。
数值 0x0806 表示以太网帧头部类型字段的值,他表示 分用 的目标是ARP模块。
该以太网帧的长度为42(实际上是46 因为tcpdump未统计以太网帧最后的4字节CRC字段),其中数据长度为28字节 Request 表示这是一个ARP的请求(相对的还有应答) who-has 192.168.234.130 tell 192.168.234.129
则表示谁是130的主机请回应给129的主机
第二个数据包中 ARP通信的源物理地址是 00
29
45:3a (也就是那台ip 130的Minix),目标端的物理地址是 00
29
c7:b1(centos的主机) Reply 表示他是一个应答,192.168.234.130 is-at 00
29
45:3a
则表示目标主机在应答他的物理地址
注意:ARP请求/应答都是从以太网驱动程序发出 并不是从ARP模块直接发送到以太网上
总结:首先从telnet客户程序发送数据到129主机的以太网驱动程序,然后以129主机的太网驱动程序发送ARP请求给LAN内的所有的主机(包括路由器),然后LAN上的所有主机的以太网驱动程序接收到ARP请求并解析,如果发现是请求自己的,则目标主机(130主机)的以太网驱动程序会发送一个ARP的应答 回复给129主机的以太网驱动程序 从而拿到130主机的物理地址 实现网络地址到物理地址的转换
DNS协议
比如访问因特网上的网站我们通常是输入域名来访问而不是输入IP地址,这中间就需要域名查询服务(比如NIS、DNS、本地静态文件等等)
DNS是一套分布式的域名服务系统
每个DNS服务器上存储着大量的机器域名和IP地址的映射
下面来抓包一个DNS通信过程
首先我们使用 host
命令,它使用DNS协议和DNS服务器通信 一般在Linux系统下 etc/resolv.conf
文件中会存放DNS服务器的IP地址 自己可以看一下
$ host -t A www.baidu.com
这个是host命令的一个简单用法 -t
参数表示使用哪种查询类型 参数A
表示 通过机器的域名获取其IP地址(CNAME 表示获取目标主机的别名 PTR表示反向查询)
开始测试 我们还是使用 tcpdump
命令来抓包
$ tcpdump -x -i ens33 -nt -s 500 port domain
注意 上面的ens33 还是以太网接口名 后面多加了 prot domain
参数表示只抓取使用域名服务的数据包也就是DNS查询和应答报文了。然后我们新建一个窗口来执行host命令
$ host -t A www.baidu.com
然后会得到大概如下的数据
IP 192.168.234.129.50085 > 192.168.234.233.53: 7657+ A? www.baidu.com. (31)0x0000: 4500 003b ab56 0000 4011 7986 c0a8 ea81
0x0010: c0a8 ea02 c3a5 0035 0027 560e 1de9 01000x0020: 0001 0000 0000 0000 0377 7777 0562 61690x0030: 6475 0363 6f6d 0000 0100 01
IP 192.168.234.233.53 > 192.168.234.129.50085: 7657 2/0/0 A 14.215.177.39, A 14.215.177.38 (63)0x0000: 4500 005b 001f 0000 8011 e49d c0a8 ea02
0x0010: c0a8 ea81 0035 c3a5 0047 5f43 1de9 81800x0020: 0001 0002 0000 0000 0377 7777 0562 61690x0030: 6475 0363 6f6d 0000 0100 01c0 0c00 01000x0040: 0100 0000 0500 040e d7b1 27c0 0c00 01000x0050: 0100 0000 0500 040e d7b1 26
下面来分析这个报文
首先2个数据包开头都是 IP 表示他描述的内容是一个 IP数据报 然后后面的 >
表示数据传输方向 在它前面的是源端 后面的是目的端,所以第一个数据包是从IP地址为 192.168.234.129 的主机向首选DNS主机 192.168.234.233 发送的DNS查询报文 端口是53(不用纠结这个dns-ip地址 一般都是上面那个 etc/resolv.conf文件中的,另外dns服务使用的端口就是53)。
在后面的 7657 表示DNS查询报文的标识值 因为在应答报文中也会有这个标识值 用来把一个DNS请求和一个DNS应答对应上 不然怎么找的到哪个是哪个的请求和回复。
在后面的 + 表示启用递归查询标志,因为如果首选DNS服务器没有查找到相应的域名和IP映射关系 将继续到下一个DNS服务器上查找以此类推
在后面的 A? 表示A类型的查询方式也就是通过域名查找IP地址喽
在后面的 www.baidu.com 就是DNS查询问题中的 查询名
最后的 31 表示DNS查询报文的长度是31字节
同理第二个数据包内容就是一个DNS应答报文了 表示从DNS服务器233请求到129的主机 端口号53 后面的 7657 和上面的129请求标识对应 表示这是一对
第二个数据包中的 2/0/0 表示该报文中包含了 2个应答资源记录、0个授权资源记录、0个额外信息记录
A 表示其后面的是IP地址 然后他有2个IP地址 应答报文长度为63字节
Socket简单科普
socket是一套通用网络编程接口 它不但可以访问内核中的 TCP/IP 协议栈 而且也可以访问其他网络协议栈
上面提到的 数据链路层 网络层 传输层协议 都是在内核中实现的。因此操作系统需要实现一组系统调用用来使应用程序能够访问这些协议提供的服务。实现这组系统调用的API主要有2套 一个是 Socket
一个是 XTI
socket主要提供2点功能
- 将应用程序的数据从用户缓冲区复制到TCP/UDP内核发送缓冲区 用来把数据交付给内核。比如
send
函数 或者就是把数据从内核TCP/UDP的缓冲区复制到用户缓冲区 用来读取数据 - 应用程序可以通过他们来修改内核中各层协议的某些头部信息或其数据结构 从而控制底层通信的行为