2015-03-13 74 views
1

我有一個從C++程序發送的UDP包,我必須在Python中讀取它。我設法接收UDP包,但不幸的是它被編碼。 C++的數據的形式爲Python緩衝區解碼

struct packet{ 
    double arg1 
    double arg2 
    double arg3 
    double arg4 
    int16_t arg5 
}; 

我接收的數據是在總共42個字節長(這是也發送相同的長度)。也許這些數據在發送之前也會稍微改變,但我仍然試圖弄清楚。 所以我的問題是:讓我們假設發送數據的結構將完全如上所述。我怎麼能從緩衝區讀出來? (順便說一句,我使用Python 2.7.9)

THX已經

編輯:我發現一些有關的結構

void Send(const CrazyFliePacket& packet) 
    { 
     FMA::ByteBuffer p = 
      FMA::Encode((PACKET_TYPE)FMA_PACKET_TYPES::CRAZYFLIE) + 
      FMA::Encode((PACKET_VERSION)0) + 
      FMA::Encode(_seq++) + 
      packet.Encode(); 

     _server->SendPacket(p.data(),p.size()); 
     printf("Packet sent with %i bytes size\n",p.size()); 
    } 

類型有:

typedef unsigned short PACKET_TYPE; 
typedef unsigned short PACKET_VERSION; 
uint16_t _seq; 

發送和接收的數據現在長度爲40個字節。

EDIT2:正確的解決方法是

print struct.unpack('<HHhddddh', data) 
+0

訪問編碼對象的優勢:怎麼了?這個C++代碼並不能說明'數據包'是如何編碼的。 – 2015-03-13 09:36:18

+0

我添加了評論。 – Slaxx 2015-03-13 11:52:31

回答

2

我覺得struct是你在找什麼。無論如何,你必須找出序列化的數據格式。它不是發送的C/C++結構,而是(希望)一個很好描述的可能是binaray的序列化格式。重要的主題是小/大的永久性,double的大小,int大小和填充。

from struct import * 
arg1, arg2, arg3, arg4, arg5 = unpack('!ddddi', buffer) 

標準尺寸就需要36個字節

>>> from struct import * 
>>> a='\x00\x00\x00\x01'*9 
>>> unpack('>ddddi', a) 
(2.1219957915e-314, 2.1219957915e-314, 2.1219957915e-314, 2.1219957915e-314, 1) 
>>> unpack('<ddddi', a) 
(7.291122046717944e-304, 7.291122046717944e-304, 7.291122046717944e-304, 7.291122046717944e-304, 16777216) 
>>> unpack('!ddddi', a) 
(2.1219957915e-314, 2.1219957915e-314, 2.1219957915e-314, 2.1219957915e-314, 1) 
>>> 

編輯:拆包變量,命名元組和階級

直接解壓到您的變量

from struct import unpack 
b = "\x00\x00\x00\x05\x00\x00\x00\x2a" 

x, y = unpack('!ii', b) 
print x 
print y 

解壓縮到一個名爲元組

from struct import unpack 
from collections import namedtuple 
b = "\x00\x00\x00\x05\x00\x00\x00\x2a" 

Point = namedtuple('Point', 'x, y') 

p = Point._make(unpack("!ii", b)) 
print p.x 
print p.y 

記住:元組屬性爲只讀

解壓和initalize類

from struct import unpack 
b = "\x00\x00\x00\x05\x00\x00\x00\x2a" 

class Point(object): 
    def __init__(self, x, y): 
     self.x = x 
     self.y = y 

p = Point(*unpack('!ii', b)) 
print p.x 
print p.y 
+0

謝謝。是的,我在代碼中讀過它是二進制的。然而,我仍然在試圖弄清楚它是如何組成的。但我認爲你寫的最終是我需要的。 3個解包之間有什麼區別? – Slaxx 2015-03-13 10:54:09

+0

小端(intel)vs大端(摩托羅拉,網絡字節順序) – stefan 2015-03-13 11:01:22

+0

好吧,我想我找到了附加字節的來源,請參閱編輯問題。 – Slaxx 2015-03-13 11:09:07

2

我也使用了結構拆包(和不得不尋找字符代碼每次)直到我學會使用ctypes做同樣的事情。我不知道哪一個執行得更快,但是我知道哪一個我可以毫無問題地轉換,只是看看C的定義。

from ctypes import LittleEndianStructure, BigEndianStructure, c_int16, c_double, sizeof 
#there are bit explicit types if you want to fix the size, like c_int8/16/32/64 

class Packet(LittleEndianStructure): # replace by BigEndian as needed 
    _fields_ = [ 
     ("arg1", c_double), 
     ("arg1", c_double), 
     ("arg3", c_double), 
     ("arg4", c_double), 
     ("arg5", c_int16) 
     ] 
    _pack_=1 # comment this or not depending on padding 

mypacket = Packet.from_buffer_copy(payload) 

這對我來說更容易閱讀,它有給我提供了一個收集的所有數據,並可以通過名稱

print(mypacket.arg1)