Как я могу обработать входящее пакетное соединение, анализируя буфер и помещая данные в правильную структуру? - PullRequest
0 голосов
/ 11 мая 2018

У меня открытое соединение, которое работает нормально, но теперь я добавляю второй тип ответа, который мне нужно обработать. Ответы относятся к двум различным структурам.

Тип ответа один - это структура размера 16 = replyTypeOne (int, int, int, int) Тип ответа два - это структура размера 64 = replyTypeTwo (int, int, int, char [48], int)

Раньше я отправлял ответ прямо в правильную структуру - теперь мне нужно добавить буфер, чтобы определить, к какой структуре должен идти ответ.

старая ручка кода ниже:

iResult = recv(connectSocket, (char *)&replyTypeOne, sizeof(replyTypeOne), 0);

Приведенный выше код поместит поля ответа в соответствующие места. Однако, так как replyTypeTwo отличается от replyTypeOne - мне нужно решение, чтобы определить два ответа друг от друга.

Так что я планирую использовать буфер (возможно, мне следует использовать MSG_PEEK ??), чтобы просмотреть входящие данные и поместить их в правильную структуру. Я просто не уверен, как обработать / разобрать буфер ответа atm.

EDIT: добавлена ​​структура replyTemp - int, int, int, int, char [48], int

#define DEFAULT_BUFLEN 128

int iResult;
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;

iResult = recv(connectSocket, (char *)&replyTemp, sizeof(replyTemp), 0)

if(iResult > 0)
{
 if(replyTemp.length > 16)
  {
   //handle replyTypeTwo -  assign replyTypeTwo fields to replyTemp fields
  } else {
   // handle replyTypeOne - asking replyTypeOne fields to replyTemp fields
  }
}
else if(iResult == 0)
{
  connectSocket = INVALID_SOCKET;
}

Ответы [ 2 ]

0 голосов
/ 11 мая 2018

Самый простой способ «заглянуть» - просто recv, а затем recv еще немного.

Поскольку это, похоже, TCP, вам уже нужен цикл recv. Цикл recv всегда необходим с TCP. Когда вы спрашиваете recv(sock, &buf, 16, 0), нет гарантии, что вы получите 16 байтов. Если сообщение было разделено на два пакета в отправителе, вы можете получить только, скажем, 10 байтов в первом пакете, и не получить 6 байтов из следующего пакета, пока вы не выполните второй recv. Таким образом, вы должны продолжать получать, пока не получите все 16 байтов. (Я написал более длинное объяснение в терминах Python , но та же идея применима и к C ++.)

Как только вы это сделаете, все, что нам нужно сделать после получения 16 байтов, это проверить поле длины (которое вы подразумевали, это первые 4 байта в обеих структурах). Если это 16, мы сделали; если нет, мы можем просто продолжать цикл до 64 байтов.

В идеале мы хотим читать в один буфер размером 64 байта вместо чтения 16-байтового буфера и 48-байтового буфера, а затем записывать данные вокруг, чтобы объединить их. Учитывая это, мы можем просто использовать union, чтобы позволить нам recv в 64-байтовом массиве символов, но затем получить доступ к первым 16 байтам как reply_type_1 или целому как reply_type_2.

Итак, вот быстрый и грязный пример (без обработки ошибок, EOF и т. Д.):

typeset union {
    reply_type_1 r1;
    reply_type_2 r2;
    char buf[64];
} msg_type;

int len = 0;
msg_type msg;
while (len < sizeof(reply_type_1)) {
    n = recv(sock, &msg.buf[len], sizeof(reply_type_1)-len, 0);
    len += n;
}
if msg.r1.length > sizeof(reply_type_1) {
    while (len < sizeof(reply_type_2)) {
        n = recv(sock, &msg.buf[len], sizeof(reply_type_2)-len, 0);
        len += n;
    }
    do_stuff_r2(msg.r2);
} else {
    do_stuff_r1(msg.r1);
}

Сохранение в msg.buf и последующий доступ к msg.r1 - это то же самое, что просто получить char buf[64] и затем выполнить do_stuff_r1(*reinterpret_cast<reply_type_1 *>(&buf)). Таким образом, если do_stuff_r1 принимает reply_type_1, он получает копию первых 16 байтов в виде структуры reply_type_1, а если он принимает reply_type_1 &, он получает ссылку на первые 16 байтов как reply_type_1 struct.

0 голосов
/ 11 мая 2018

Один из способов справиться с этим - использовать общий заголовок, от которого наследуются все ответные пакеты, с полем типа (и часто размера). Тогда все, что вам нужно сделать, - это проверить тип и привести в соответствие соответственно.

Вам потребуется усовершенствовать код, чтобы справиться со случаем, когда вы получаете неполный пакет (или более одного пакета одновременно), но основы этого будут:

struct replyCommon
{
    short type;
};

struct replyOne : public replyCommon
{
    int a, b, c, d;
};

struct replyTwo : public replyCommon
{
    int a, b, c;
    char d[48];
    int e;
};

#define DEFAULT_BUFLEN 128

int iResult;
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;

iResult = recv(connectSocket, recvbuf, recvbuflen, 0)

if(iResult > sizeof(replyCommon))
{
    if (reinterpret_cast<replyCommon*>(recvbuf)->type == 1)
        handleReplyOne(reinterpret_cast<replyOne*>(recvbuf));
    else
    if (reinterpret_cast<replyCommon*>(recvbuf)->type == 2)
        handleReplyTwo(reinterpret_cast<replyTwo*>(recvbuf));

}
else if(iResult == 0)
{
  connectSocket = INVALID_SOCKET;
}
...