简介

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模块与简单使用相关推荐

  1. [转载] Python: struct 模块之字节对齐问题

    参考链接: Python中的struct模块 P y t h o n Python Python 在二进制写文件时,可以用 s t r u c t struct struct 模块将数据捆绑成结构体转 ...

  2. python struct 模块

    Python没有提供直接的将用户定义的数据类型和文件IO关联起来的功能,但是它提供了struct库(是一个内置库)--我们可以以二进制模式来写这些数据(有趣的是,它真的是设计来讲文本数据写为缓存的) ...

  3. python struct模块的使用

    struct模块中的函数 函数 return explain pack(fmt,v1,v2-) string 按照给定的格式(fmt),把数据转换成字符串(字节流),并将该字符串返回. pack_in ...

  4. python struct模块_python struct 模块

    struct模块用于二进制和结构体之间的互相转化,此模块中大部分函数接受一个实现了Buffer协议的对象,最常见的实现了Buffer协议的对象包括bytes.bytearray等,大多数像byte数组 ...

  5. 项目一:使用python tkinter模块做简单的计算器

    小白第一次发博客,可能有很多问题,望指正! 讲的不是很详细,提供思路. 目录: 成果展示 代码说明 其他补充和参考资料 ------分割线-------- 1.成果展示 基本效果图:         ...

  6. python expect模块pexpect简单应用

    Pexpect 是一个自动控制的 Python 模块,可以用来ssh.ftp.passwd.telnet 等命令行进行自动交互. 官方网站是 http://www.noah.org/.通过它,可以实现 ...

  7. python wxpy模块,python wxpy模块 (一)简单上手

    简单上手 登陆微信: 导入模块 from wxpy import * 初始化机器人,扫码登陆 bot = Bot() 找到好友: 搜索名称含有 "游否" 的男性深圳好友 my_fr ...

  8. python argpare 模块的简单用法

    1.实例: #!/usr/bin/python #coding:utf-8import argparseparser = argparse.ArgumentParser()parser.add_arg ...

  9. python renamer模块_artellapipe-tools-renamer-以简单的方式重命名DCC对象的工具-Tomas Poveda...

    作者:Tomas Poveda ### 作者邮箱:tpovedatd@gmail.com ### 首页:https://github.com/ArtellaPipe/artellapipe-tools ...

  10. Python - struct模块、partical模块

    #partical方法胡定函数第一个参数 #可以使用iter迭代,但是配合iter使用要指定两个参数  #struct.pack()/unpack()只打包/解包四个字节(编码ASCII,虽然是ASC ...

最新文章

  1. 抛弃注意力,比EfficientNet快3.5倍,类Transformer新模型跨界视觉任务实现新SOTA
  2. matlab preloadfcn,simulink中打不开SysytemGenerator?返回错误Error evaluating ...
  3. AlertDialog的使用(二):分别创建
  4. 实践重于理论——创建一个监控程序探测WCF的并发处理机制
  5. 利用BADI ME_PROCESS_PO_CUST進行PO check
  6. java.lang.ClassNotFoundException: javax.servlet.jsp.jstl.core.LoopTag
  7. java comparator_Java基础之String漫谈(二)
  8. LeetCode:892. 三维形体的表面积
  9. js+springMVC 提交数组数据到后台
  10. 太火!这本 AI 图书微软强推,程序员靠它拿下 50K!
  11. Java为什么不允许覆盖静态方法?
  12. contentType,charset和pageEncoding的区别
  13. Gym - 102163M
  14. 少讲大道理,多解决小问题
  15. Java分代垃圾回收机制:年轻代/年老代/持久代(转)
  16. 十七、字符串类型String(一)
  17. redis 入门笔记(一)
  18. matlab 加权残值法,分步迭代加权残值法
  19. 微信公众号登陆微商城
  20. CMOS 图像传感器简介(1):像素结构

热门文章

  1. 读Zepto源码之Data模块
  2. MyEclipse项目中的包按层次显示
  3. python file operate example - 2
  4. javaweb工程中web.xml配置
  5. 2.Prometheus 监控技术与实践 --- Prometheus基本概念及部署
  6. 8.企业安全建设指南(金融行业安全架构与技术实践) --- 安全考核
  7. 23.MySQL 函数
  8. 20.经典抽象数据类型
  9. 8086的两种工作模式_在线式UPS工作模式
  10. jQuery中find和filter的区别