第3章TCP/IP协议簇
3.1TCP/IP协议簇介绍
TCP(Transmission Control Protocol)/IP(Internet Protocol)协议是现代互联网的基石,其实TCP/IP代表一组协议,称为TCP/IP协议簇,TCP和IP只是其中的两个最重要的协议。TCP/IP协议簇使得由异构硬件和软件系统组成的主机进行互联,从而构建起全球互联网大厦。
由于主机间进行通信的过程十分复杂,因此,经常将主机间通信的过程划分为相对独立的不同层次,每层实现相对独立的一定功能,上层通过标准接口调用下层提供的功能而不关心具体的细节,以简化主机间通信协议的设计。ISO(International Standardization Organization)推出的OSI(Open System Interconnect Reference Model)协议是一种参考协议,分为七层,从下到上分别为物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。TCP/IP协议簇参考ISO/OSI实现。
TCP/IP协议中的TCP协议最早由斯坦福大学的两名研究人员于1973年提出,为实现主机间通过TCP通信,逐渐衍生出其他协议,最终形成TCP/IP协议族。1983年TCP/IP协议簇被UNIX 4.2BSD系统采用,随着UNIX的成功,TCP/IP协议簇逐步成为UNIX主机间通信的标准网络协议。Internet的前身ARPANET最初使用NCP(Network Control Protocol)协议,后来由于看到TCP/IP协议簇具有跨平台特性,于是ARPANET的实验人员对TCP/IP协议簇进行改进后采用,并规定连入ARPANET的计算机必须采用TCP/IP协议簇。
图31TCP/IP协议簇结构
随着ARPANET逐渐发展成为Internet,TCP/IP协议簇也就成了Internet的标准连接协议。
TCP/IP协议簇由四层构成,各层的名称与所包含的协议如图31所示,其中的应用层与链路层包含的协议众多,而传输层和网络层包含的协议较少。
3.2链路层
链路层也称为数据链路层或网络接口层,该层包括主机用于连接网络的网络接口卡及其驱动程序,主要处理与传输媒介(如双绞线、光纤、无线电波等)的物理接口细节。由于绝大部分主机使用Ethernet网卡接入网络,因此,链路层所使用的通信协议一般为Ethernet。
链路层处理的数据为数据帧,格式如图32所示,其中,目标MAC(Media Access Control)地址、源MAC地址和类型组成14(6+6+2)字节的帧头,后面为数据部分,最后为CRC(Cyclic Redundancy Check)部分。
图32链路层数据帧格式
在图32中,MAC地址为主机网络接口卡地址; 类型为来自网络层的数据类型,IPv4为0x0800,ARP为0x0806,PPPoE为0x8864,802.1Q tag为0x8100,IPv6为0x86DD,MPLS Label为0x8847; 数据部分为来自网络层的数据,最少为46字节,最大为1500字节; CRC为循环冗余校验码,主要校验所收到的数据是否有错。链路层数据帧最小为64(6+6+2+46+4)字节,最大为1518(6+6+2+1500+4)字节,主要利用网络接口卡的物理地址即MAC地址进行通信。
主机作为数据发送方时,链路层负责将来自本机网络层的数据报文封装为数据帧进行发送,数据接收方在收到数据帧后会给数据发送方发送反馈信息,如果数据传输有误,发送方需要重新发送出错的帧数据; 主机作为数据接收方时,链路层负责对接收到的数据帧进行CRC校验,并给数据发送方发送反馈信息,要求重新发送出错的帧数据,并将接收到的正确帧数据的目标MAC地址、源MAC地址和CRC部分去掉后,递交给网络层处理。
链路层通信用MAC地址识别主机,主机间交换数据帧。
代码31可以获取本机网卡的MAC地址。
1#代码31link01.py
2#!/usr/bin/env python3
3# coding: utf-8
4import uuid
. 5node = uuid.uuid1()
6print('type(node)=',type(node))
7print('node=',node)
8hex = node.hex
9print('hex=',hex)
10mac_addr = hex[-12:]
11print('mac_addr=',mac_addr)
代码31第4行引入uuid,uuid实现UUID(Universally Unique Identifier),即全局唯一标识符。第5行通过调用uuid的uuid1()函数,得到由MAC地址、当前时间戳和随机数字生成,以对象node表示的UUID值。第8行通过对象node的hex属性得到包含主机MAC地址的字符串,第10行取字符串的后12位,即为主机的MAC地址。程序运行结果如图33所示。
type(node)=
node=997041ae-937a-11e7-a2db-000c294be4dd
hex=997041ae937a11e7a2db000c294be4dd
mac_addr=000c294be4dd
图33代码31运行结果
Python的第三方模块psutil可以获取主机的大量信息,其中包括主机全部网卡的设备名称和MAC地址,在命令行下在线安装psutil的步骤如下。
sudo apt-get install python3-pip
pip3 install psutil
其中,命令“sudo aptget install python3pip”用于安装pip工具,pip是Python软件包管理工具,此处安装的pip版本号为3; 命令“pip3 install psutil”使用pip工具安装psutil模块。
利用psutil获取主机网卡设备名称和MAC地址程序如代码32所示。
1#代码32link02.py
2#!/usr/bin/env python3
3# coding: utf-8
4import psutil
5info=psutil.net_if_addrs()
6print('info=',info)
7print('type(info)=',type(info))
8net1=info['eth0']
9print('net1=',net1)
10print('type(net1)=',type(net1))
11packet=net1[2]
12print('packet=',packet)
13print('type(packet)=',type(packet))
14print('mac_addr=',packet.address)
代码32第4行引入了psutil,调用psutil的net_if_addrs()函数获取了主机全部的网卡信息,然后从获取的信息中逐步得到网卡设备名称和MAC地址,程序运行结果如图34所示。
如图34所示,主机全部网卡的信息保存在字典型变量info中,其中包含的网卡设备名称分别为eth0和lo; 网卡eth0的信息保存在列表变量net1中; 从net1中取得的网卡物理信息保存在对象packet中,最后从packet的属性address中取得网卡的MAC地址。
注: 有些第三方模块,例如,psutil,安装后,只能由系统默认安装的Python调用,此时,应将系统默认安装的Python设置为最高优先级,在命令行下执行调用psutil模块的程序,在Atom集成环境执行这些程序会抛出异常。
info= {'eth0': [snic(family=, address='192.168.3.17', netmask='255.255.255.0', broadcast='192.168.3.255', ptp=None), snic(family=, address='fe80∷d669:5643:a4a5:e4b4%eth0', netmask='ffff:ffff:ffff:ffff∷', broadcast=None, ptp=None), snic(family=, address='00:0c:29:4b:e4:dd', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)], 'lo': [snic(family=, address='127.0.0.1', netmask='255.0.0.0', broadcast=None, ptp=None), snic(family=, address='∷1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None), snic(family=, address='00:00:00:00:00:00', netmask=None, broadcast=None, ptp=None)]}
type(info)=
net1= [snic(family=, address='192.168.3.17', netmask='255.255.255.0', broadcast='192.168.3.255', ptp=None), snic(family=, address='fe80∷d669:5643:a4a5:e4b4%eth0', netmask='ffff:ffff:ffff:ffff∷', broadcast=None, ptp=None), snic(family=, address='00:0c:29:4b:e4:dd', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]
type(net1)=
packet= snic(family=, address='00:0c:29:4b:e4:dd', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)
type(packet)=
mac_addr= 00:0c:29:4b:e4:dd