python服务器/客户模型代码 之三

python的struct

多个struct函数(Struct 的方法)采用缓冲区参数。这个对象实现缓冲区协议,并提供可读或可读写缓冲区。用于此目的的最常见python类型是 bytes 和 bytearray,但许多其他可以视为字节数组的类型实现缓冲区协议,以便可以读取/填充它们,而无需从 bytes 对象进行额外的复制。

格式字符串描述了打包和解包数据时的数据布局。它们由格式字符组成,格式字符指定要打包/解包的数据类型。此外,特殊字符控制字节顺序、大小和对齐方式。每个格式字符串都包含一个可选的前缀字符(描述数据的整体属性)和一个或多个格式字符(描述实际数据值和填充)。

格式字符串

格式字符串的第一个字符

格式字符串的第一个字符可用于指示打包数据的字节顺序、大小和对齐方式,如下表所示

字符字节序大小对齐
@nativenativenative
=native标准
<小端顺序标准
>大端顺序标准
!网路序标准

native依赖主机。

格式字符

考虑到它们的类型,C 和 Python 值之间的转换应该是显而易见的。

当格式字符串第一字符是 ‘<’、‘>’、‘!’ 或“=”,“标准大小”是指使用标准大小时打包值的大小(以字节为单位)。

当格式字符串第一字符是 ‘@’,打包值的大小取决于平台。

格式字符C 类型Python 类型标准大小
xpad byteno value
ccharbytes of length 11
bsigned charinteger1
Bunsigned charinteger1
?_Boolbool1
hshortinteger2
Hunsigned shortinteger2
iintinteger4
Iunsigned intinteger4
llonginteger4
Lunsigned longinteger4
qlong longinteger8
Qunsigned long longinteger8
nssize_tinteger
Nsize_tinteger
e(6)float2
ffloatfloat4
ddoublefloat8
schar[]bytes
pchar[]bytes
Pvoid*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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值