欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

计算机网络基础知识总结(python实现)

程序员文章站 2022-07-01 17:26:46
...

OSI模型(七层)

计算机网络基础知识总结(python实现)

TCP/IP 4层模型

计算机网络基础知识总结(python实现)

一、链路层

  • 概念:链路层也称为数据链路层或网络接口层,该层包括主机用于链接网络的网络接口卡及其驱动程序。
  • 功能:主要处理传输媒介(如双绞线、光纤、无线电波等)的物理接口细节。
  • 通信协议:一般为Ethernet
  • 处理数据:数据帧
  • 两个重要设备名称:网桥和交换机。

其中,目标MAC(Media Access Control)地址、源MAC地址和类型组成14(6+6+2)字节的帧头,后面为数据部分,最后为CRC(Cyclic Redundancy Check)部分。

目标MAC地址(6B) 源MAC地址 (6B) 类型 (2B) 数据 (46~1500B) CRC (4B)

MAC地址为主机网络接口卡地址;类型为来自网络层的数据类型,IPv4为0x0800,ARP为0x0806,PPPoE为0x8864, 802.1Q tag为0x8100,IPv6为0x86DD, MPLSLabel为0x8847;数据部分为来自网络层的数据,最少为46字节,最大为1500字节;CRC为循环冗余校验码,主要校验所收到的数据是否有错。链路层数据帧最小为64(6+6+2+46+4)字节,最大为1518(6+6+2+1500+4)字节,主要利用网络接口卡的物理地址即MAC地址进行通信。

主机作为数据发送方时,链路层负责将来自本机网络层的数据报文封装为数据帧进行发送,数据接收方在收到数据帧后会给数据发送方发送反馈信息,如果数据传输有误,发送方需要重新发送出错的帧数据;主机作为数据接收方时,链路层负责对接收到的数据帧进行CRC校验,并给数据发送方发送反馈信息,要求重新发送出错的帧数据,并将接收到的正确帧数据的目标MAC地址、源MAC地址和CRC部分去掉后,递交给网络层处理。链路层通信用MAC地址识别主机,主机间交换数据帧。

""" 
@author:oxff 
@github:https://github.com/oxff644 
"""
#获取本机网卡的MAC地址 
import uuid node =uuid.uuid1()#通过调用uuid的uuid1()函数,得到由MAC地址、当前时间戳和随机数字生成,以对象node表示的UUID值 
print('type(node) =',type(node)) 
print('node= ',node)
hex =node.hex#通过对象node的hex属性得到包含主机MAC地址的字符串print('hex=',hex) mac_addr =hex[-12:]#取字符串的后12位,即为主机的MAC地址 print('mac_addr=',mac_addr)

Python的第三方模块psutil可以获取主机的大量信息,其中包括主机全部网卡的设备名称和MAC地址。举个例子:

"""
@author:oxff
@github:https://github.com/oxff644
"""
#利用psutil获取主机网卡设备名称和MAC地址程序
import psutil
info =psutil.net_if_addrs()#调用psutil的net_if_addrs()函数获取主机全部的网卡信息
print('info= ',info)
print('type(info)=',type(info))
net1 =info['eth0']
print('net1= ',net1)
packet =net1[2]
print('packet=',packet)
print('type(packet)=',type(packet))
print('mac_addr =',packet.address)#取得网卡的MAC地址

二、网络层

  • 功能:负责获取和维护主机的IP地址,给数据报文选择路由路径
  • 基本概念:
    1. IP地址:IP地址是其他协议内容的重要组成部分,是主机间进行网际互联的标识
    2. IPV4:
    * 概念:IPv4是互联网协议IP(Internet Protocol)的第4版,也是第一个被广泛使用,构成现今互联网技术的基础协议,是TCP/IP协议簇中的核心协议。
    * 基本构成:4字节即32个二进制位表示一个地址,通常用点分十进制法表示,例如202.201.32.9,其中的数字都是十进制的数字,中间用实心圆点分隔。一个IPv4地址分为网络地址和主机地址两部分,其中网络地址可以描述为202.201.32.0/24,表示网络地址部分为202.201.32.0,长度为24位,其余8位为主机部分。
    * 分类:IPv4地址根据网络地址与主机地址的不同划分方式,分为A类、B类、C类、D类与E类地址,如表计算机网络基础知识总结(python实现)
    * 特殊地址

计算机网络基础知识总结(python实现)

    *  数据报文格式
首部(20-60B) 数据(0-65516B)

其中:数据报文首部各字段如下计算机网络基础知识总结(python实现)

  • VER: IP协议的版本号。值为二进制0100时为IPv4,值为二进制0110时为IPv6。

  • IHL:数据报文首部长度。数据报文首部长度等于IHL值乘以4,例如,IHL为二进制0101时,数据报文首部长度为20(5×4)B;IHL为二进制1111时,数据报文首部长度为60(15×4)B。

  • DS:区分服务(Differentiated Services)。DS的前3位表示优先级,优先级低的数据报文在网络拥塞时会被丢弃;接下来4位分别表示最小时延、最大吞吐量、最高可靠性和最小代价,表示数据报文传送过程中的期望,该4位中最多有1位为1,共有5种组合;DS的最后1位未使用。

  • 标识:唯一标识数据报文的16位二进制数值。当数据报文总长度大于1500B时,对数据报文分片时,该值被复制到分好片的数据报文中;同理,来自链路层,具有相同标识的多个数据报文会被重新组装为一个大的数据报文。

  • 标志:表示数据报文是否可分片或者是否为最后一个分片。最高位是预留位,其值为0。当中间位为0时,表示该数据报文可以分片;当中间位为1时,表示该数据报文不可以分片。当最后的位为1时,表示该数据报文为分片数据报,且后面还有分片;当最后的位为0时,表示该数据报文已是最后一个分片。

  • 分片偏移:表示该数据报文在原数据报文中的相对位置。相对位置必须为8字节的整数倍,也就是分片偏移值乘以8。

  • 生存时间:以数据报文在网络中的剩余跳数表示数据报文在网络中的寿命。数据报文到达某个路由器时,在路由器转发该数据报文之前,先将生存时间减1,如果生存时间变为0,则丢弃该数据报文;否则,进行转发。数据报文的生存时间最大值为255,表示数据报文在网络中最多被转发255次,如果数据报文的生存时间初始值为1,则该数据报文只能在本地局域网内部传输。

  • 协议:指出该数据报文内容所属的协议。值为1时,来自ICMP(Internet Control MessageProtocol);值为2时,来自IGMP(Internet Group Management Protocol);值为6时,来自TCP(Transmission Control Protocol);值为17时,来自UDP(User DatagramProtocol);值为89时,来自OSPF(Open Shortest Path First)协议。

  • 首部校验和:该数据报文的首部校验和,未包括数据部分。因为数据报文每经过一个路由器,首部的一些内容会发生变化,如生存时间,这就需要路由器重新计算首部校验和,如果包括了数据部分,则会加大计算量。

  • 源IP地址:发送数据的主机的IPv4地址。

  • 目标IP地址:接收数据的主机的IPv4地址。

  • 选项:可选数据。

    3.IPV6:

    • 概念:IPv6是互联网协议IP(Internet Protocol)的第6版,使用16字节也就是128个二进制位表示1个地址,由于IPv6地址长度4倍于IPv4,因此,IPv4所采用的点分十进制格式表示地址的方式不再适用。
    • 表示方法:
    1. 	冒分十六进制表示法
    2. 	0位压缩表示法
    3. 	内嵌IPv4地址表示法
    
    • 组成:IPv6数据报文由报文头部、扩展报文头部和数据三部分组成,其中报文头部长度固定为40字节,扩展报文头部和数据部分长度可变。
      | 报文头部(40B) | 扩展报文头部(长度可变) | 数据(长度可变) |
      |----|----|----|
      4.网络层协议:
ARP(地址解析协议) 用于将网络层的IP地址对应到链路层的网卡MAC地址
RARP(反向地址解析协议) 用于局域网中的主机向网关的ARP表或者缓存请求IP地址
ICMP(Internet控制报文协议) 用于主机与路由器之间传递控制消息,控制消息包括网络是否通畅、数据是否到达目标主机、路由是否可用等内容
IGMP(Internet组管理协议) 用于管理组播组成员的加入和离开,主机通过IGMP通知组播路由器希望接收或离开某个特定组播组的信息,组播路由器通过IGMP周期性地查询组播组成员是否处于活动状态,实现所连网段组成员关系的收集与维护。

Python的第三方模块psutil可以获取计算机IP地址及其他网络配置信息,举个例子:

"""
@author:oxff
@github:https://github.com/oxff644
"""
#获取计算机IP地址
import psutil
info =psutil.net_if_addrs()
print('info =',info)
net1 =info['eth0']
net2 =info['lo']
print('net1=',net1,end=' ')
print('net2=',net2,end=' ')
print('net1[0]=',net1[0])
print('net1[1]=',net1[1])
print('IPv4_addr=',net1[0].address)
print('IPv6_addr=',net1[1].address)
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
#获取局域网网关地址
import netifaces
info =netifaces.gateways()
print('info =',info)
print('type(info)=',type(info))
gateway_addr =info['default'][2][0]
print('gateway_addr=',gateway_addr)
#利用python 实现获取主机收发数据的统计信息
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
#获取局域网网关地址
import psutil
info =psutil.net_io_counters()#调用net_io_counters()函数获取主机收发数据的统计信息,统计信息存入snetio对象info中
'''
调用net_io_counters()函数时,如果加上参数pernic=True,
即info=psutil.net_io_counters(pernic=True)
将逐个网卡取得数据收发信息并存入字典变量info中。
'''
print('info=',info)
print('type(info)=',type(info))
print('bytes_sent=',info.bytes_sent)#收发数据字节数
print('bytes_recv=',info.bytes_recv)
print('packets_sent=',info.packets_sent)#收发数据报文数
print('packets_recv=',info.packets_recv)
print('errin =',info.errin)#提取出错的收发报文数
print('errout =',info.errout)
print('dropin=',info.dropin)#提取丢弃的收发数据报文shu
print('dropout=',info.dropout)
  • Socket编程
    • 网络上的主机间通过IP地址与端口号进行通信,称为Socket通信,其实,TCP/IP协议中应用层的HTTP、FTP、DNS等都是通过Socket通信实现的。
    • 在Socket通信中,提供服务的一端称为Socket服务端,调用Socket服务的一端称为Socket客户端。Socket服务端首先用自己的IP地址、指定端口号和连接方式创建服务并启动服务,等待来自客户端的连接请求;Socket客户端向服务端发起连接请求,连接请求被服务端接受后,双方就可以进行通信
    • Socket编程中要实现服务器端同时服务于多个客户端,需要用到多进程或者多线程技术,Python中的socketserver模块内置服务器端多任务处理机制,可用多进程或者多线程实现服务器端多任务的同时执行。
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
#扫描主机端口
import socket
s =socket.socket(socket.AF_INET,socket.SOCK_STREAM)#使用IPv4地址,SOCK_STREAM表示使用TCP连接
s.settimeout(0.5)#用函数settimeout()设置s操作的超时时间,因为是针对本机操作,速度很快,因此,设置为0.5s,如果针对远程主机操作,该值应该设为一个较大的值,如设置为3s
ip='192.168.0.103'
for port in range(5000,9000):
    result =s.connect_ex((ip,port))#调用函数connect_ex()连接服务器
    if result==0:#result的值是否为0,若为0,说明扫描端口处于打开状态
        print('port %d is openned!'% port)
s.close()

#!/usr/bin/env python3
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
#利用进程池扫描主机端口
from multiprocessing import Pool
import socket
import os
def scan_port(ports):
    s =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.settimeout(1)
    for port in range(ports,ports+4096):
        result =s.connect_ex((ip,port))
        if result ==0:#判断变量result值是否为0,若为0表示所测试的端口处于打开状
            print('I am process %d,port %d is openned!'%(os.getpid(),port))
    s.close()
ip ='192.168.0.103'
p =Pool(16)#用函数Pool()创建进程池,参数16表示进程池中有16个子进程
for k in range(16):
    p.apply_async(scan_port,args=(k*4096,))#以非阻塞方式给进程池中的子进程分配任务,scan_port指明子进程要执行的函数名,args=(k*4096,)以命名参数形式给出元组类型的参数,每个子进程要扫描的开始端口是k*4096
p.close()
p.join()#调用函数join()等待进程池中子进程执行结束
print("all subprocesses had finihed!!")
#!/usr/bin/env python3
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
#利用线程池扫描主机端口
import threadpool
import socket
def scan_port(num):
    s =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.settimeout(1)
    ports =num*4096
    thread_name ='thread'+str(num)
    for port in range(ports,ports+4096):
        result =s.connect_ex((ip,port))
        if result ==0:
            print('I am %s,port %d is openned!' %(thread_name,port))
    s.close()
ip ='192.168.0.104'
p=threadpool.ThreadPool(16)
num_list =list(range(16))
tasks =threadpool.makeRequests(scan_port,num_list)
for task in tasks:
    p.putRequest(task)
p.wait()
print('All threads had finished!!')
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
#探测主机是否在线的实例,实现类似ping命令的功能
import socket
import struct#用于将其他类型数据转换为bytes字节流类型
import array#用于将bytes字节流数据转换为2B有符号整型数组
import time
def checksum(packet):
    if len(packet) & 1:
        packet =packet+'\0'#判断报文数据长度是否奇数,若为奇数,则在报文末尾追加字符“\0”
    words =array.array('h',packet)#将bytes类型的报文数据转换为2B有符号的整型数组words,因为ICMP报文的校验和为16位(2B)
    sum =0
    for word in words:
        sum +=(word & 0xffff)#word&0xffff运算保证累加到sum的值为word的低16位
    sum =(sum>>16)+(sum & 0xffff)#将sum的高16位与低16位相加存入sum中
    sum =sum +(sum>>16)
    return (~sum) & 0xffff#sum进行按位取反后与0xffff进行与运算的结果,该运算保证返回值的高16位为0,低16位为sum的反码计算结果
'''
利用struct模块的pack()函数构造ICMP报文的首部,
其中,类型值为8,表示该报文为探测目标主机是否存在的回应请求报文,
代码为0,校验和为0,标识符为1234,序号为5
'''
header =struct.pack('bbHHh',8,0,0,1234,5)
data =struct.pack('d',time.time())#构造ICMP报文的数据部分,数据部分值取系统当前的时间
packet =header+data
chkSum =checksum(packet)#以packet为参数调用函数checksum,计算ICMP报文的校验和,保存在变量chkSum中
header =struct.pack('bbHHh',8,0,chkSum,1234,5)#以新的校验和chkSum重新生成ICMP报文的首部
packet =header+data
s =socket.socket(socket.AF_INET,socket.SOCK_RAW,socket.getprotobyname('icmp'))
s.settimeout(3)#设置socket对象s的超时时间为3s
ip =input('ip adress is:')
for kk in range(4):#利用循环发送探测主机的ICMP数据报文并接收应答报文
    try:
        t1 =time.time()
        s.sendto(packet,(ip,0))
        (r_data,r_addr) =s.recvfrom(1024)
        t2 =time.time()
    except Exception as e:
        print('Error is',e)
        continue
    print('Receive the respond from %s,data is %d bytes,time is %.2f ms'\
          %(r_addr[0],len(r_data),(t2-t1)*1000))
    (h1,h2,h3,h4,h5) =struct.unpack('bbHHh',r_data[20:28])#r_data[20:28]取IP报文序号20~27的字节流,这8字节的字节流为ICMP报文的首部,r_data[0:20]为IP报文的首部
    print('type =%d,code =%d,chksum =%u,Id=%u,SN=%d' %(h1,h2,h3,h4,h5))#输出ICMP报文首部的5个字段值,分别为类型、代码、校验和、标识符和序号。
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
'''
网络嗅探就是利用SOCK_RAW直接在链路层获取数据报文,
然后对报文进行分析,获取主机正在进行的网络通信信息,
例如,通信的源主机、目标主机、所使用的协议等。
网络嗅探不影响主机的正常通信.
Linux系统中使用PF_PACKET、Windows系统中使用AF_PACKET。
'''
import socket
import struct
import binascii#用于将二进制数据转换为ASCII数据
s =socket.socket(socket.PE_PACKET,socket.SOCK_RAW,socket.htons(0x0800))#表示IPv4的编号0x0800由主机顺序转换为2B的网络顺序,此行语句执行需要root用户权限
for xx in range(10):
    data =s.recvfrom(2048)
    print('\nFrame number is %d:' % xx)
    packet =data[0]
    frame_leader_b =packet[0:14]
    frame_header_s =struct.unpack("!6s6s2s",frame_leader_b)
    source_MAC_Addr =binascii.hexlify(frame_header_s[0])
    dest_MAC_Addr =binascii.hexlify(frame_header_s[1])
    proto_type =binascii.hexlify(frame_header_s[2])
    print('Source MAC address is',source_MAC_Addr)
    print('Destination MAC address is',dest_MAC_Addr)
    print('Protocol type is ',proto_type)
    ip_header_b =packet[14:34]
    ip_header_s =struct.unpack('!12s4s4s',ip_header_b)
    print("Protocol is:",ip_header_s[0][9:10])
    print("Source IP address is: "+socket.inet_ntoa(ip_header_s[2]))
    print("Destination IP address is: "+socket.inet_ntoa(ip_header_s[2]))
s.close()

三、传输层

  • 功能:主要为网络中的两台主机提供端到端的数据传输服务,作为数据发送方,传输层将来自应用层的数据进行分割并加上传输层报文头部后组装成传输层数据报文,递交给网络层处理;作为数据接收方,传输层将来自网络层的数据去掉传输层报文头部并对数据进行组装,然后将数据递交给应用层。
    

    网络中一台主机向另外一台主机发送数据的过程

计算机网络基础知识总结(python实现)

  • 传输层协议
    
协议名称 特点 功能 应用场景
TCP 面向连接,可靠 提供面向连接、可靠的数据传输服务
UDP 无连接,不对传输的数据进行可靠性保证,资源消耗小、处理速度快,实现简单 提供的服务没有连接、只能提供尽力而为的数据传输服务, 适合于一次传输少量数据和传输中偶尔出现错误对结果影响不大的服务(例如,音频、视频等服务),但对于需要传输较多数据,且数据传输出现错误需要重传的服务并不适合,使用UDP协议包括:TFTP(简单文件传输协议)、SNMP(简单网络管理协议)、DNS(域名解析协议)、NFS、BOOTP

TCP协议的三次握手和四次挥手:

计算机网络基础知识总结(python实现)

:seq:"sequance"***;ack:"acknowledge"确认号;SYN:"synchronize"请求同步标志;;ACK:“acknowledge"确认标志”;FIN:"Finally"结束标志。

  • TCP连接建立过程:首先Client端发送连接请求报文,Server段接受连接后回复ACK报文,并为这次连接分配资源。Client端接收到ACK报文后也向Server段发生ACK报文,并分配资源,这样TCP连接就建立了。

  • TCP连接断开过程:假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK,“告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息”。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。当Server端确定数据已发送完成,则向Client端发送FIN报文,“告诉Client端,好了,我这边数据发完了,准备好关闭连接了”。Client端收到FIN报文后,"就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。“,Server端收到ACK后,“就知道可以断开连接了”。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!

    为什么要三次挥手?

    在只有两次“握手”的情形下,假设Client想跟Server建立连接,但是却因为中途连接请求的数据报丢失了,故Client端不得不重新发送一遍;这个时候Server端仅收到一个连接请求,因此可以正常的建立连接。但是,有时候Client端重新发送请求不是因为数据报丢失了,而是有可能数据传输过程因为网络并发量很大在某结点被阻塞了,这种情形下Server端将先后收到2次请求,并持续等待两个Client请求向他发送数据…问题就在这里,Cient端实际上只有一次请求,而Server端却有2个响应,极端的情况可能由于Client端多次重新发送请求数据而导致Server端最后建立了N多个响应在等待,因而造成极大的资源浪费!所以,“三次握手”很有必要!

    为什么要四次挥手?

    试想一下,假如现在你是客户端你想断开跟Server的所有连接该怎么做?第一步,你自己先停止向Server端发送数据,并等待Server的回复。但事情还没有完,虽然你自身不往Server发送数据了,但是因为你们之前已经建立好平等的连接了,所以此时他也有主动权向你发送数据;故Server端还得终止主动向你发送数据,并等待你的确认。其实,说白了就是保证双方的一个合约的完整执行!

    使用TCP的协议:FTP(文件传输协议)、Telnet(远程登录协议)、SMTP(简单邮件传输协议)、POP3(和SMTP相对,用于接收邮件)、HTTP协议等。

1.多进程TCP实例

服务端代码:

#!/usr/bin/env python3
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
'''
多进程TCP实例(服务端)
要实现socketserver的多进程TCP编程
需要调用socketserver的ForkingTCPServer()函数
'''
import socketserver
def factorial(n):
    s=1
    for x in range(2,n+1):
        s =s*x
    return s
class Factorial_server(socketserver.StreamRequestHandler):
    def handle(self):
        conn =self.request
        try:
            data_b =conn.recv(1024)
            data_s =data_b.decode('utf-8')
            data =int(data_s)
            if data >1:
                fact =factorial(data)
            else:
                fact =1
            fact_s =str(fact)
            fact_b =fact_s.encode('utf-8')
            conn.send(fact_b)
        print('factorial(%d) =%s,from %s'% (data,fact_s,self.client_address[0]))
        except Exception as e:
            print('Error is ',e)
ip ='192.168.0.103'
server =socketserver.ForkingTCPServer((ip,8899),Factorial_server)
print('wait for TCO connecting...')

server.serve_forever()

客户端代码:
#!/usr/bin/env python3
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
#客户端代码
import socket
ip ='192.168.0.103'
s =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((ip,8899))
xx_s =input('Enter a number:')
xx_b =xx_s.encode('utf-8')
s.send(xx_b)
result_b =s.recv(1024)
result_s =result_b.decode('utf-8')
print('Factorial(%s) =%s' % (xx_s,result_s))
s.close()

服务器与客户端之间传输数值时,先将数值转换为字符串,再将字符串转换为bytes后传输,实现不同长度数值的传输。

2.多进程UDP实例

服务端程序

#!/usr/bin/env python3
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
'''
UDP服务器程序
要实现socketserver的多进程UDP编程,
需要调用socketserver的Forking-UDPServer()函数
'''
import socketserver
def factorial(n):
    s=1
    for x in range(2,n+1):
        s=s*x
    return s
class Factorial_server(socketserver.DatagramRequestHandler):
    def handle(self):
        try:
            (data_b,s) =self.request
            data_s =data_b.decode('utf-8')
            data =int(data_s)
            if data>1:
                fact =factorial(data)
            else:
                fact =1
            fact_s =str(fact)
            fact_b =fact_s.encode('utf-8')
            s.sendto(fact_b,self.client_address)
            print('factorial(%d)= %s,from %s' % (data,fact_s,self.client_address[0]))
        except Exception as e:
            print('Error is ',e)
ip ='192.168.0.103'
server =socketserver.ForkingUDPServer((ip,8988),Factorial_server)
print('Bins UDP on 8988....')
server.serve_forever()

客户端程序

#!/usr/bin/env python3
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
import socket
ip ='192.168.0.103'
s =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
xx_s =input('enter a number:')
xx_b =xx_s.encode('utf-8')
s.sendto(xx_b,(ip,8988))
while True:
    (result_b,s_addr) =s.recvfrom(1024)
    if s_addr[0] ==ip:
        result_s =result_b.decode('utf-8')
        print('Factorial(%s)= %s' %(xx_s,result_s))
        break
s.close()

四、应用层

  • 常用协议
  1. HTTP
  • HTTP(HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议,用于传输HTML(HyperTextMarkup Language)数据,而HTML是互联网中主机之间进行交互的标准语言。
  • HTTP规定通信双方分别为客户端和服务器端,双方以请求/应答方式工作,即客户端向服务器端发起请求,服务器端对来自客户端的请求进行应答,双方交互的数据使用可靠传输协议TCP进行传输。
  • HTTP的报文分为请求报文和应答报文
  • HTTP请求报文和HTTP应答报文两者都是HTML文本,分成多行,用CRLF表示换行,行内的字段长度没有限制,用空格作为分隔fu

利用Python3的urllib所包含的模块request可以设置HTTP请求报文首部,获取指定服务器端的HTTP响应报文,代码如下:

#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
#获取指定服务器端的HTTP响应报文
from urllib import request
url ='http://www.baidu.com'
header ={
    'Accept':'text/html',
    'Connection':'keep-alive'
}
req =request.Request(url,headers=header)
response =request.urlopen(req)
print('Status code=',response.getcode())
print('url=',response.geturl())
print('info=',response.info())

2.HTTPS

  • HTTPS使用SSL(Secure Sockets Layer)或者TLS(Transport Layer Security)对HTTP数据报文进行加密后递交给传输层的TCP进行传输,使用端口默认为443
    
  • 主机间使用HTTPS通信时,会同时使用到非对称加密算法即公/私钥加密算法、对称加密算法、HASH算法等,其中,非对称加密算法用于加密密码,对称加密算法用于加密要发送的消息和HASH值,HASH算法用于计算要发送消息的HASH值
    

3.FTP

  • FTP(File Transfer Protocol,文件传输协议)用于Internet上主机间文件的共享与传输。
    
  • FTP服务实现包括服务器端和客户端。服务器端是提供文件存储空间的计算机,经常被称为FTP服务器;客户端是通过Internet以FTP访问服务器的主机。通过Internet,从FTP服务器端复制文件至客户端,称为“下载(download)”;将客户端的文件复制到FTP服务器上,则称为“上传(upload)”。
    
  • FTP服务器端和客户端的连接需要使用两个独立的TCP连接:一个是命令链路,用来传送命令和状态数据;另一个是数据链路,用来传输数据。
    
  • FTP协议有两种工作方式:PORT和PASV,即主动式和被动式工作方式。
    

    1.PORT(主动)工作方式的过程是:客户端向服务器的FTP端口(默认是21)发送连接请求,服务器接受连接,建立一条命令链路。当需要传送数据时,客户端在命令链路上用PORT命令告诉服务器,“我打开了XX端口,你来连接我”,于是服务器利用20端口向客户端的XX端口发送连接请求,建立一条数据链路来传送数据。

    2.PASV(被动)工作方式的过程是:客户端向服务器的FTP端口(默认是21)发送连接请求,服务器接受连接,建立一条命令链路。当需要传送数据时,服务器在命令链路上用PASV命令告诉客户端,“我打开了XX端口,你来连接我”,于是客户端向服务器的XX端口发送连接请求,建立一条数据链路来传送数据。(实际应用中经常使用PASV工作方式)

4.DNS

  • DNS(Domain Name System)能够提供互联网域名解析服务,是互联网的一项核心服务
  • DNS数据报文格式DNS数据报文一般使用UDP协议传输,常用端口号为53

这里举例说明域名解析过程。

例如,某用户单击链接www.linux.com,则其本地DNS服务器开始搜索自己的DNS数据库信息,如果没有搜索到,就转到上级DNS服务器,若上级DNS服务器也没有该域名的记录,则继续转上级DNS服务器,直到根DNS服务器。根DNS服务器在其DNS数据库里查找COM*域,然后它用NS记录回复该用户DNS服务器,指示可以从linux.com的DNS服务器ns.linux.com处查询到www.linux.com的信息。于是,经过DNS服务器ns.linux.com的转换,该用户得到了www.linux.com的对应IP地址,并且其DNS服务器缓存了该NS记录。下次如果有用户再需要解析该域名时,相关信息在本地即可获得。

5.SMTP

  • SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,用于将邮件从源地址传送到目的地址。
    
  • SMTP利用传输层的TCP协议传输信件数据,使用端口一般为25。
    
  • 利用SMTP通信的双方通过命令和应答的方式交换文本信息,文本信息以CRLF作为结束标志,类似于FTP的命令链路,因此,没有定义特定的报文格式
    

6.POP3

  • POP3(Post Office Protocol 3)即邮局协议版本3,用于收信方从自己的邮件服务器上接收其他用户发给自己的邮件,是常用的邮件接收协议
    
  • POP3利用传输层的TCP协议传输数据,使用端口一般为110
    
  • POP3属于离线式协议,即收信方登录到邮件服务器后,会一次性将所有邮件下载到本地计算机上,服务器上的邮件同时被删除。
    
  • 与SMTP类似,使用POP3通信的双方通过命令和应答方式交换文本信息,文本信息以CRLF作为结束标志,因此,没有定义特定的报文格式
    
  • IMAP(Internet Mail Access Protocol)即互联网邮件操作协议,属于在线式协议,允许用户在线操作邮件,方便用户随时随地利用不同终端设备处理邮件,弥补了POP3的不足,是目前最常用的邮件接收协议
    
  • IMAP与POP3主要功能比较
    

计算机网络基础知识总结(python实现)

7. DHCP

  • DHCP(Dynamic Host Configuration Protocol)即动态主机配置协议,是一个简化主机IP地址分配管理的协议。用户可以利用DHCP服务器管理动态的IP地址分配及其他相关的环境配置工作(如DNS、WINS、Gateway的设置)
    

8.NAT协议

  • NAT网络地址转换(Network Address Translation)属接入广域网(WAN)技术,是一种将私有(保留)地址转化为合法IP地址的转换技术,它被广泛应用于各种类型Internet接入方式和各种类型的网络中。原因很简单,NAT不仅完美地解决了lP地址不足的问题,而且还能够有效地避免来自网络外部的攻击,隐藏并保护网络内部的计算机。
    

五、举例

**  **在浏览器中输入 www.baidu.com  后执行的全部过程

现在假设如果我们在客户端(客户端)浏览器中输入http://www.baidu.com,而baidu.com为要访问的服务器(服务器),下面详细分析客户端为了访问服务器而执行的一系列关于协议的操作:

1)客户端浏览器通过DNS解析到www.baidu.com的IP地址220.181.27.48,通过这个IP地址找到客户端到服务器的路径。客户端浏览器发起一个HTTP会话到220.161.27.48,然后通过TCP进行封装数据包,输入到网络层。

2)在客户端的传输层,把HTTP会话请求分成报文段,添加源和目的端口,如服务器使用80端口监听客户端的请求,客户端由系统随机选择一个端口如5000,与服务器进行交换,服务器把相应的请求返回给客户端的5000端口。然后使用IP层的IP地址查找目的端。

3)客户端的网络层不用关系应用层或者传输层的东西,主要做的是通过查找路由表确定如何到达服务器,期间可能经过多个路由器,这些都是由路由器来完成的工作,不作过多的描述,无非就是通过查找路由表决定通过那个路径到达服务器。

4)客户端的链路层,包通过链路层发送到路由器,通过邻居协议查找给定IP地址的MAC地址,然后发送ARP请求查找目的地址,如果得到回应后就可以使用ARP的请求应答交换的IP数据包现在就可以传输了,然后发送IP数据包到达服务器的地址。


参考:

1.《python网络编程(linux)》赵宏,包广斌,马栋林编著.—北京:清华大学出版社,2018(大数据与人工智能技术丛书)

2.https://www.cnblogs.com/maybe2030/p/4781555.html