Вопросы о многопоточной архитектуре клиент-сервер UDP - PullRequest
0 голосов
/ 26 января 2019

Я немного практикуюсь с сокетами и архитектурой клиент-сервер UDP, и, ссылаясь на некоторые примеры, доступные в Интернете, я реализовал очень простой сервер UDP, использующий C, и класс клиента UDP, использующий C ++.
Вкратце, текущая реализация позволяет серверу прослушивать входящие сообщения и передавать обратно тот же пакет клиенту.
Кажется, что он работает нормально, если клиент делает последовательные запросы.
Вот краткий пояснительный пример:

#include "UDPClient.h"
int main(int argc, char* argv[]) {
  UDPClient testClient;
  testClient.initSockets(1501, "127.0.0.1", 1500);
  for (int i = 0; i < 10; i++) {
    testClient.notifyEntry();
    testClient.notifyExit();
  }
  return 0;
}

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

#include <thread>
#include "UDPClient.h"
int main(int argc, char* argv[]) {
  UDPClient testClient;
  std::thread thrdOne, thrdTwo;
  testClient.initSockets(1501, "127.0.0.1", 1500);
  for (int i = 0; i < 10; i++) {
    thrdOne = std::thread(UDPClient::notifyEntry, std::ref(testClient));
    thrdTwo = std::thread(UDPClient::notifyExit, std::ref(testClient));
  }
  return 0;
}

Как видите, notifyEntry и notifyExit были сделаны static и в настоящее время для правильной работы требуется ссылка на экземпляр класса. Кроме того, в их тело функции я также добавил небольшой блок кода для проверки того, что, поскольку сервер отправляет обратно тот же контент, отправленное сообщение равно полученному.
Вот пояснительный пример:

void UDPClient::notifyEntry(UDPClient& inst) {
  char buffer = "E"
  inst.sendPacket(buffer);  // sendto...
  inst.receivePacket(buffer);  // recvfrom...
  if (!(buffer == 'E') ){
    std::string e = "Buffer should be E but it is ";
    e.append(buffer);
    throw UDPClientException(e);
  }
}

При использовании многопоточности часто случается, что вышеупомянутая проверка выдает исключение, потому что буфер фактически содержит другой char (тот, который был отправлен notifyExit).

Учитывая эту информацию, я хотел бы спросить вас:

  1. это происходит из-за того, что recvfrom потока может также перехватить ответ на запрос другого, поскольку сокет создает только один связанный сокет?
  2. если да, должен ли я создавать более одного сокета (например, каждый из которых может использоваться только для одного типа сообщений, то есть один для notifyEntry и один для notifyExit)? Разве многопоточность на сервере только для ответа не решает проблему, упомянутую в любом случае?

1 Ответ

0 голосов
/ 27 января 2019

это происходит из-за того, что recvfrom потока может также перехватить ответ на запрос другого, поскольку сокет создает только один связанный сокет?

Это очень вероятно -если у вас есть несколько потоков, вызывающих recvfrom() на одном и том же сокете UDP, то будет неопределенным / непредсказуемым, какой поток получит какой входящий пакет UDP.

, если да, я должен создать более одного сокета(например, каждое из них может использоваться только для одного типа сообщений, то есть для notifyEntry и для notifyExit)?

Да, я бы рекомендовал каждому потоку создавать свой собственный частный сокет UDPи связать () свой сокет с собственным портом (например, передав 0 в качестве номера порта bind());таким образом, каждый поток может быть уверен, что получит только свои собственные ответы и не будет сбит с толку ответами, предназначенными для других потоков.(Обратите внимание, что вы также захотите закодировать свой сервер, чтобы отправлять его ответы обратно на IP-адрес и порт, о которых сообщал вызов recvfrom(), вместо того, чтобы отправлять ответные пакеты обратно на номер порта с жестким кодом)

Разве многопоточность на сервере только для ответа не решает проблему, упомянутую в любом случае?

Нет, правильная обработка пакетов UDP (или нет) является отдельной проблемой, которая не зависит от того,сервер является однопоточным или многопоточным.

...