Программирование игр, создание игрового движка, OpenGL, DirectX, физика, форум
GameDev.ru / Программирование / Форум / C++, Winsock2, узнать, пришли ли данные на сокет UDP

C++, Winsock2, узнать, пришли ли данные на сокет UDP

Страницы: 1 2 Следующая »
RammПостоялецwww12 июня 201812:50#0
Добрый день!
В Winsock функция recvfrom приостанавливает работу потока до тех пор, пока что-нибудь не придет на сокет.
В C# можно узнать, пришли ли какие-либо данные на сокет с помощью
if (udp_socket.Available > 0)
и только после этого лезть их снимать.
В Winsock такой штуки нет. Пытаюсь использовать функцию select для обнаружения изменения состояния сокета.
Делаю так:
//Создаю udp-сокет s, забиндил его на определенный порт

//объявляю и забиваю все необходимое для работы функции select, в т.ч. нулевой интервал
  fd_set socket_set_1 = { 1, s };
  timeval timeout_1 = { 0, 0 };
  FD_ZERO(&socket_set_1);
  FD_SET(s, &socket_set_1);
  while (1)
  {
//в цикле проверяю состояние сокета, если нет ошибок
    int select_res = select(0, &socket_set_1, 0, 0, &timeout_1);
    if (select_res == SOCKET_ERROR)
    {
      std::cout << "Error select " << WSAGetLastError() << std::endl;
      break;
    }
    else
      if ((select_res != SOCKET_ERROR) && (select_res != 0))
      {

        count_bytes = recvfrom(
          s,
          buff,
          sizeof(buff),
          0,
          (sockaddr *)&client_addr,
          &client_addr_size);
        std::cout << count_bytes << " bytes in message" << std::endl;
      }
  }
Постоянно выбивает ошубку 10022...
1. Правильно ли я понимаю, что функция select отлавливает любое изменение сокета?
2. Что я делаю не так (и как исправить)?
3. Если нужно будет добавить еще и отправку с этого сокета, то что делать с select-ом, после отправки она выдаст, что произошло изменение, но никаких данных не пришло. Так?
4. Есть ли альтернатива этой функции? Можно ли каким-нибудь другим способом узнать, пришло ли что-нибудь на сокет или нет?

Правка: 12 июня 2018 12:51

0iStalkerМодераторwww12 июня 201812:58#1
#ifdef WIN32
  ioctlsocket(socket,FIONREAD,&bytes_available);
#else
#error not a Windows OS
#endif

Правка: 12 июня 2018 12:58

kiparУчастникwww12 июня 201813:08#2
Можно еще
WSAEventSelect(socket, read_event, FD_READ);
ZabПостоялецwww12 июня 201813:36#3
Ramm
1. Массив для select надо заполнять перед каждым вызовом. Он его изменяет в процессе работы, возвращает через него же список изменившихся сокетов.
2. Заполнять fd_set надо исключительно макросами, иначе будет непереносимо. Это под виндой там массив номеров сокетов, а под юниксом битовая маска.
3. Надо бы сокет переводить в неблокирующий режим с помощью fcntl, иначе все равно управление может зависнуть на recvfrom.
RammПостоялецwww12 июня 201814:06#4
0iStalker
u_long iMode = 1;
    int iResult = ioctlsocket(s, FIONBIO, &iMode);
    if (iResult != NO_ERROR)
    {
      printf("ioctlsocket failed with error: %ld\n", iResult);
    }
Итак, воткнул этот код, выкинул select, теперь неблокирующий режим включился.
Если на сокете ничего нет, функция recvfrom возвращает -1. Если что-то есть - количество байтов.
Вопрос, стандартный способ отлова ошибок, типа такого:
      if (count_bytes == SOCKET_ERROR)
      {
        std::cout << "Error " << WSAGetLastError() << std::endl;
      }
Выдает ошибку 10035.
1. Это нормально?)
2. Я правильно понимаю, что сокет остается синхронным?  Но при этом не блокирующий?
3. Если я планирую еще и отправлять с этого сокета, то это все еще можно делать?
Т.е. в основном потоке смотреть на сокет и разбирать входящие сообщения n миллисекунд, а затем переходить на отправку вне зависимости от того, все ли прочитал или нет.
4. Он всегда обработает сообщение целиком? Не будет такого, что из-за отсутствия блокировки прочитается только часть сообщения?
kipar
> WSAEventSelect(socket, read_event, FD_READ);
Это вот так, например, ее использовать?
WSAEVENT hEvent = WSACreateEvent();
    WSAEventSelect(s, hEvent, FD_READ);
    int result = WSAWaitForMultipleEvents(1, &hEvent, FALSE, 0, FALSE);
    if(result==0)
    {
      std::cout << "recvfrom" << std::endl;
      count_bytes = recvfrom(
        s,
        buff,
        sizeof(buff),
        0,
        (sockaddr *)&client_addr,
        &client_addr_size);
      std::cout << count_bytes << " bytes in message" << std::endl;
      if (count_bytes == SOCKET_ERROR)
      {
        std::cout << "Error " << WSAGetLastError() << std::endl;
      }
    }
RammПостоялецwww12 июня 201814:17#5
Zab
> 1. Массив для select надо заполнять перед каждым вызовом. Он его изменяет в
> процессе работы, возвращает через него же список изменившихся сокетов.
Я вот эту механику не догнал, т.е. да, все работает, но как так, я все заполняю и тут же в селекте проверяю его состояние? Или изменение? А как он может отследить изменение, если я все только что заполнил?
Zab
> иначе все равно управление может зависнуть на recvfrom.
Т.е. в этом случае select и WSAEventSelect - не вариант?
ZabПостоялецwww12 июня 201814:21#6
Ramm
> 4. Он всегда обработает сообщение целиком? Не будет такого, что из-за
> отсутствия блокировки прочитается только часть сообщения?
UDP-сообщения всегда приходят целиком и неповрежденными. Или не приходят вовсе...
Ты не сможешь принимать его по частям, даже если захочешь. После первого обращения к сообщению, оно стирается, независимо от того, целиком ты его получил или только начальный кусок. По этой же причине ты не можешь сначала узнать длину, а потом читать. Запросить длину ты можешь, но при этом сотрешь сообщение.
ZabПостоялецwww12 июня 201814:28#7
Ramm
Задача select'а - ждать. В неблокирующем режиме это единственный вызов, на котором ждать положено, все остальное возвращает ошибку, если совершить операцию не готово.
WSAEventSelect - тоже самое, select - просто обертка над ним, максимально юникс-совместимая, насколько оно вообще совместимо.
select'у ты скармливаешь сразу много сокетов, когда готов к работе будет хотя бы один из них - она отомрет. Какой именно готов к работе можно узнать в fd_set, там будет список готовых на месте полного перечня ожидаемых, который ты туда положил. Для следующего вызова надо снова заполнить его.
kiparУчастникwww12 июня 201814:35#8
Ramm
> Это вот так, например, ее использовать?
Нет, судя по хелпу все равно надо ioctlsocket делать. Это просто чтоб ждать данных не sleepом а событием.
RammПостоялецwww12 июня 201814:49#9
Zab
> Задача select'а - ждать. В неблокирующем режиме это единственный вызов, на
> котором ждать положено, все остальное возвращает ошибку, если совершить
> операцию не готово.
Могу я обойтись вообще без него? Перевести сокеты в неблокирующий режим и просто смотреть на возвращаемое recvfrom значение какое-то время, а потом переходить к отправке сообщений с этого же сокета.
Если это, например, сетевая игра, то мне нужно за 1 кадр, который максимум занимает 1/30 секунды, все сделать, и если наложены ограничения по количеству потоков, я не могу позволить recvfrom повесить единственный поток. Если есть пакеты, я их прочитаю и обработаю, если нет - я их подожду, или же пойду дальше, а recvfrom попробую запустить еще раз, но позже. Пакетов может и не быть вовсе, зато мне может понадобится оправить с сокета данные и т.д.
В той же Юнити (там правда C#) нельзя создавать свои потоки, например. В Unreal Engine 4 тоже нельзя создать свои потоки (насколько я знаю, но я с ним, правда, близко не сталкивался). И вот там неблокирующие сокеты - это выход.
Zab
> WSAEventSelect - тоже самое, select - просто обертка над ним,
Это я понял.
ZabПостоялецwww12 июня 201814:58#10
Ramm
> Могу я обойтись вообще без него? Перевести сокеты в неблокирующий режим и
> просто смотреть на возвращаемое recvfrom значение какое-то время, а потом
> переходить к отправке сообщений с этого же сокета.
Можешь. Но зачем крутить холостые циклы, когда для этого есть select? Впрочем, причина есть. Вызвав select и нарвавшись на ожидание, ты отдаешь управление винде и неизвестно когда получишь его обратно, это неуправляемо. Если у тебя время измеряется десятыми долями секунды, управление можно отдавать смело, а если сотыми - надо взвешивать.

Отправлять сообщение с того же сокета ты можешь в любое время, не дожидаясь окончания процесса приема. Никаких конфликтов не будет. Готов ли сокет к отправке можно узнать тоже через select.

Правка: 12 июня 2018 15:00

RammПостоялецwww12 июня 201814:58#11
kipar
> Нет, судя по хелпу все равно надо ioctlsocket делать. Это просто чтоб ждать
> данных не sleepом а событием.
А как тогда WSAEventSelect настроить, если сокет и отправлять, и принимать должен?
RammПостоялецwww12 июня 201815:00#12
Zab
> Надо бы сокет переводить в неблокирующий режим с помощью fcntl, иначе все равно
> управление может зависнуть на recvfrom.
fcntl - это аналог ioctlsocket, но под unix.
ZabПостоялецwww12 июня 201815:09#13
Не удивлюсь если для UDP сокеты всегда готовы к отправке. Если ты отправляешь слишком много, буфера переполнены, он может выкинуть отправляемое сообщение и это будет нормальным процессом доставки, никаким не сбоем, сообщения имеют право теряться, в том числе и так.
CDПостоялецwww12 июня 201815:58#14
Если только Winsock, то есть асинхронный WSARecvFrom
https://msdn.microsoft.com/en-us/library/windows/desktop/ms741686(v=vs.85).aspx

Правка: 12 июня 2018 15:58

Страницы: 1 2 Следующая »

/ Форум / Программирование игр / Сеть

2001—2018 © GameDev.ru — Разработка игр