Вопрос по UDP сокетам

Тема в разделе "WASM.NETWORKS", создана пользователем _DEN_, 13 фев 2009.

  1. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Есть сервер и клиенты, которые общаются по UDP в неизвестном формета. Мне нужно сделать UDP-прокси между сервером и клиентами (выдать себя за настоящий сервер). О формате общения я знаю только что оно происходит по принципу запрос-ответ.

    Т.к. ответы от реального сервера будут неотличимы с точки зрения того, для какого клиента они предназначены, я решил сделать так: клиенты приходят с запросом, я ставлю их в очередь, достаю по одному запросу, шлю реальному серверу, получаю ответ, и отдаю клиенту.

    Сначала я сделал так:

    Поток 1:

    request = recvfrom(client); // принимаем запрос от клиента
    request_queue.push(reqest); // ставлю его в очередь


    Поток 2:

    request = request_queue.pop();
    sendto(server, request);
    recvfrom(server, response);
    sendto(client, response);


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

    Тогда я решил что у меня должен быть один главный сокет, забайндиный на тот самый порт, и с которого я буду слать ответы. Но и это не удалось - после того как клиент закрывался, вызов recvfrom для этого сокета заканчивался ошибкой WSAECONNRESET 10054 An existing connection was forcibly closed by the remote host. Не понимаю, как такая ошибка может быть в UDP протоколе?

    Так как на один и тот же порт нельзя забайндить несколько сокетов, получается, что в UDP нельзя одновременно общаться с несколькими клиентами? Можно только последовательно отвечать на входящие запросы?

    Тогда непонятно, как быть, если между запросом и ответом проходит много времени, а клиенты ходят часто?
     
  2. like

    like New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2008
    Сообщения:
    21
    Адрес:
    Чебоксары
    Я предпологаю,что в поле порта отправителя,запроса к серверу от твоего прокси нужно указать другой порт;забиндить еще один сокет на этот порт,чтобы слушать оба сокета. И RAW сокеты тебе в помощь.
     
  3. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    like

    Я так и делаю!

    Один сокет - принимает запрос от клиента, и отправляет ему ответ. Второй сокет - отправляет запрос к настоящему серверу и читает с него ответ.
    Проблема в том, что первый сокет после получения запроса как бы прибивается к этому клиенту, и когда клиент уходит, умирает и сокет, что для UDP мне совершенно не ясно.
     
  4. like

    like New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2008
    Сообщения:
    21
    Адрес:
    Чебоксары
    Очень странно.Листинг программы в студию,можно не целиком, только важные моменты (socket(),bind(),sendto(),recevfrom()).
     
  5. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Схематично:

    udp_accept_thread - принимает запрос и вставляет в очередь
    udp_interact_thread - обрабатывает очередь запросов

    За network::socket_ptr кроется винсокет, мемберфункции - одноименны

    Код (Text):
    1. udp_accept_thread()
    2. {
    3.     udp_interface = network::socket_ptr(new network::socket(network::socket::udp)); // Этот же сокет используется в следующем потоке
    4.     udp_interface->bind(udp); // Это IP+Port, на который принимаются пакеты от клиентов
    5.  
    6.     std::size_t const maxsize = 1024;
    7.  
    8.     while()
    9.     {
    10.         content.resize(maxsize);
    11.         content.resize(sck->receivefrom(addr, &*content.begin(), content.size()));
    12.  
    13.         acquire_object(udp_queue); // Очередь запросов, RAII для захвата мутекса
    14.         udp_queue.push_back(addr + content);
    15.     }
    16. }
    17.  
    18. udp_interact_thread()
    19. {
    20.     network::socket_ptr sck(new network::socket(network::socket::udp)); // Это сокет для общения с настоящим сервером
    21.  
    22.     std::size_t const maxsize = 1024;
    23.    
    24.     while()
    25.     {
    26.         request_queue local_queue;
    27.         {
    28.             acquire_object(udp_queue);
    29.             local_queue.swap(udp_queue);
    30.         }
    31.         if(local_queue.empty())
    32.         {
    33.             sleep(1);
    34.         }
    35.         else while(!local_queue.empty())
    36.         {
    37.             request const& req = local_queue.front();
    38.             sck->sendto(server, &*req.content.begin(), req.content.size());
    39.  
    40.             response.content.resize(sck->receivefrom(response.addr, &*response.content.begin(), response.content.size()));
    41.  
    42.             udp_interface->sendto(req.source, &*response.content.begin(), response.content.size());
    43.  
    44.             local_queue.pop_front();
    45.         }
    46.     }
    47. }
     
  6. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Вот что сказано про ошибки recvfrom:

    WSAECONNRESET - The virtual circuit was reset by the remote side executing a hard or abortive close. The application should close the socket; it is no longer usable. On a UDP-datagram socket this error indicates a previous send operation resulted in an ICMP Port Unreachable message.

    Как это понимать?

    ---

    И еще: только что выяснилось, что существует событие, при котором взаимодействие происходит по принципу "запрос-запрос-ответ-ответ", что никак не вписывается в текущий алгоритм :dntknw:
     
  7. like

    like New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2008
    Сообщения:
    21
    Адрес:
    Чебоксары
    Попробуй использовать select().
     
  8. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Короче удалил все нафик, обдумал все еще раз, написал все с нуля и все заработало как часы. Плюс ко всему изначальная мысль о том, что будут проблемы с конкурирующим проксируемым трафиком оказалась фигней - клиенты работают параллельно через один прокси без проблем.

    Бывает...
     
  9. Madslyy

    Madslyy New Member

    Публикаций:
    0
    Регистрация:
    14 мар 2009
    Сообщения:
    1
    Вот и правильно.