面向对象之套接字,在Python的struct模块中展开数据格式转换的办法

By admin in 4858.com on 2019年2月11日

 一丶套接字(socket)

在Python的struct模块中开展多少格式转换的措施,pythonstruct

Python是一门极度简单的语言,对于数据类型的代表,不像其他语言预约义了众各类类(如:在C#中,光整型就定义了8种),它只定义了二种为主类型:字符串,整数,浮点数,元组,列表,字典。通过那七种数据类型,我们得以成功大多数办事。但当Python必要经过互连网与任何的阳台展开相互的时候,必须考虑到将那几个数据类型与其他平台或语言之间的档次举行互动转换难题。打个比方:C++写的客户端发送一个int型(4字节)变量的数目到Python写的服务器,Python接收到代表这一个平头的4个字节数据,怎么解析成Python认识的平头呢?
Python的正规模块struct就用来化解那几个难题。

struct模块的内容不多,也不是太难,上边对内部最常用的章程举行介绍:
 struct.pack

struct.pack用于将Python的值按照格式符,转换为字符串(因为Python中并未字节(Byte)类型,可以把这边的字符串掌握为字节流,或字节数组)。其函数原型为:struct.pack(fmt,
v1, v2, …),参数fmt是格式字符串。v1, v2,
…表示要更换的python值。下边的例子将五个整数转换为字符串(字节流):
 

import struct

a = 20
b = 400

str = struct.pack("ii", a, b) 
#转换后的str虽然是字符串类型,但相当于其他语言中的字节流(字节数组),可以在网络上传输
print 'length:', len(str)
print str
print repr(str)

#---- result
#length: 8
#   ----这里是乱码
#'/x14/x00/x00/x00/x90/x01/x00/x00'

格式符”i”表示转换为int,’ii’表示有七个int变量。举办更换后的结果长度为8个字节(int类型占用4个字节,七个int为8个字节),可以看来输出的结果是乱码,因为结果是二进制数据,所以体现为乱码。可以拔取python的内置函数repr来收获可辨识的字符串,其中十六进制的0×00000014,
0×00001009分别代表20和400。
面向对象之套接字,在Python的struct模块中展开数据格式转换的办法。struct.unpack

struct.unpack做的做事刚刚与struct.pack相反,用于将字节流转换成python数据类型。它的函数原型为:struct.unpack(fmt,
string),该函数重回一个元组。 下边是一个简单的例证:
 

str = struct.pack("ii", 20, 400)
a1, a2 = struct.unpack("ii", str)
print 'a1:', a1
print 'a2:', a2

#---- result:
#a1: 20
#a2: 400
struct.calcsize

struct.calcsize用于统计格式字符串所对应的结果的长度,如:struct.calcsize(‘ii’),再次来到8。因为五个int类型所占据的长短是8个字节。
struct.pack_into, struct.unpack_from

那多少个函数在Python手册中负有介绍,但并未交到怎么着运用的例子。其实它们在骨子里行使使得的并不多。谷歌了很久,才找到一个事例,贴出来共享一下:
 

import struct
from ctypes import create_string_buffer

buf = create_string_buffer(12)
print repr(buf.raw)

struct.pack_into("iii", buf, 0, 1, 2, -1)
print repr(buf.raw)

print struct.unpack_from('iii', buf, 0)

#---- result
#'/x00/x00/x00/x00/x00/x00/x00/x00/x00/x00/x00/x00'
#'/x01/x00/x00/x00/x02/x00/x00/x00/xff/xff/xff/xff'
#(1, 2, -1)

至于格式字符串

在Python手册中,给出了C语言中常用项目与Python类型对应的格式符:

4858.com 1

具体内容请参考Python手册struct 模块

Python是一门非凡简单的言语,对于数据类型的表示,不像其余语言预订义了许多类…

Python是一门万分简短的语言,对于数据类型的象征,不像其余语言预订义了累累类型(如:在C#中,光整型就定义了8种)
它只定义了四种为主项目:字符串,整数,浮点数,元组(set),列表(array),字典(key/value)
通过那多种数据类型,我们得以成功半数以上工作。但当Python要求通过互连网与其他的阳台展开相互的时候,必须考虑到将这一个数据类型与其它平台或语言之间的种类举行互动转换难题。打个比方:C++写的客户端发送一个int型(4字节)变量的数额到Python写的服务器,Python接收到代表那个平头的4个字节数据,怎么解析成Python认识的平头呢?
Python的规范模块struct就用来消除那个标题。
struct模块的始末不多,也不是太难,下边对内部最常用的方法进行介绍:
1、 struct.pack
struct.pack用于将Python的值按照格式符,转换为字符串(因为Python中一贯不字节(Byte)类型,可以把那里的字符串精晓为字节流,或字节数组)。其函数原型为:struct.pack(fmt,
v1, v2,
…),参数fmt是格式字符串,关于格式字符串的相干信息在下边有所介绍。v1,
v2, …表示要转移的python值。下边的例证将五个整数转换为字符串(字节流):

1 struct模块 pack

struct.pack
用于将Python的值依据格式符号,转为字符串,注意再次来到的结果是一个字符串,因为Python中一贯不字节(Byte)类型,能够将那里的字符串精通为字节流或许字节数组。
其函数原型为:struct.pack(fmt, v1, v2,
…),参数fmt是格式字符串,关于格式字符串的连锁音信在有着介绍。v1, v2,
…表示要转换的python值。下边的例证将三个整数转换为字符串(字节流):

import struct
packData= struct.pack("H5sbbH",1,"otcyb",2,3,7 )
print repr(packData)  #'\x01\x00otcyb\x02\x03\x00\x07\x00'
unPackData=struct.unpack("H",packData)  #出错
print unPackData

在上边将 数据{1,”otcyb”,2,3,7}
组装成一个字节流字符串类型,存储在packData中。

  tcp是基于链接的,必须先启动服务端,然后再起步客户端去链接服务端

#!/usr/bin/env python
#encoding:utf8
importsys
reload(sys)
sys.setdefaultencoding("utf-8")
importstruct
a=20
b=400
str=struct.pack("ii",a,b)
print'length:',len(str)#length:8
printstr#乱码:�
printrepr(str)#'\x14\x00\x00\x00\x90\x01\x00\x00'

2 unpack

struct.unpack
struct.unpack做的做事刚刚与struct.pack相反,用于将字节流转换成python数据类型。它的函数原型为:struct.unpack(fmt,
string),该函数重回一个元组。 需求注意的是
unpack传入的参数有多个,一个是格式字符串,首个参数是一个字符串类型的数码,表示要被解包的数额,那几个数额的长度要和格式字符中指定的数据的长短一致,否则解包不成事,在上头的例子中
unPackData=struct.unpack(“H”,packData) #出错
的因由就是因为被解包数据和格式字符串数据不一致

packData= struct.pack("H5sbbH",1,"otcyb",2,3,7 )
print repr(packData)
unPackData=struct.unpack("H5sbbH",packData)
print unPackData

输出结果:
'\x01\x00otcyb\x02\x03\x00\x07\x00'
(1, 'otcyb', 2, 3, 7)

需要说明的是 输出的结果是一个元组的形式。 

  在攻读socket从前大家先学几个新模块:

格式符”i”表示转换为int,’ii’表示有三个int变量。
展开转移后的结果长度为8个字节(int类型占用4个字节,七个int为8个字节)
可以见见输出的结果是乱码,因为结果是二进制数据,所以显得为乱码。
可以应用python的内置函数repr来博取可识其他字符串,其中十六进制的0x00000014,
0x00001009分别代表20和400。
2、 struct.unpack
struct.unpack做的干活刚刚与struct.pack相反,用于将字节流转换成python数据类型。它的函数原型为:struct.unpack(fmt,
string),该函数再次来到一个元组。
下边是一个简练的例子:

  struct模块:

#!/usr/bin/envpython
#encoding:utf8

importsys
reload(sys)
sys.setdefaultencoding("utf-8")

importstruct

a=20
b=400

#pack
str=struct.pack("ii",a,b)
print'length:',len(str)#length:8
printstr#乱码:�
printrepr(str)#'\x14\x00\x00\x00\x90\x01\x00\x00'

#unpack
str2=struct.unpack("ii",str)
print'length:',len(str2)#length:2
printstr2#(20,400)
printrepr(str2)#(20,400)

4858.com,    1、 struct.pack       struct.pack用于将Python的值依据格式符,转换为字符串(因为Python中绝非字节(Byte)类型,可以把那边的字符串掌握为字节流,或字节数组)。其函数原型为:struct.pack(fmt, v1, v2, …),参数fmt是格式字符串,关于格式字符串的相干音信在底下有所介绍。v1, v2, …表示要更换的python值。

3、 struct.calcsize
struct.calcsize用于统计格式字符串所对应的结果的长短,如:struct.calcsize(‘ii’),再次来到8。因为三个int类型所占有的尺寸是8个字节。

    2、 struct.unpack       struct.unpack做的干活刚刚与struct.pack相反,用于将字节流转换成python数据类型。它的函数原型为:struct.unpack(fmt, string),该函数重临一个元组。 

import struct

a = 20
b = 400
s = struct.pack('ii', a, b)
print(s, type(s))
#输出:b'\x14\x00\x00\x00\x90\x01\x00\x00' <class 'bytes'>
print('length: ', len(s))
#输出:length:  8
s2 = struct.unpack('ii', s)
print(s2)
#输出:(20, 400)

s2 = struct.unpack('ii', s)
#报错:unpack requires a buffer of 4 bytes
#==>解压需要一个4字节的缓冲区,也就是说'ii'表示8个字节的缓冲
importstruct
print"len:",struct.calcsize('i')#len:4
print"len:",struct.calcsize('ii')#len:8
print"len:",struct.calcsize('f')#len:4
print"len:",struct.calcsize('ff')#len:8
print"len:",struct.calcsize('s')#len:1
print"len:",struct.calcsize('ss')#len:2
print"len:",struct.calcsize('d')#len:8
print"len:",struct.calcsize('dd')#len:16

  #格式符”i”表示转换为int,’ii’代表有三个int变量。

4、 struct.pack_into、 struct.unpack_from
那三个函数在Python手册中具有介绍,但从不付诸怎样利用的例子。其实它们在实际上利用使得的并不多。谷歌(Google)了很久,才找到一个例子,贴出来共享一下:

  #展开转移后的结果长度为8个字节(int类型占用4个字节,七个int为8个字节)

#!/usr/bin/envpython
#encoding:utf8

importsys
reload(sys)
sys.setdefaultencoding("utf-8")

importstruct
fromctypesimportcreate_string_buffer

buf=create_string_buffer(12)
printrepr(buf.raw)#'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

struct.pack_into("iii",buf,0,1,2,-1)
printrepr(buf.raw)#'\x01\x00\x00\x00\x02\x00\x00\x00\xff\xff\xff\xff'

printstruct.unpack_from("iii",buf,0)#(1,2,-1)

  可以动用python的内置函数repr来得到可甄其余字符串,其中十六进制的0x00000014, 0x00001009分别表示20和400。

具体内容请参见Python手册 struct 模块
Python手册 struct
模块:http://docs.python.org/library/struct.html\#module-struct

  subprocess模块:

原文地址:http://zfblog.xyz/archives/227.html

    那几个模块用的不多,只是用于实践复杂的系统命令而已..大家无需深究.

  

  基于UDP协议的socket

  server端:

import socket
udp_sk = socket.socket(type=socket.SOCK_DGRAM)   #创建一个服务器的套接字
udp_sk.bind(('127.0.0.1',9000))        #绑定服务器套接字
msg,addr = udp_sk.recvfrom(1024)
print(msg)
udp_sk.sendto(b'hi',addr)                 # 对话(接收与发送)
udp_sk.close()                         # 关闭服务器套接字

  client端: 

import socket
ip_port=('127.0.0.1',9000)
udp_sk=socket.socket(type=socket.SOCK_DGRAM)
udp_sk.sendto(b'hello',ip_port)
back_msg,addr=udp_sk.recvfrom(1024)
print(back_msg.decode('utf-8'),addr)

 

二丶粘包

  让我们依据tcp先制作一个远道执行命令的次第(命令ls -l ; lllllll ; pwd)

res=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)

的结果的编码是以当前所在的系统为准的,如果是windows,那么res.stdout.read()读出的就是GBK编码的,在接收端需要用GBK解码

且只能从管道里读一次结果

注意

 

  同时实施多条命令之后,得到的结果很只怕唯有局地,在推行其余命令的时候又吸收到以前实施的其它一些结出,那种表现就是粘包。

  那粘包的成因是什么吧?

当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去。 
MTU是Maximum Transmission Unit的缩写。意思是网络上传送的最大数据包。MTU的单位是字节。 大部分网络设备的MTU都是1500。
如果本机的MTU比网关的MTU大,大的数据包就会被拆开来传送,这样会产生很多数据包碎片,增加丢包率,降低网络速度。

 

  这我们如何缓解粘包呢?

  1.大家得以用time模块,为多个文件之间设置一定的时刻间隔,让多少个不会构成在联名,那么些艺术使得,可是会骤降程序运行效能,而且很蠢.

  2.我们地点讲过struct模块,大家得以用struct模块把报头的五个字节取出来,再解包得到文件的大小,通过解包出来的解包大小来读取数据.

  上面看多少个示范:

  服务器:

4858.com 24858.com 3

import socket
import subprocess

server = socket.socket()

server.bind(('127.0.0.1',8008))

server.listen(5)

while True:
    print("server is working.....")
    conn,addr = server.accept()     #建立链接
    # 字节类型
    while True:
        # 针对window系统
        try:
            cmd = conn.recv(1024).decode("utf8") # 阻塞

            if cmd == b'exit':      #判断与客户端链接是否结束
                break

            res=subprocess.Popen(cmd,
                             shell=True,
                             stderr=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             )
            # print("stdout",res.stdout.read())
            # print("stderr",res.stderr.read().decode("gbk"))
            out=res.stdout.read()
            err=res.stderr.read()

            print("out响应长度",len(out))       #输出出来的长度
            print("err响应长度",len(err))       #错误的长度
            if err:
                 import struct
                 header_pack = struct.pack("i", len(err))   #压包
                 conn.send(header_pack)     #发送
                 conn.send(err)         #发送错误信息
            else:
                 #构建报头
                 import struct
                 header_pack=struct.pack("i",len(out))      #压包
                 print("header_pack",header_pack)           #压包信息
                 # # 发送报头
                 conn.send(str(len(out)).encode("utf8"))    #发送压包
                 # 发送数据
                 conn.send(out)     #发送输出出来的数据

        except Exception as e:      #错误信息处理
            break


    conn.close()

服务器

  客户端:

4858.com 44858.com 5

import socket
import struct
sk = socket.socket()

sk.connect(('127.0.0.1',8008))

while 1:
    cmd = input("请输入命令:")
    sk.send(cmd.encode('utf-8')) # 字节       #发送输出的指令
    if cmd=="":
        continue
    if cmd == 'exit':           #判断是否退出
        break

    data_length=int(sk.recv(1024).decode("utf8"))           #接收到的信息
    print("data_length",data_length)        #打印出来

    recv_data_length=0          #接收到的长度
    recv_data=b""

    while recv_data_length<data_length:     #如果接收到的长度小于设定的数据长度,则全输出出来,否则直接输出最大长度
        data=sk.recv(1024)          #阻塞  设定最大输出长度
        recv_data_length+=len(data)         #计算数据长度
        recv_data+=data

    print(recv_data.decode("gbk"))


sk.close()

客户端

  本示例通过客户端输入一段系统命令,把所输入的系统命令编码然后发给服务器端,服务器解码后再调用subprocess模块,把出口的值打包后,回传给客户端,然后客户端再解包,通过解包拿到的文本内容信息大小打印出来.

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有