python- socket通信 以16进制的数据进行传递与解析


struct.pack也就是将数据按照二进制的格式进行传输  
       在网络编程中,利用 socket 进行通信时,常常会用到 struct 模块,在网络通信中,大多数传递的数据以二进制流(binary data)存在。传递字符串时无需过多担心,但传递 int,char 之类的基本数据时,就需要一种机制将某些特定的结构体类型打包成二进制流的字符串,然后在进行网络传输,而接收端也可以通过某种机制进行解包还原出原始数据。struct 模块便提供了这种机制,该模块主要作用就是对 python 基本类型值与用 python 字符串格式表示的 C struc 类型间的转化(This module performs conversions between Python values and C structs represented as Python strings.)。

另一种使用场景在与 python 与其他语言之间数据交换时使用,例如 C++ 写的客户端发送了一个 int 型(4字节)的数据到 Python 的服务器,Python 接收到这个数据时,需要解析成 Python 认识的整数,此时就将用到 struct 模块。
 

      如果熟悉结构体中数据对齐的规则,可以合理设计结构体的结构,各成员变量的顺序,使得所有的数据成员存放在连续的存储区,而且结构体的长度等于所有成员长度之和(可以适当在尾部用字符数组补齐,避免编译器自动填充),这样就方便用send函数发送了。( 如果服务器和客户端都是用C/C++开发,两端可以通过同样结构的结构体来封包和解包,可以不考虑数据对齐的问题)下面讨论的是在C++和python开发的两端之间传输数据的情况:客户端用的C++编写,服务器端用python编写,相对于C++中用struct来封包和解包,python提供了struct库实现类似的功能,最重要的三个函数是pack,unpack,calcsize,struct库处理二进制数据的具体用法可以参考这篇文章:http://www.cnblogs.com/gala/archive/2011/09/22/2184801.html在python下用socket接收到的字节流实际上是字符串,需要对所有的字节进行指定解析格式,所以在C++发送的时候就要避免发送不确定的数据(如编译器自动填充的数据),如果用struct组织数据就需要考虑数据对齐的问题了。下面用一个实例来说明:在C++客户端有1个a(float) ,1个b(unsigned char),一个c(short) 要发送给服务器端,在不使用#pragma指令指定编译器的对齐位数时, 在我的编译器环境下默认为4,可以这样设计结构体:


python- socket通信 以16进制的数据进行传递与解析_第1张图片



另一个考虑的问题就是进制的转换




#!/usr/bin/env python
#coding:utf-8
__author__ = 'ferraborghini'
from socket import *
import struct
#将16进制数据当做字节流传递
def dataSwitch(data):
    str1 = ''
    str2 = ''
    while data:
        str1 = data[0:2]
        s = int(str1,16)
        str2 += struct.pack('B',s)
        data = data[2:]
    return str2
if __name__ == "__main__":
    HOST = 'localhost'
    PORT = 21567
    BUFSIZE = 1024
    ADDR = (HOST,PORT)
    tcpCliSock = socket(AF_INET,SOCK_STREAM)
    tcpCliSock.connect(ADDR)
    while True:
        data = raw_input('>')
        if not data:
            break
        tcpCliSock.send(dataSwitch(data))
        data = tcpCliSock.recv(BUFSIZE)
        if not data:
            break
        print data
    tcpCliSock.close()

解析报文,这时候读入的其实相当于二进制流,我们要做的就将二进制流转化为16进制就行。

#!/usr/bin/env python
#coding:utf-8
from socket import *
from time import ctime
if __name__ == "__main__":
    HOST = ''                       #此处为空代表可以绑定所有有效地址
    PORT = 21567
    BUFSIZE = 1024
    ADDR = (HOST,PORT)
    tcpSerSocket = socket(AF_INET,SOCK_STREAM)
    tcpSerSocket.bind(ADDR)
    tcpSerSocket.listen(5)          #最多可以有5个连接同时进入
    while True:
        print 'waiting for connection...'
        tcpCliSock,addr = tcpSerSocket.accept()
        print '...connected from:',addr
 
        while True:
            data = tcpCliSock.recv(BUFSIZE)
            print data.encode('hex')
            if not data:
                break
            tcpCliSock.send('[%s] %s'%(ctime(),data))
            # tcpCliSock.close()        #如果接收完,就断开的话,下次再发送就会报错,书本上有问题
    tcpSerSocket.close()


项目开发中的一个例子:控制的是USR-R16-T继电器灯的通断

#将16进制数据当做二进制字节流传递
def dataSwitch(data):
    str1 = ''
    str2 = b''
    while data:
        str1 = data[0:2]
        print(str1)
        s = int(str1,16)
        str2 += struct.pack('B',s)
        data = data[2:]
    return str2
'''
req = struct.pack('7B', int('61', 16), int('64', 16), int('6D',16), int('69',16), int('6E',16), int('0D',16), int('0A',16))  #将16进制数据转换为10进制,再打包
client.send(req) 
 
data = client.recv(8096)
print ('Server send information : %s' % data.decode('utf-8'))

ss = dataSwitch('61646D696E0D0A')
print(ss)

client.send(ss)
data = client.recv(8096)
print ('Server send information : %s' % data.decode('utf-8'))

'''
#从终端获取16进制字符串,然后转换为二进制的字节流用于网络传输
while True:
    msg = input('-->').strip()
    print(type(msg))
    if len(msg)==0:continue
    client.send(dataSwitch(msg))
    #print(dataSwitch(msg))
    #client.send(msg.encode('utf-8'))
    #client.send(msg)
    data = client.recv(8096)
    print(data)
    #print ('Server send information : %s' % data[0:2].decode('utf-8'))
sk_obj.close()












你可能感兴趣的:(python)