2013-06-06 4 views
3

Извините, если вопрос был ранее получен, но я искал и не нашел ничего, что мне помогло. Как указано в заголовке вопроса, я пытаюсь передать пакет с сервера на набор клиентов, которые прослушивают любое сообщение.Проблема с трансляцией с использованием Boost.Asio

Клиент будет подсчитывать количество сообщений, которое он получает в течение одной секунды.

на стороне сервера вещей выглядит следующим образом:

class Server 
{ 
public: 

    Server(boost::asio::io_service& io) 
     : socket(io, udp::endpoint(udp::v4(), 8888)) 
     , broadcastEndpoint(address_v4::broadcast(), 8888) 
     , tickHandler(boost::bind(&Server::Tick, this, boost::asio::placeholders::error)) 
     , timer(io, boost::posix_time::milliseconds(20)) 
    { 
     socket.set_option(boost::asio::socket_base::reuse_address(true)); 
     socket.set_option(boost::asio::socket_base::broadcast(true)); 

     timer.async_wait(tickHandler); 
    } 

private: 

    void Tick(const boost::system::error_code&) 
    { 
     socket.send_to(boost::asio::buffer(buffer), broadcastEndpoint); 

     timer.expires_at(timer.expires_at() + boost::posix_time::milliseconds(20)); 
     timer.async_wait(tickHandler); 
    } 

private: 

    udp::socket socket; 
    udp::endpoint broadcastEndpoint; 

    boost::function<void(const boost::system::error_code&)> tickHandler; 
    boost::asio::deadline_timer timer; 

    boost::array<char, 100> buffer; 

};

Он инициализируется и запустить следующим образом:

int main() 
{ 
    try 
    { 
     boost::asio::io_service io; 
     Server server(io); 
     io.run(); 
    } 
    catch (const std::exception& e) 
    { 
     std::cerr << e.what() << "\n"; 
    } 

    return 0; 
} 

Это (видимо) работает отлично. Теперь приходит клиент ...

void HandleReceive(const boost::system::error_code&, std::size_t bytes) 
{ 
    std::cout << "Got " << bytes << " bytes\n"; 
} 

int main(int argc, char* argv[]) 
{ 
    if (argc != 2) 
    { 
     std::cerr << "Usage: " << argv[0] << " <host>\n"; 
     return 1; 
    } 

    try 
    { 
     boost::asio::io_service io; 

     udp::resolver resolver(io); 
     udp::resolver::query query(udp::v4(), argv[1], "1666"); 

     udp::endpoint serverEndpoint = *resolver.resolve(query); 
     //std::cout << serverEndpoint.address() << "\n"; 

     udp::socket socket(io); 
     socket.open(udp::v4()); 

     socket.bind(serverEndpoint); 

     udp::endpoint senderEndpoint; 
     boost::array<char, 300> buffer; 

     auto counter = 0; 
     auto start = std::chrono::system_clock::now(); 

     while (true) 
     { 
      socket.receive_from(boost::asio::buffer(buffer), senderEndpoint); 
      ++counter; 

      auto current = std::chrono::system_clock::now(); 
      if (current - start >= std::chrono::seconds(1)) 
      { 
       std::cout << counter << "\n"; 

       counter = 0; 
       start = current; 
      } 
     } 
    } 
    catch (const std::exception& e) 
    { 
     std::cerr << e.what() << "\n"; 
    } 

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

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

Я прочитал, что вы должны привязать сокет клиента к адресу address_v4::any(). Я сделал, он не работает (говорит что-то о сокете, уже использующем адрес/порт).

Заранее спасибо.

PS: Я под Windows 8.

ответ

9

Я немного удивлен, что это работает на той же машине. Я не ожидал бы клиента, прослушивает порт 1666, чтобы получить данные, отправляемые на широковещательный адрес на порт 8888.

bind() назначает локальной конечной точки (в состав локального адреса и порта) к разъему. Когда сокет привязывается к конечной точке, он указывает, что сокет будет принимать только данные, отправленные на связанный адрес и порт. Часто рекомендуется связываться с address_v4::any(), так как это будет использовать все доступные интерфейсы для прослушивания. В случае системы с несколькими интерфейсами (возможно, с несколькими картами NIC) привязка к определенному адресу интерфейса приведет к тому, что сокет будет только слушать данные, полученные от указанного интерфейса [1]. Таким образом, можно получить адрес через resolve(), когда приложение хочет привязываться к определенному сетевому интерфейсу и хочет поддерживать его разрешение, предоставляя IP-адрес напрямую (127.0.0.1) или имя (localhost).

Важно отметить, что при привязке к сокету конечная точка состоит из адреса и. Это является источником моего удивления, что он работает на одной машине. Если сервер пишет для трансляции: 8888, сокет, связанный с портом 1666, не должен получать датаграмму. Тем не менее, здесь визуальный конечных точек и сетей:

               .--------. 
                   .--------.| 
.--------. address: any       address: any .--------.|| 
|  | port: any /    \ port: 8888 |  ||| 
| server |-(----------->| address: broadcast |---------->)-| client ||' 
|  |    \ port: 8888 /    |  |' 
'--------'             '--------' 

сервер связывается с любым адресом и любой порт, включает возможность широковещательной передачи, и передает данные в удаленную конечную точку (передача: 8888).Клиенты, привязанные к любому адресу на порту 8888, должны получить данные.

Простой пример:

Сервер:

#include <boost/asio.hpp> 

int main() 
{ 
    namespace ip = boost::asio::ip; 
    boost::asio::io_service io_service; 

    // Server binds to any address and any port. 
    ip::udp::socket socket(io_service, 
         ip::udp::endpoint(ip::udp::v4(), 0)); 
    socket.set_option(boost::asio::socket_base::broadcast(true)); 

    // Broadcast will go to port 8888. 
    ip::udp::endpoint broadcast_endpoint(ip::address_v4::broadcast(), 8888); 

    // Broadcast data. 
    boost::array<char, 4> buffer; 
    socket.send_to(boost::asio::buffer(buffer), broadcast_endpoint); 
} 

Клиент:

#include <iostream> 

#include <boost/asio.hpp> 

int main() 
{ 
    namespace ip = boost::asio::ip; 
    boost::asio::io_service io_service; 

    // Client binds to any address on port 8888 (the same port on which 
    // broadcast data is sent from server). 
    ip::udp::socket socket(io_service, 
         ip::udp::endpoint(ip::udp::v4(), 8888)); 

    ip::udp::endpoint sender_endpoint; 

    // Receive data. 
    boost::array<char, 4> buffer; 
    std::size_t bytes_transferred = 
    socket.receive_from(boost::asio::buffer(buffer), sender_endpoint); 

    std::cout << "got " << bytes_transferred << " bytes." << std::endl; 
} 

Когда клиент не совмещаться с сервером, то это может быть множество вопросов, связанных с сетевыми:

  • Проверьте подключение между сервером и клиент.
  • Проверьте исключения брандмауэра.
  • Проверьте поддержку/исключения широковещания на устройстве маршрутизации.
  • Используйте инструмент сетевого анализатора, такой как Wireshark, чтобы убедиться, что поле time to live в пакетах достаточно велико, чтобы оно не было отброшено во время маршрутизации.

1. В Linux, широковещательные дейтаграммы, полученные с помощью адаптера не будут переданы в сокет, связанный с конкретным интерфейса, а назначения дейтаграммы устанавливаются на широковещательный адрес. С другой стороны, Windows передаст широковещательные датаграммы, полученные адаптером , к сокетам, привязанным к определенному интерфейсу .

+0

Ваш ответ был довольно просвещен, я должен признать. Моя ошибка заключалась в открытии сокета сервера с использованием данного порта вместо порта 0. –

+0

Кроме того, я отправил неправильный код ... это были две версии одной и той же программы, я изменил номер порта в одном из них. –