Python struct模块与简单使用
简介
struct模块常常用在网络编程中,
- 将要发送的数据转换成字节流形式使用函数
struct.pack()
; - 将收到的字节流解析成具体数据使用函数
struct.unpack()
; - 计算格式字符串的长度使用函数
struct.calcsize()
;
它们是struct模块中最常使用的函数,其函数声明为:
pack(fmt, v1, v2, ...)
------ 根据所给的fmt描述的格式将值v1,v2,…转换为一个字符串。unpack(fmt, bytes)
------ 根据所给的fmt描述的格式将bytes反向解析出来,返回一个元组。calcsize(fmt)
------ 根据所给的fmt描述的格式返回该结构的大小。
重点
可以发现,这三个函数中都有一个fmt
的格式字符串,下面给出的是格式字符串中字符的实际含义:
网络发送数据时,有字节序的问题,它的字符含义如下:
以一个简单的网络CS程序为例,服务器程序等待客户端的连接,连接成功后,接收客户端发送的信息并输出;客户端程序连接服务器程序,然后等待输入并发送给服务器程序。
网络编程中,发送端会先发送一个包头,再发送具体的包体(实际发送数据)。包头的意义在于标识出包体数据的长度,包头的长度是固定的,事先预定好的。接收端会先接收的固定长度的包头,再根据包头中标识包体长度的字段来决定接收多少包体数据。例如,包头的结构如下:
struct msg_header
{unsigned short unused1; // 暂未使用unsigned int msgBodyLen; // 包体长度unsigned long long unuse2; // 暂未使用
}
msg_header
对应的fmt
为'HIL'
,根据上面表中信息不难看懂其中的对应关系。
这里没有使用字节序对应的字符。只要客户端与服务端对应即可,如果客户端使用小端字节序
,服务端也需要使用小端字节序
;如果客户端使用大端字节序
,服务端也必须使用大端字节序
。
客户端发送数据的代码片段如下:
client_sock = socket.socket()
...
send_msg = input()
msg_header_fmt = 'HIL' # 包头的格式,与msg_header中字段一一对应
msg_header_data = struct.pack(msg_header_fmt, 0, len(send_msg), 0) # 包头中只有到msgBody字段
client_sock.sendall(msg_header_data)
msg_body_data = send_msg.encode() # send_msg类型为str,发送时需要使用encode()转换成二进制数据
client_sock.sendall(msg_body_data)
客户端发送数据时先发送包头再发送包体,当然也可以包头包体一起发送:
send_msg = input('input:')
msg_fmt = 'HIL{}s'.format(len(send_msg)) # 包头包体的格式
msg_data = struct.pack(msg_fmt, 0, len(send_msg), 0, send_msg.encode()) # 包头中只用到msgBody字段
self.sock .sendall(msg_data)
msg_fmt = 'HIL{}s'.format(len(send_msg))
会根据发送数据的长度生成对应的fmt,例如发送的数据为'hello world'
,msg_fmt
等于就'HIL11s'
,也就表示包头后面跟着长度为11个字节的包体。
服务端接收数据的代码片段如下:
def recv_some(client_sock, data_len)):...msg_header_format = 'HIL'
msg_header_len = struct.calcsize(msg_header_format) # 包头长度# 接收包头收据
recv_suc, msg_header_data = recv_some(client_sock, msg_header_len)
if not recv_suc:break# 获取包体长度(只取包体长度字段) 注意strcut.unpack()返回的是一个tuple
_, msg_body_len, _ = struct.unpack(msg_header_format, msg_header_data)# 接收包体数据
recv_suc, msg_body_data = recv_some(client_sock, msg_body_len)
if not recv_suc:break# msg_body_data为二进制数据,需要使用decode()进行装换
print('recv msg: {}'.format(msg_body_data.decode()))
示例
客户端代码为:
import socket
import structclass TestClient:def __init__(self, ip_addr, port):self.ip_addr = ip_addrself.port = portself.sock = socket.socket()def start(self):print('connect to {}:{}'.format(self.ip_addr, self.port))self.sock.connect((self.ip_addr, self.port))print('connect OK')while True:send_msg = input('input:')msg_header_fmt = 'HIL' # 包头的格式,与msg_header中字段一一对应msg_header_data = struct.pack(msg_header_fmt, 0, len(send_msg), 0) # 包头中只有到msgBody字段msg_body_data = send_msg.encode()self.sock .sendall(msg_header_data + msg_body_data)if __name__ == '__main__':one_client = TestClient('127.0.0.1', 10088)one_client.start()
服务端代码为:
import socket
import structdef recv_some(client_sock, data_len):recv_data = b''remain_len = data_lenwhile True:try:cur_data = client_sock.recv(remain_len)except ConnectionResetError as error:return False, recv_datarecv_data += cur_dataremain_len -= len(cur_data)if remain_len == 0:breakreturn True, recv_dataclass TestServer:def __init__(self, ip_addr, port):self.ip_addr = ip_addrself.port = portself.sock = socket.socket()def start(self):self.sock.bind((self.ip_addr, self.port))self.sock.listen(5)print('server listen on {}:{}'.format(self.ip_addr, self.port))client_sock, client_info = self.sock.accept()print('recv connection from {}'.format(client_info))msg_header_format = 'HIL'msg_header_len = struct.calcsize(msg_header_format)while True:# 接收包头recv_suc, msg_header_data = recv_some(client_sock, msg_header_len)if not recv_suc:break# 获取包体长度_, msg_body_len, _ = struct.unpack(msg_header_format, msg_header_data)# 接收包体数据recv_suc, msg_body_data = recv_some(client_sock, msg_body_len)if not recv_suc:breakprint('recv msg: {}'.format(msg_body_data.decode()))if __name__ == '__main__':one_server = TestServer('127.0.0.1', 10088)one_server.start()
Python struct模块与简单使用相关推荐
- [转载] Python: struct 模块之字节对齐问题
参考链接: Python中的struct模块 P y t h o n Python Python 在二进制写文件时,可以用 s t r u c t struct struct 模块将数据捆绑成结构体转 ...
- python struct 模块
Python没有提供直接的将用户定义的数据类型和文件IO关联起来的功能,但是它提供了struct库(是一个内置库)--我们可以以二进制模式来写这些数据(有趣的是,它真的是设计来讲文本数据写为缓存的) ...
- python struct模块的使用
struct模块中的函数 函数 return explain pack(fmt,v1,v2-) string 按照给定的格式(fmt),把数据转换成字符串(字节流),并将该字符串返回. pack_in ...
- python struct模块_python struct 模块
struct模块用于二进制和结构体之间的互相转化,此模块中大部分函数接受一个实现了Buffer协议的对象,最常见的实现了Buffer协议的对象包括bytes.bytearray等,大多数像byte数组 ...
- 项目一:使用python tkinter模块做简单的计算器
小白第一次发博客,可能有很多问题,望指正! 讲的不是很详细,提供思路. 目录: 成果展示 代码说明 其他补充和参考资料 ------分割线-------- 1.成果展示 基本效果图: ...
- python expect模块pexpect简单应用
Pexpect 是一个自动控制的 Python 模块,可以用来ssh.ftp.passwd.telnet 等命令行进行自动交互. 官方网站是 http://www.noah.org/.通过它,可以实现 ...
- python wxpy模块,python wxpy模块 (一)简单上手
简单上手 登陆微信: 导入模块 from wxpy import * 初始化机器人,扫码登陆 bot = Bot() 找到好友: 搜索名称含有 "游否" 的男性深圳好友 my_fr ...
- python argpare 模块的简单用法
1.实例: #!/usr/bin/python #coding:utf-8import argparseparser = argparse.ArgumentParser()parser.add_arg ...
- python renamer模块_artellapipe-tools-renamer-以简单的方式重命名DCC对象的工具-Tomas Poveda...
作者:Tomas Poveda ### 作者邮箱:tpovedatd@gmail.com ### 首页:https://github.com/ArtellaPipe/artellapipe-tools ...
- Python - struct模块、partical模块
#partical方法胡定函数第一个参数 #可以使用iter迭代,但是配合iter使用要指定两个参数 #struct.pack()/unpack()只打包/解包四个字节(编码ASCII,虽然是ASC ...
最新文章
- 抛弃注意力,比EfficientNet快3.5倍,类Transformer新模型跨界视觉任务实现新SOTA
- matlab preloadfcn,simulink中打不开SysytemGenerator?返回错误Error evaluating ...
- AlertDialog的使用(二):分别创建
- 实践重于理论——创建一个监控程序探测WCF的并发处理机制
- 利用BADI ME_PROCESS_PO_CUST進行PO check
- java.lang.ClassNotFoundException: javax.servlet.jsp.jstl.core.LoopTag
- java comparator_Java基础之String漫谈(二)
- LeetCode:892. 三维形体的表面积
- js+springMVC 提交数组数据到后台
- 太火!这本 AI 图书微软强推,程序员靠它拿下 50K!
- Java为什么不允许覆盖静态方法?
- contentType,charset和pageEncoding的区别
- Gym - 102163M
- 少讲大道理,多解决小问题
- Java分代垃圾回收机制:年轻代/年老代/持久代(转)
- 十七、字符串类型String(一)
- redis 入门笔记(一)
- matlab 加权残值法,分步迭代加权残值法
- 微信公众号登陆微商城
- CMOS 图像传感器简介(1):像素结构