как обрабатывать пакеты в многопоточной серверной клиентской программе? - PullRequest
1 голос
/ 09 мая 2011

У меня сейчас есть клиентское приложение, которое работает, но оно однопоточное.

мои пакеты выглядят так: | "

"|" используется в качестве разделителя для моих данных.

всегда сопровождается 4 цифрами.

<данные> выглядит следующим образом: | <идентификатор транзакции> | <команда> | | | <контрольная сумма> |

мой код для создания пакетов:

_snprintf_s(data_buffer, WS_MAX_DATA_PACKET_SIZE, 
               WS_MAX_DATA_PACKET_SIZE - 1,
               "%s%d%s%d%s%d%s%s%s%d%s", 
               WS_PACKET_SEP, pkt->transaction_id, 
               WS_PACKET_SEP, pkt->command, 
               WS_PACKET_SEP, pkt->bufsize, 
               WS_PACKET_SEP, pkt->buf, 
               WS_PACKET_SEP, pkt->checksum, WS_PACKET_SEP);

buf_len = strlen(data_buffer);

_snprintf_s(send_buffer, WS_MAX_DATA_PACKET_SIZE, 
            WS_MAX_DATA_PACKET_SIZE - 1, "%04d%s%s", 
            buf_len, WS_PACKET_SEP, data_buffer);

buf_len = strlen(send_buffer);
// Send buffer
bytes_sent = send(ConnectSocket, send_buffer, buf_len, 0);

Клиентский поток отправляет команду серверу, а затем вызывает функцию GetIncomingPackets (). В GetIncomingPackets () я вызываю recv () для получения 5 байтов, это должно быть длина остальной части пакета, я анализирую эти 5 байтов и проверяю, соответствуют ли они моему ожидаемому формату. Затем я конвертирую первые 4 байта в целое число x. Затем я снова вызываю recv (), чтобы получить больше x байтов, а затем анализирую их в моей структуре пакета.

Проблема возникает, когда я добавляю другой поток, чтобы сделать то же самое (отправлять и получать команды). Я запускаю свое приложение, запускаю 2 потока и отправляю их для отправки разных команд и жду ответов. Когда потоки вызывают GetIncomingPackets (), возвращаемые данные являются недействительными. Первые 5 байтов, которые я ожидаю, иногда отсутствуют, и я просто получаю следующие 5 байтов, поэтому я не могу получить свой пакет .

Я даже добавил блок критической секции между 2 вызовами recv () в моем GetIncomingPackets (), чтобы протекторы не прерывали друг друга при получении полного пакета. Без дополнительного кода для проверки ошибок, вот так выглядит функция

#define WS_SIZE_OF_LEN_PACKET 5
bool GetIncomingPackets(SOCKET sd, dev_sim_packet_t *pkt )
{
    char len_str_buf[WS_SIZE_OF_LEN_PACKET + 1] = {0};      // + 1 for NULL char
    char data_buf[WS_MAX_DATA_PACKET_SIZE + 1] = {0};
    int ret = 0;
    int data_len = 0;

    EnterCriticalSection( &recv_critical_section );
    nReadBytes = WS_RecvAll(sd, len_str_buf, WS_SIZE_OF_LEN_PACKET );
    ret = WS_VerifyLenPacket(len_str_buf);
    // Convert data packet lenght string received to int 
    data_len = WS_ConvertNumberFromString(len_str_buf, WS_SIZE_OF_LEN_PACKET );   
    // Get data from packet
    nReadBytes = WS_RecvAll(sd, data_buf, data_len);
    LeaveCriticalSection( &recv_critical_section  );
    ret = ParseMessager(data_buf, data_len, pkt);
}

Мой вопрос: что может быть причиной этой проблемы и как я могу ее исправить? Или есть лучшие способы сделать то, что я пытаюсь сделать. Причина, по которой я пытаюсь сделать его многопоточным, заключается в том, что мое приложение будет взаимодействовать с 2 другими источниками, и я хочу иметь поток для обработки каждого запроса, поступающего из любого источника.

спасибо заранее и не стесняйтесь задавать любые вопросы, если я не объяснил что-то хорошо.

Вот код для WS_RecvAll (). Буфер является статическим буфером, объявленным в GetIncomingPackets () следующим образом:

  char data_buf[WS_MAX_DATA_PACKET_SIZE + 1] = {0};   // + 1 for NULL char


int WS_RecvAll(SOCKET socket_handle, char* buffer, int size)
{
    int ret = 0;
    int read = 0;
    int i = 0;
    char err_buf[100] = {0};
    while(size)
    {
        ret = recv(socket_handle, &buffer[read], size, 0);
        if (ret == SOCKET_ERROR)
        {
            printf("***ERROR***: recv failed, error = %d\n", WSAGetLastError());
            return WS_ERROR_RECV_FAILED;
        }
        if (ret == 0) {
            break;
        }
        read += ret;
        size -= ret;
     }
     return read;
 }

Ответы [ 2 ]

1 голос
/ 09 мая 2011

Очень сложно отлаживать проблемы MT, особенно при одном удалении, но если вы используете астатический буфер, не следует:

 LeaveCriticalSection( &recv_critical_section  );
 ret = ParseMessager(data_buf, data_len, pkt);

быть:

 ret = ParseMessager(data_buf, data_len, pkt);
 LeaveCriticalSection( &recv_critical_section  );

А зачем использовать статический буфер в любом случае?

0 голосов
/ 09 мая 2011

Мне любопытно узнать, использовали ли вы один и тот же дескриптор socked в обоих потоках для подключения к серверу.

...