python中import struct_python中的struct模块的学习

由于TCP协议中的黏包现象的发生,对于最low的办法,每次发送之前让他睡一秒,然后在发送,可是这样真的太low了,而且太占用资源了。

黏包现象只发生在tcp协议中:

1.从表面上看,黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向流通信的特点。

2.实际上,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的

问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。

我们可以借助一个模块,这个模块可以把要发送的数据长度转换成固定长度的字节。这样客户端每次接收消息之前只要先接受这个固定长度字节的内容看一看接下来要接收的信息大小,那么最终接受的数据只要达到这个值就停止,就能刚好不多不少的接收完整的数据了。

该模块可以把一个类型,如数字,转成固定长度的bytes

>>> struct.pack('i',1111111111111) struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #这个是范围

importjson,struct#假设通过客户端上传1T:1073741824000的文件a.txt

#为避免粘包,必须自定制报头

header={'file_size':1073741824000,'file_name':'/a/b/c/d/e/a.txt','md5':'8f6fbf8347faa4924a76856701edb0f3'} #1T数据,文件路径和md5值

#为了该报头能传送,需要序列化并且转为bytes

head_bytes=bytes(json.dumps(header),encoding='utf-8') #序列化并转成bytes,用于传输

#为了让客户端知道报头的长度,用struck将报头长度这个数字转成固定长度:4个字节

head_len_bytes=struct.pack('i',len(head_bytes)) #这4个字节里只包含了一个数字,该数字是报头的长度

#客户端开始发送

conn.send(head_len_bytes) #先发报头的长度,4个bytes

conn.send(head_bytes) #再发报头的字节格式

conn.sendall(文件内容) #然后发真实内容的字节格式

#服务端开始接收

head_len_bytes=s.recv(4) #先收报头4个bytes,得到报头长度的字节格式

x=struct.unpack('i',head_len_bytes)[0] #提取报头的长度

head_bytes=s.recv(x) #按照报头长度x,收取报头的bytes格式

header=json.loads(json.dumps(header)) #提取报头

#最后根据报头的内容提取真实的数据,比如

real_data_len=s.recv(header['file_size'])

s.recv(real_data_len)

struct的详细用法

#_*_coding:utf-8_*_#http://www.cnblogs.com/coser/archive/2011/12/17/2291160.html

__author__ = 'Linhaifeng'

importstructimportbinasciiimportctypes

values1= (1, 'abc'.encode('utf-8'), 2.7)

values2= ('defg'.encode('utf-8'),101)

s1= struct.Struct('I3sf')

s2= struct.Struct('4sI')print(s1.size,s2.size)

prebuffer=ctypes.create_string_buffer(s1.size+s2.size)print('Before :',binascii.hexlify(prebuffer))#t=binascii.hexlify('asdfaf'.encode('utf-8'))#print(t)

s1.pack_into(prebuffer,0,*values1)

s2.pack_into(prebuffer,s1.size,*values2)print('After pack',binascii.hexlify(prebuffer))print(s1.unpack_from(prebuffer,0))print(s2.unpack_from(prebuffer,s1.size))

s3=struct.Struct('ii')

s3.pack_into(prebuffer,0,123,123)print('After pack',binascii.hexlify(prebuffer))print(s3.unpack_from(prebuffer,0))

借助struct模块,我们知道长度数字可以被转换成一个标准大小的4字节数字。因此可以利用这个特点来预先发送数据长度。

importsocket,struct,jsonimportsubprocess

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加

phone.bind(('127.0.0.1',8080))#服务端

phone.listen(5)whileTrue:

conn,addr=phone.accept()whileTrue:

cmd=conn.recv(1024)if not cmd:break

print('cmd: %s' %cmd)

res=subprocess.Popen(cmd.decode('utf-8'),

shell=True,

stdout=subprocess.PIPE,

stderr=subprocess.PIPE)

err=res.stderr.read()print(err)iferr:

back_msg=errelse:

back_msg=res.stdout.read()

conn.send(struct.pack('i',len(back_msg))) #先发back_msg的长度

conn.sendall(back_msg) #在发真实的内容

conn.close()

服务端(自定制报头)

#客户端#_*_coding:utf-8_*_

importsocket,time,struct

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

res=s.connect_ex(('127.0.0.1',8080))whileTrue:

msg=input('>>:').strip()if len(msg) == 0:continue

if msg == 'quit':breaks.send(msg.encode('utf-8'))

l=s.recv(4)

x=struct.unpack('i',l)[0]print(type(x),x)#print(struct.unpack('I',l))

r_s=0

data=b''

while r_s

r_d=s.recv(1024)

data+=r_d

r_s+=len(r_d)#print(data.decode('utf-8'))

print(data.decode('gbk')) #windows默认gbk编码

客户端(自定制报头)

我们还可以把报头做成字典,字典里包含将要发送的真实数据的详细信息,然后json序列化,然后用struck将序列化后的数据长度打包成4个字节(4个自己足够用了)

记住以下图

#复杂报头

importsocket,struct,jsonimportsubprocess

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加

phone.bind(('127.0.0.1',8080))

phone.listen(5)whileTrue:

conn,addr=phone.accept()whileTrue:

cmd=conn.recv(1024)if not cmd:break

print('cmd: %s' %cmd)

res=subprocess.Popen(cmd.decode('utf-8'),

shell=True,

stdout=subprocess.PIPE,

stderr=subprocess.PIPE)

err=res.stderr.read()print(err)iferr:

back_msg=errelse:

back_msg=res.stdout.read()

headers={'data_size':len(back_msg)}

head_json=json.dumps(headers)

head_json_bytes=bytes(head_json,encoding='utf-8')

conn.send(struct.pack('i',len(head_json_bytes))) #先发报头的长度

conn.send(head_json_bytes) #再发报头

conn.sendall(back_msg) #在发真实的内容

conn.close()

服务端:定制稍微复杂一点的报头

#客户端

from socket import *

importstruct,json

ip_port=('127.0.0.1',8080)

client=socket(AF_INET,SOCK_STREAM)

client.connect(ip_port)whileTrue:

cmd=input('>>:')if not cmd:continueclient.send(bytes(cmd,encoding='utf-8'))

head=client.recv(4)

head_json_len=struct.unpack('i',head)[0]

head_json=json.loads(client.recv(head_json_len).decode('utf-8'))

data_len=head_json['data_size']

recv_size=0

recv_data=b''

while recv_size

recv_data+=client.recv(1024)

recv_size+=len(recv_data)print(recv_data.decode('utf-8'))#print(recv_data.decode('gbk')) #windows默认gbk编码

客户端

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值