天天看点

python网络编程linux_Python网络编程(Linux)

第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协议簇。

图31TCP/IP协议簇结构

随着ARPANET逐渐发展成为Internet,TCP/IP协议簇也就成了Internet的标准连接协议。

TCP/IP协议簇由四层构成,各层的名称与所包含的协议如图31所示,其中的应用层与链路层包含的协议众多,而传输层和网络层包含的协议较少。

3.2链路层

链路层也称为数据链路层或网络接口层,该层包括主机用于连接网络的网络接口卡及其驱动程序,主要处理与传输媒介(如双绞线、光纤、无线电波等)的物理接口细节。由于绝大部分主机使用Ethernet网卡接入网络,因此,链路层所使用的通信协议一般为Ethernet。

链路层处理的数据为数据帧,格式如图32所示,其中,目标MAC(Media Access Control)地址、源MAC地址和类型组成14(6+6+2)字节的帧头,后面为数据部分,最后为CRC(Cyclic Redundancy Check)部分。

图32链路层数据帧格式

在图32中,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地址识别主机,主机间交换数据帧。

代码31可以获取本机网卡的MAC地址。

1#代码31link01.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)

代码31第4行引入uuid,uuid实现UUID(Universally Unique Identifier),即全局唯一标识符。第5行通过调用uuid的uuid1()函数,得到由MAC地址、当前时间戳和随机数字生成,以对象node表示的UUID值。第8行通过对象node的hex属性得到包含主机MAC地址的字符串,第10行取字符串的后12位,即为主机的MAC地址。程序运行结果如图33所示。

type(node)=

node=997041ae-937a-11e7-a2db-000c294be4dd

hex=997041ae937a11e7a2db000c294be4dd

mac_addr=000c294be4dd

图33代码31运行结果

Python的第三方模块psutil可以获取主机的大量信息,其中包括主机全部网卡的设备名称和MAC地址,在命令行下在线安装psutil的步骤如下。

sudo apt-get install python3-pip

pip3 install psutil

其中,命令“sudo aptget install python3pip”用于安装pip工具,pip是Python软件包管理工具,此处安装的pip版本号为3; 命令“pip3 install psutil”使用pip工具安装psutil模块。

利用psutil获取主机网卡设备名称和MAC地址程序如代码32所示。

1#代码32link02.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)

代码32第4行引入了psutil,调用psutil的net_if_addrs()函数获取了主机全部的网卡信息,然后从获取的信息中逐步得到网卡设备名称和MAC地址,程序运行结果如图34所示。

如图34所示,主机全部网卡的信息保存在字典型变量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