Python网络编程:编码、字节序与消息帧处理
立即解锁
发布时间: 2025-09-01 02:03:07 阅读量: 5 订阅数: 27 AIGC 

### Python 网络编程:编码、字节序与消息帧处理
在网络编程中,数据的传输和处理涉及多个关键方面,包括编码、字节序以及消息帧的处理。理解这些概念对于编写高效、可靠的网络应用程序至关重要。
#### 1. 编码与部分消息解码风险
在网络通信中,编码是将数据转换为适合传输的格式的过程。然而,使用某些编码方式对部分接收的消息进行解码存在风险。例如,当使用将某些字符编码为多个字节的编码方式时,一个字符可能会被分割在已接收的消息部分和尚未到达的数据包之间。这种情况下,解码可能会产生错误。相关解释可在标准库文档中找到,更多示例可参考相关资料。
#### 2. 网络字节序与二进制数
如果仅在网络上传输文本,需要处理的问题主要是编码和消息帧。但在某些情况下,可能需要以比文本更简洁的方式传达信息,或者与使用原始二进制数据的服务进行通信,这时就需要考虑网络字节序的问题。
##### 2.1 整数传输示例
以整数 4253 为例,许多协议会将其作为字符串 ‘4253’ 传输,这需要至少四个字节。在计算机中,整数以二进制形式存储,使用 `hex()` 函数可以查看其存储方式:
```python
>>> hex(4253)
'0x109d'
```
这里,该整数以最显著字节 0x10 和最不显著字节 0x9d 相邻存储。
##### 2.2 字节序差异
不同计算机处理器架构在二进制数的存储顺序上存在差异。一些计算机是 “大端序”(big-endian),如较旧的 SPARC 处理器,将最显著字节放在首位;而另一些计算机,如常见的 x86 架构,是 “小端序”(little-endian),将最不显著字节放在首位。
| 字节序类型 | 特点 | 示例处理器 |
| ---- | ---- | ---- |
| 大端序 | 最显著字节在前 | 旧 SPARC 处理器 |
| 小端序 | 最不显著字节在前 | x86 架构 |
##### 2.3 Python 中的字节序处理
Python 的 `struct` 模块可以清晰地区分这两种字节序。以下是将整数 4253 分别以小端序和大端序表示的示例:
```python
>>> import struct
>>> struct.pack('<i', 4253)
b'\x9d\x10\x00\x00'
>>> struct.pack('>i', 4253)
b'\x00\x00\x10\x9d'
```
`struct` 模块还提供了 `unpack()` 方法用于将二进制数据转换为 Python 数字:
```python
>>> struct.unpack('>i', b'\x00\x00\x10\x9d')
(4253,)
```
在网络数据传输中,大端序成为了标准。`struct` 模块为 `pack()` 和 `unpack()` 方法添加了新符号 `!`,表示使用网络字节序。
##### 2.4 二进制数据传输建议
为网络套接字传输准备二进制数据时,建议遵循以下步骤:
1. 使用 `struct` 模块创建用于网络传输的二进制数据,并在数据到达时进行解包。
2. 如果可以控制数据格式,使用 `!` 前缀选择网络字节序;如果协议指定使用小端序,则使用 `<`。
3. 始终测试 `struct` 模块如何组织数据,并与协议标准进行比较,注意可以使用格式化字符串中的 `x` 字符插入填充字节。
#### 3. 消息引用与帧处理
在网络通信中,根据使用的协议不同,消息的处理方式也有所不同。
##### 3.1 UDP 数据报
如果使用 UDP 数据报进行通信,协议会将数据以离散且可识别的块进行传输。若网络出现问题,需要手动重新组织和重传这些块。
##### 3.2 TCP 流
使用 TCP 流进行通信时,需要处理消息帧的问题,即如何界定消息,使接收方知道一个消息的结束和下一个消息的开始。由于 `sendall()` 提交的数据可能会被拆分为多个数据包进行实际网络传输,接收方可能需要多次调用 `recv()` 方法才能读取完整的消息。
以下是几种常见的消息帧处理模式:
1. **发送所有数据后断开连接**
发送方循环将所有数据发送给 `sendall()` 后关闭套接字,接收方多次调用 `recv()` 直到返回空字符串,表示发送方已关闭套接字。示例代码如下:
```python
#!/usr/bin/env python3
# Network Programming in Python: The Basics
# Client that sends data then closes the socket, not expecting
# a reply.
import socket
from argparse import ArgumentParser
def server(address):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(address)
sock.listen(1)
print('Run this script in another window with "-c" to connect')
print('Listening at', sock.getsockname())
sc, sockname = sock.accept()
print('Accepted connection from', sockname)
sc.shutdown(socket.SHUT_WR)
message = b''
while True:
more = sc.recv(8192) # arbitrary value of 8k
if not more: # socket has closed when recv() returns ''
print('Received zero bytes - end of file')
break
print('Received {} bytes'.format(len(more)))
message += more
print('Message:\n')
print(message.decode('ascii'))
sc.close()
sock.close()
def client(address):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(address)
sock.shutdown(socket.SHUT_RD)
sock.sendall(b'Beautiful is better than ugly.\n')
sock.sendall(b'Explicit is better than implicit.\n')
sock.sendall(b'Simple is better than complex.\n')
sock.close()
if __name__ == '__main__':
parser = ArgumentParser(description='Transmit & receive a data stream')
parser.add_argument('hostname', nargs='?', default='127.0.0.1',
help='IP address or hostname (default: %(default)s)')
parser.add_argum
```
0
0
复制全文
相关推荐









