Чтение структуры внутри структуры с помощью функции распаковки PHP

StackOverflow https://stackoverflow.com/questions/2256879

Вопрос

Я хочу знать, как читать структуру внутри структуры через PHP. распаковать функция.Когда я получаю пакет 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)
};
Это было полезно?

Решение

$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 бита, машинный порядок байтов)".На моей машине это нормально.Но поищите в документации протокола порядок байтов данных.

Тестовые данные в $msg были взяты из результата

__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;
}

Другие советы

Я использую это:


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;
        }

}

И все работает нормально!

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top