python服务器/客户模型代码 之三
python的struct
多个struct函数(Struct 的方法)采用缓冲区参数。这个对象实现缓冲区协议,并提供可读或可读写缓冲区。用于此目的的最常见python类型是 bytes 和 bytearray,但许多其他可以视为字节数组的类型实现缓冲区协议,以便可以读取/填充它们,而无需从 bytes 对象进行额外的复制。
格式字符串描述了打包和解包数据时的数据布局。它们由格式字符组成,格式字符指定要打包/解包的数据类型。此外,特殊字符控制字节顺序、大小和对齐方式。每个格式字符串都包含一个可选的前缀字符(描述数据的整体属性)和一个或多个格式字符(描述实际数据值和填充)。
格式字符串
格式字符串的第一个字符
格式字符串的第一个字符可用于指示打包数据的字节顺序、大小和对齐方式,如下表所示
字符 | 字节序 | 大小 | 对齐 |
---|---|---|---|
@ | native | native | native |
= | native | 标准 | 无 |
< | 小端顺序 | 标准 | 无 |
> | 大端顺序 | 标准 | 无 |
! | 网路序 | 标准 | 无 |
native依赖主机。
格式字符
考虑到它们的类型,C 和 Python 值之间的转换应该是显而易见的。
当格式字符串第一字符是 ‘<’、‘>’、‘!’ 或“=”,“标准大小”是指使用标准大小时打包值的大小(以字节为单位)。
当格式字符串第一字符是 ‘@’,打包值的大小取决于平台。
格式字符 | C 类型 | Python 类型 | 标准大小 |
---|---|---|---|
x | pad byte | no value | |
c | char | bytes of length 1 | 1 |
b | signed char | integer | 1 |
B | unsigned char | integer | 1 |
? | _Bool | bool | 1 |
h | short | integer | 2 |
H | unsigned short | integer | 2 |
i | int | integer | 4 |
I | unsigned int | integer | 4 |
l | long | integer | 4 |
L | unsigned long | integer | 4 |
q | long long | integer | 8 |
Q | unsigned long long | integer | 8 |
n | ssize_t | integer | |
N | size_t | integer | |
e | (6) | float | 2 |
f | float | float | 4 |
d | double | float | 8 |
s | char[] | bytes | |
p | char[] | bytes | |
P | void* | integer |
pack
struct.pack(format, v1, v2, …)
返回一个bytes对象,该对象包含v1、v2、…值,这些值根据格式字符串格式组合在一起。参数必须与格式所需的值完全匹配。
struct.pack_into(format, buffer, offset, v1, v2, …)
根据格式字符串格式,该函数将v1、v2、…值组合在一起,并将打包字节写入从位置 offset 开始的可写缓冲区 buffer。
unpack
struct.unpack(format, buffer)
根据格式字符串 format 从缓冲区 buffer 中解包。结果是一个元组,即使它只包含一项。缓冲区的大小(以字节为单位)必须与格式所需的大小相匹配。格式所需的大小可以使用calcsize() 来计算。
struct.unpack_from(format, /, buffer, offset=0)
根据格式字符串格式,从偏移位置开始从缓冲区解包。结果是一个元组,即使它只包含一项。缓冲区的大小(以字节为单位)从位置偏移量开始,必须至少为格式所需的大小,格式所需的大小可以使用calcsize() 来计算。
struct.iter_unpack(format, buffer)
根据格式字符串格式迭代地从缓冲区 buffer 中解包。该函数返回一个迭代器,它将从缓冲区读取相同大小的块,直到其所有内容都被消耗。缓冲区的大小(以字节为单位)必须是格式所需大小的倍数。
每次迭代都会生成一个由格式字符串指定的元组。
struct 数据传输
struct.Struct(format)
返回一个新的 Struct 对象,该对象根据格式字符串格式写入和读取二进制数据。创建一次 Struct 对象,并调用其方法比调用具有相同格式的模块级函数更有效,因为该对象仅编译一次格式字符串。
pack
pack(v1, v2, …)
与 pack() 函数相同,但使用编译后的格式字符串。len(result)等于该Struct 对象的size。
pack_into(buffer, offset, v1, v2, …)
与 pack_into() 函数相同,使用编译后的格式字符串。
unpack
unpack(buffer)
与 unpack() 函数相同,使用编译后的格式字符串。缓冲区的大小(以字节为单位)必须等于该Struct 对象的size。
unpack_from(buffer, offset=0)
与 unpack_from() 函数相同,使用编译后的格式字符串。缓冲区的大小(以字节为单位)从位置 offset 开始,必须至少为 size。
iter_unpack(buffer)
与 iter_unpack() 函数相同,使用编译后的格式字符串。缓冲区的大小(以字节为单位)必须是 该Struct 对象的size 的倍数。
server代码
import socket
import sys
from struct_data import *
def serve_connect(connection, client_address):
try:
print ('connection from', client_address)
data = struct_data(connection, PKT_PACK_HEAD_FMT, PKT_PACK_BODY_FMT, PKT_PACK_ID, FIELD_NAMES)
s_data = data.receive_packet()
s_data["name"]=s_data["name"].decode("utf-8").strip('\x00')
print('received: {0}'.format(s_data))
resp = struct_data(connection, RESP_PKT_PACK_HEAD_FMT, RESP_PKT_PACK_BODY_FMT, RESP_PKT_PACK_ID, RESP_FIELD_NAMES)
data_s = {"employee_id":s_data["employee_id"], "status":1}
memb_data = list(data_s.values())
bdata = resp.make_packet(memb_data);
connection.sendall(bdata)
finally:
# Clean up the connection
connection.close()
def tcp_server():
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Bind the socket to the port
server_address = ('localhost', 10000)
print('starting up on %s port %s' % server_address)
sock.bind(server_address)
# Listen for incoming connections
sock.listen(1)
while True:
# Wait for a connection
print('waiting for a connection')
connection, client_address = sock.accept()
serve_connect(connection, client_address)
if __name__ == "__main__":
tcp_server()
client代码
import socket
import sys
from struct_data import *
def tcp_client():
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Connect the socket to the port where the server is listening
server_address = ('localhost', 10000)
print('connecting to %s port %s' % server_address)
sock.connect(server_address)
try:
data = struct_data(sock, PKT_PACK_HEAD_FMT, PKT_PACK_BODY_FMT, PKT_PACK_ID, FIELD_NAMES)
data_s = {"employee_id":10020, "name":"John", "age":30, "height":175}
memb_data = list(data_s.values())
bdata = data.make_packet(memb_data);
sock.sendall(bdata)
resp = struct_data(sock, RESP_PKT_PACK_HEAD_FMT, RESP_PKT_PACK_BODY_FMT, RESP_PKT_PACK_ID, RESP_FIELD_NAMES)
s_data = resp.receive_packet()
print('received: {0}'.format(s_data))
finally:
print('closing socket')
sock.close()
if __name__ == "__main__":
tcp_client()
辅助代码
辅助代码使用python的库,在python的值和C结构之间, 转换数据。在python中,C结构被表达成python的bytes类型。
这种方法可用于
- 与外部源(比如网络)交换数据
- 在python应用与C语言之间交换数据
struct_data.py包含下列的代码内容
import struct
from struct_data_const import *
class struct_data:
def __init__(self, conn, fmt_head_str, fmt_body_str, pack_id, field_names):
self.fmt_head_str = fmt_head_str;
self.fmt_body_str = fmt_body_str;
self.conn = conn
self.pack_id = pack_id
self.field_names = field_names
def make_packet(self, data):
body_sz = struct.calcsize(self.fmt_body_str)
b_head_data = struct.pack(self.fmt_head_str, self.pack_id, body_sz);
fmt = [i for i in self.fmt_body_str]
ndx = 0
b_body_data=bytes(bytearray())
cnt = 0;
for x in fmt[1:]:
if x>='0' and x <='9':
cnt = cnt * 10 + int(x)
continue
elif cnt == 0:
cnt = 1
fmt1 = "{0}{1}{2}".format(fmt[0], cnt, x)
cnt = 0
if x == 's':
b_body_data += struct.pack(fmt1, data[ndx].encode("utf-8"))
else:
b_body_data += struct.pack(fmt1, data[ndx])
ndx = ndx + 1
bdata = b_head_data + b_body_data
return bdata
def unpack_packet(self, fmt_str, data):
dict_data = struct.unpack(fmt_str, data)
return dict_data
def receive_packet(self):
sz = struct.calcsize(self.fmt_head_str)
data = self.conn.recv(sz)
l1 = self.unpack_packet(self.fmt_head_str, data);
d1 = dict(zip(FIELD_HEAD_NAMES, l1))
body_data = self.conn.recv(d1["len"])
l2 = self.unpack_packet(self.fmt_body_str, body_data);
b1 = dict(zip(self.field_names, l2))
return b1
struct_data_const.py包含下列的代码内容
FIELD_NAMES = ["employee_id", "name", "age", "height"]
PKT_PACK_FMT = "<HH10sBH"
PKT_PACK_BODY_FMT = "<H10sBH"
FIELD_HEAD_NAMES = ["id", "len"]
PKT_PACK_HEAD_FMT = "<HH"
RESP_FIELD_HEAD_NAMES = ["resp_id", "len"]
RESP_PKT_PACK_HEAD_FMT = "<HH"
RESP_FIELD_NAMES = ["employee_id", "status"]
RESP_PKT_PACK_BODY_FMT = "<HB"
PKT_PACK_ID = 0xDAFE
RESP_PKT_PACK_ID = 0xDBFE
标签:struct,python,之三,缓冲区,格式,字符串,服务器,data,socket
From: https://blog.csdn.net/IT_Beijing_BIT/article/details/142236667