通过PHP的解包函数读取Struct中的Struct

时间:2010-02-13 07:51:41

标签: php struct packet variable-length

我想知道如何通过php的unpack函数读取结构中的结构。当我得到一个IS_MCI数据包时,我检查它的类型以确保它等于ISP_MCI,然后检查NumC以找出该数据包中有多少个CompCar结构。问题是尝试通过单个函数将这些内容解压缩到一个数组中。我总是得到一个未定义的偏移量。所以,我正在寻找一些新的眼睛。

你会如何处理这个数据包?

有问题的结构是:

struct IS_MCI // Multi Car Info - if more than 8 in race then more than one of these is sent
{
    byte    Size;       // 4 + NumC * 28
    byte    Type;       // ISP_MCI
    byte    ReqI;       // 0 unless this is a reply to an TINY_MCI request
    byte    NumC;       // number of valid CompCar structs in this packet

    CompCar Info[8];    // car info for each player, 1 to 8 of these (NumC)
};

struct CompCar // Car info in 28 bytes - there is an array of these in the MCI (below)
{
    word    Node;       // current path node
    word    Lap;        // current lap
    byte    PLID;       // player's unique id
    byte    Position;   // current race position : 0 = unknown, 1 = leader, etc...
    byte    Info;       // flags and other info - see below
    byte    Sp3;
    int     X;          // X map (65536 = 1 metre)
    int     Y;          // Y map (65536 = 1 metre)
    int     Z;          // Z alt (65536 = 1 metre)
    word    Speed;      // speed (32768 = 100 m/s)
    word    Direction;  // direction of car's motion : 0 = world y direction, 32768 = 180 deg
    word    Heading;    // direction of forward axis : 0 = world y direction, 32768 = 180 deg
    short   AngVel;     // signed, rate of change of heading : (16384 = 360 deg/s)
};

2 个答案:

答案 0 :(得分:3)

$msg = 
   chr(0x20) // Size = 32 (4+1*28)
  . chr(0x1) // Type = 1
  . chr(0x0) // ReqI=0
  . chr(0x1) // NumC=1
    . chr(0x1) . chr(0x0) // node=1
    . chr(0x2) . chr(0x0)  // lap=2
    . chr(0x3) // puid=3
    . chr(0x5) // pos=5
    . chr(0x10) // info=16
    . chr(0x0) //sp3=0
    . chr(0x0) . chr(0x0) . chr(0x1) . chr(0x0) // x=65536
    . chr(0x0) . chr(0x0) . chr(0x2) . chr(0x0) // y=65536*2
    . chr(0x0) . chr(0x0) . chr(0x3) . chr(0x0)  // z=65536*3
    . chr(0x0) . chr(0x20) // speed=8192
    . chr(0x0) . chr(0x10) // dir=4096
    . chr(0x0) . chr(0x8) // heading=2048
    . chr(0x0) . chr(0x4) // AngVel=1024
;

$IS_MCI = unpack('CSize', $msg);
if ( strlen($msg) < $IS_MCI['Size'] ) {
  die("not enough data");
}
$IS_MCI += unpack('CType/CReqI/CNumC', substr($msg, 1));
$IS_MCI['Info'] = array();

for($i=0; $i<$IS_MCI['NumC']; $i++) {
  $data = substr($msg, 4+($i*28), 28);
  $IS_MCI['Info'][] = unpack('vNode/vLap/CPLID/CPosition/CInfo/CSp3/lX/lY/lZ/vSpeed/vDirection/vHeading/sAngVel', $data);
}
print_r($IS_MCI);

打印

Array
(
    [Size] => 32
    [Type] => 1
    [ReqI] => 0
    [NumC] => 1
    [Info] => Array
        (
            [0] => Array
                (
                    [Node] => 1
                    [Lap] => 2
                    [PLID] => 3
                    [Position] => 5
                    [Info] => 16
                    [Sp3] => 0
                    [X] => 65536
                    [Y] => 131072
                    [Z] => 196608
                    [Speed] => 8192
                    [Direction] => 4096
                    [Heading] => 2048
                    [AngVel] => 1024
                )

        )

)

现在,该代码做了一些您可能不想理所当然的假设(即添加更多错误/读取数据处理)。

  • 它假定在代码运行之前已完全读取数据包($ msg)。您可能只想读取当前需要的部分(不需要substr())。或者至少准备好消息可以分成几个块。
  • 它还将size / num参数视为已授予,即它不会检查这些值是否可行以及是否有足够的数据可用。这绝对是你必须要改变的。 Size必须介于0 ... 228之间,NumC必须介于0 ... 8之间,并且两个值必须合在一起,依此类推。
  • 还仔细看看我在unpack()中使用的格式标识符。对于word,我使用了v代表“无符号短(始终为16位,小端字节顺序)。但对于int我使用过l:“签名长(总是32位,机器字节顺序)”。我的机器上没问题。但是搜索协议文档中endianness的数据

$ msg中的testdata取自

的结果
__declspec(align(1)) struct CompCar // Car info in 28 bytes - there is an array of these in the MCI (below)
{
    word    Node;       // current path node
    word    Lap;        // current lap
    byte    PLID;       // player's unique id
    byte    Position;   // current race position : 0 = unknown, 1 = leader, etc...
    byte    Info;       // flags and other info - see below
    byte    Sp3;
    int     X;          // X map (65536 = 1 metre)
    int     Y;          // Y map (65536 = 1 metre)
    int     Z;          // Z alt (65536 = 1 metre)
    word    Speed;      // speed (32768 = 100 m/s)
    word    Direction;  // direction of car's motion : 0 = world y direction, 32768 = 180 deg
    word    Heading;    // direction of forward axis : 0 = world y direction, 32768 = 180 deg
    short   AngVel;     // signed, rate of change of heading : (16384 = 360 deg/s)
};

__declspec(align(1)) struct IS_MCI // Multi Car Info - if more than 8 in race then more than one of these is sent
{
    byte    Size;       // 4 + NumC * 28
    byte    Type;       // ISP_MCI
    byte    ReqI;       // 0 unless this is a reply to an TINY_MCI request
    byte    NumC;       // number of valid CompCar structs in this packet

    CompCar Info[1];    // example: one element, fixed
};

int _tmain(int argc, _TCHAR* argv[])
{
  struct IS_MCI mci = {
    32, 1, 0, 1,
    { 1, 2, 3, 5, 16, 0, 65536, 65536*2, 65536*3, 8192, 4096, 2048, 1024 }
  };

  WSADATA wsaData;
  WORD wVersionRequested = MAKEWORD( 2, 2 );
   int err = WSAStartup( wVersionRequested, &wsaData );
  if ( err != 0 ) {
      /* Tell the user that we could not find a usable */
      /* WinSock DLL.                                  */
      return 1;
  }

  SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  sockaddr_in addr; 
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
  addr.sin_port = htons( 8081 );
  if ( 0!=connect( s, (SOCKADDR*) &addr, sizeof(addr) ) ) {
    printf("%X ", WSAGetLastError());
    return 0;
  }
  send(s, (const char*)&mci, sizeof(mci), 0);
  shutdown(s, SD_BOTH);
  closesocket(s);
  return 0;
}

答案 1 :(得分:1)

我正在使用它:


class IS_MCI extends ISP {
        public $Size;
        public $Type = ISP_MCI;
        public $ReqI;
        public $NumC;

        public function IS_MCI($data, &$CompCar) {
                $up = unpack('CSize/CType/CReqI/CNumC', $data);
                $this->Size = $up['Size'];
                $this->ReqI = $up['ReqI'];
                $this->NumC = $up['NumC'];

                $temp = array();

                $p = 4;
                for ($i = 0; $i NumC; $i++) {
                        $up2 = unpack('SNode/SLap/CPLID/CPosition/CInfo/CSp3/IX/IY/IZ/SSpeed/SDirection/SHeading/sAngVel', substr($data, $p, 28));
                        $temp[] = new CompCar($up2['Node'],$up2['Lap'],$up2['PLID'],$up2['Position'],$up2['Info'],$up2['Sp3'],$up2['X'],$up2['Y'],$up2['Z'],$
                        $p += 28;
                }
                $CompCar = $temp;
        }
}

和CompCar类:


class CompCar {
        public $xNode;          // current path node
        public $Lap;            // current lap
        public $PLID;           // player's unique id
        public $Position;       // current race position : 0 = unknown, 1 = leader, etc...
        public $Info;           // flags and other info - see below
        public $Sp3;
        public $X;              // X map (65536 = 1 metre)
        public $Y;              // Y map (65536 = 1 metre)
        public $Z;              // Z alt (65536 = 1 metre)
        public $Speed;          // speed (32768 = 100 m/s)
        public $Direction;      // direction of car's motion : 0 = world y direction, 32768 = 180 deg
        public $Heading;        // direction of forward axis : 0 = world y direction, 32768 = 180 deg
        public $AngVel;         // signed, rate of change of heading : (16384 = 360 deg/s)

        public $SpeedKPH;       // speed in kph
        public $SpeedMPH;       // speed in mph
        public $DirectionC;     // Direction calculated to degrees
        public $HeadingC;       // Heading calculated to degrees
        public $AngVelC;        // Calculated

        // ADDED:
        public $SpeedMS;                // speed in mps

        public function __construct($xNode,$Lap,$PLID,$Position,$Info,$Sp3,$X,$Y,$Z,$Speed,$Direction,$Heading,$AngVel) {
                $this->xNode = $xNode;
                $this->Lap = $Lap;
                $this->PLID = $PLID;
                $this->Position = $Position;
                $this->Info = $Info;
                $this->Sp3 = $Sp3;
                $this->X = $X;
                $this->Y = $Y;
                $this->Z = $Z;
                $this->Speed = $Speed;
                $this->Direction = $Direction;
                $this->Heading = $Heading;
                $this->AngVel = $AngVel;

                $this->doCalcs();
        }

        private function doCalcs() {
                // Speed Calc
                $old = $this->Speed;
                $this->SpeedKPH = ($old * (100 / 32768)) * 3.6;
                $this->SpeedMPH = $this->SpeedKPH * 0.6215;

                $this->SpeedKPH = floor($this->SpeedKPH);
                $this->SpeedMPH = floor($this->SpeedMPH);
                $this->SpeedMS = $this->SpeedKPH/3.6;

                // Direction
                $this->DirectionC = CompCar::degrees($this->Direction);

                // Heading
                $this->HeadingC = CompCar::degrees($this->Heading);

                // Angle Calcs
                $this->AngVelC = $this->AngVel * 180 / 8192;
        }

        public static function degrees($input) {
                $input = $input / 65535 * 360;
                //$input = 360 - floor($input);
                $input = floor(360 - $input);
                return $input;
        }

}

一切正常!