Python编程 VII 网络编程

什么是网络?

是OSI的7层模型? 还是TCP/IP 协议簇?wifi/移动通信的空口互联? 又或者是内核进程的虚拟数据转发?

  • 网络的本质基于预定义的通信协议通过物理介质(或虚拟物理介质如虚拟端口,虚拟mac地址)在设备间进行数据传输的一种组织结构。
    • 网络是“有层次”的,无论是固网通信还是空口通信。数据包的传输都在进行一个“封装-解封装”的过程
    • 网络是“动态”的,端到端的网络通信,需要网卡持续监听外部网络接收到的数据包,并通过中断触发内核来对数据包进行处理

TCP/IP协议栈

在其他的网络内容会具体介绍网络的转发和传输。这里只是简单讲解下TCP/IP 协议

网络是一种基于多层通信的数据传输模式

网络模型一开始由OSI 预先定义 即7层网络结构 :

物理层: 比特流的传输、电气特性、机械特性

数据链路层: 将比特率转换为物理信号。并进行基础的网络设备控制

网络层: 提供数据的转发,寻址。以及基础的网络质量检测

传输层: 封装,承载数据。与对端设备建立连接通道。进行数据传输

会话层: 管理会话连接的建立、维护和终止

表示层: 处理数据格式转换、加密解密、压缩解压缩

应用层: 设计应用数据结构如何在设备间进行连接,处理,解析

在实现层面上应用较多的是TCP/IP协议栈,设计了4层网络结构 :

链路层: 数据到介质的转换,通常由网卡等硬件进行。

网络层: 进行数据的转发,寻址。以及基础的网络质量检测

传输层: 封装,承载数据。与对端建立连接。进行数据传输

应用层: 设计应用数据结构如何在设备间进行连接,处理,解析

在数据传输过程中,每一层都具备对应的头部,来更好的控制数据的转发和对应协议的识别。

通过这种设计,允许将一端的信息/代码/数据传输到另外一端,并被解析执行。

socket

socket 起源于linux概念,国内一般称为套接字。它实际上是一种linux内核网络协议栈上的描述符资源。也可以理解为一种网络协议接口,通过socket,我们可以创建多种不同的网络协议,并将数据使用各种网络协议传输到对端,而不需要去考虑每种协议的具体实现。

socket 底层实现的简单描述

上面我们已经说明了socket是一种网络协议接口,它对上提供了数据层,网络层数据传输的功能。对下提供了发送数据包所必须的IP,端口,协议头设计。

在实现上。网络协议栈中,在这一层定义了sock接口。然后定义了通用的C函数指针,再在不同的传输层协议上根据不同的协议描述进行不同的实现。在收发包时,在sock层拆封网络层协议头,识别网络层中的协议号,将数据包转发给对应的结构去处理。转发到对应结构后,通过强制转换,转换为对应的协议数据包来进行进一步的数据解析

传输层协议

通常使用的比较多的是UDP协议和TCP协议

UDP(协议号17)

UDP协议,通常被认为是无连接的,因为协议本身只定义了原,目端口号信息。将应用层数据写入UDP数据包中,传输到对端的设备的目的端口,不存在连接过程和保活,重传等用来保证数据完整性的设计。但是可以在其上层进行这些可靠性设计。所以UDP优点是传输效率高,无论是数据发送层面还是包信息利用率上。而缺点是无连接,当发送掉包时,会导致整个数据不完整,需要在应用层再进行完整性确认。

TCP(协议号6)

TCP协议,与UDP不同,TCP中定义了复杂的控制字段,并设计了状态机来标识TCP传输过程中的每一个阶段,它提供了连接过程,重传,保活等机制来在传输层保护数据的完整性。在TCP中,数据是流式传输的。它并非UDP那样进行用户的写入-打包-发送。而是存在两个 接收/发送的缓存队列,每次从其中获取一定量的字符发送到对端。

Python中的socket

python中同样提供了socket的标准库用于进行网络编程

一个最简单的UDP echo server 实现 :

1
2
3
4
5
6
7
8
9
10
11
12
13
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)                 
#创建socket对象,AF_INET 指定了使用IP协议,SOCK_DGRAM指定是UDP
sock.bind(("127.0.0.1",5560))
#绑定IP地址和端口号 本地主机使用127.0.0.1
buffer = 10240
#设置接收缓存块的大小,接收到的数据包会被存放在缓存中
print("server start")
while True : #设置接收/发送循环
message,addr = sock.recvfrom(buffer) #从socket中接收数据,返回值为一个接收到的信息和ip地址和端口的元组
print(f"recevie message {message.decode('utf-8')} from {addr}") #从数据包解码字符串并打印
send_message = f"recevie message {message.decode('utf-8')} from {addr}".encode()
sock.sendto(send_message,addr) #将收到的信息发送回原地址

1
2
3
4
5
6
7
8
9
# 客户端
import socket
buff = 10240
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True :
message = input("输入消息:")
sock.sendto(message.encode(),("127.0.0.1",5560))
message,addr = sock.recvfrom(buff)
print(f"receive message {message} from {addr}")

这其中有几个主要过程 :

1.调用socket函数,传入协议族,数据类型,返回 socket结构体。它本身是一种多态实现,帮助我们屏蔽了UDP,TCP甚至网络层协议的差异性,根据我们传递的参数创建对应的UDP或者TCP套接字实例

2.当我们传入的是AF_INET,SOCK_DGRAM时,返回的是一个UDP 套接字对象。

3.将套接字对象与对应的网卡进行绑定,(端口号是内核用于转发数据报的逻辑标识。当服务器收到目的地址为127.0.0.1 端口号为5560的 UDP包时,会将数据包转发给对应的上层应用程序处理,实际上就是将拆除协议头的数据报文写入 socket 接收缓冲区中)

4.设置了接收数据所使用的缓存大小

5.设置while 循环 使用 recvfrom 获取数据,接收到的是对端传来的字节数据和对端的地址端口信息

  1. 使用sendto 向目标地址发送数据。

socket 过程常用函数

1
2
3
4
5
6
7
8
9
server = socket.socket()  # 定义协议族和协议信息,返回对应的协议套接字实例
server.bind((ip,port)) # 将套接字与网卡进行绑定
server.recvfrom(buffer_size) #接收经过buffer_size限制大小的数据,返回字节消息和远端的IP 端口信息
server.sendto(message,addr) #向目标网络发送数据,传入待发送的字符和远端的IP 端口信息
server.listen() #用于TCP,执行后打开对应的网络端口监听
server.accept() #当创建的TCP接收到连接请求后,通过这个方法返回一个socket的子链接公用底层的端口和IP信息
sock.recv() #因为TCP 是有连接的,确认绑定关系后,不需要再指定IP和端口信息
sock.send() #同上
client.connect((ip,port)) #TCP客户端的调用,指定对端地址和端口进行TCP连接

Python编程 VII 网络编程
http://gadoid.io/2025/04/17/Python编程-VII-网络编程/
作者
Codfish
发布于
2025年4月17日
许可协议