From 2d9838f467013d5397b6daf83afb5ccea92065a4 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sun, 20 Feb 2011 01:13:03 -0800 Subject: udp: update docs for transport, create common header for wait implementation Reimplemented simple udp transport with one impl class. Moved wait for ready/select implementation into common header. Important note on select, timeval should have usecs < 1 second or it may error on some platforms. Fixed in this implementation. --- host/lib/transport/udp_simple.cpp | 160 +++++++++----------------------------- 1 file changed, 36 insertions(+), 124 deletions(-) (limited to 'host/lib/transport/udp_simple.cpp') diff --git a/host/lib/transport/udp_simple.cpp b/host/lib/transport/udp_simple.cpp index 6799ac7b2..094f570ff 100644 --- a/host/lib/transport/udp_simple.cpp +++ b/host/lib/transport/udp_simple.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2011 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,159 +15,71 @@ // along with this program. If not, see . // +#include "udp_common.hpp" #include -#include -#include #include #include using namespace uhd::transport; +namespace asio = boost::asio; /*********************************************************************** - * Helper Functions + * UDP simple implementation: connected and broadcast **********************************************************************/ -/*! - * Wait for available data or timeout. - * \param socket the asio socket - * \param timeout the timeout in seconds - * \return false for timeout, true for data - */ -static bool wait_available( - boost::asio::ip::udp::socket &socket, double timeout -){ - #if defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32) - - //setup timeval for timeout - timeval tv; - tv.tv_sec = 0; - tv.tv_usec = long(timeout*1e6); - - //setup rset for timeout - fd_set rset; - FD_ZERO(&rset); - FD_SET(socket.native(), &rset); - - return ::select(socket.native()+1, &rset, NULL, NULL, &tv) > 0; - - #else /*defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32)*/ - - //FIXME: why does select fail on macintosh? - for (size_t i = 0; i < size_t(timeout*1e3); i++){ - if (socket.available()) return true; - boost::this_thread::sleep(boost::posix_time::milliseconds(1)); - } - return false; - - #endif /*defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32)*/ -} - -/*********************************************************************** - * UDP connected implementation class - **********************************************************************/ -class udp_connected_impl : public udp_simple{ +class udp_simple_impl : public udp_simple{ public: - //structors - udp_connected_impl(const std::string &addr, const std::string &port); - ~udp_connected_impl(void); + typedef boost::shared_ptr socket_sptr; - //send/recv - size_t send(const boost::asio::const_buffer &); - size_t recv(const boost::asio::mutable_buffer &, double); + udp_simple_impl( + const std::string &addr, const std::string &port, bool bcast, bool connect + ):_connected(connect){ + //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; -private: - boost::asio::ip::udp::socket *_socket; - boost::asio::io_service _io_service; -}; + //resolve the address + asio::ip::udp::resolver resolver(_io_service); + asio::ip::udp::resolver::query query(asio::ip::udp::v4(), addr, port); + _receiver_endpoint = *resolver.resolve(query); -udp_connected_impl::udp_connected_impl(const std::string &addr, const std::string &port){ - //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; + //create and open the socket + _socket = socket_sptr(new asio::ip::udp::socket(_io_service)); + _socket->open(asio::ip::udp::v4()); - // resolve the address - boost::asio::ip::udp::resolver resolver(_io_service); - boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); - boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); + //allow broadcasting + _socket->set_option(asio::socket_base::broadcast(bcast)); - // Create, open, and connect the socket - _socket = new boost::asio::ip::udp::socket(_io_service); - _socket->open(boost::asio::ip::udp::v4()); - _socket->connect(receiver_endpoint); -} - -udp_connected_impl::~udp_connected_impl(void){ - delete _socket; -} - -size_t udp_connected_impl::send(const boost::asio::const_buffer &buff){ - return _socket->send(boost::asio::buffer(buff)); -} + //connect the socket + if (connect) _socket->connect(_receiver_endpoint); -size_t udp_connected_impl::recv(const boost::asio::mutable_buffer &buff, double timeout){ - if (not wait_available(*_socket, timeout)) return 0; - return _socket->receive(boost::asio::buffer(buff)); -} + } -/*********************************************************************** - * UDP broadcast implementation class - **********************************************************************/ -class udp_broadcast_impl : public udp_simple{ -public: - //structors - udp_broadcast_impl(const std::string &addr, const std::string &port); - ~udp_broadcast_impl(void); + size_t send(const asio::const_buffer &buff){ + if (_connected) return _socket->send(asio::buffer(buff)); + return _socket->send_to(asio::buffer(buff), _receiver_endpoint); + } - //send/recv - size_t send(const boost::asio::const_buffer &); - size_t recv(const boost::asio::mutable_buffer &, double); + size_t recv(const asio::mutable_buffer &buff, double timeout){ + if (not wait_for_recv(_socket->native(), timeout)) return 0; + return _socket->receive(asio::buffer(buff)); + } private: - boost::asio::ip::udp::socket *_socket; - boost::asio::ip::udp::endpoint _receiver_endpoint; - boost::asio::io_service _io_service; + bool _connected; + asio::io_service _io_service; + socket_sptr _socket; + asio::ip::udp::endpoint _receiver_endpoint; }; -udp_broadcast_impl::udp_broadcast_impl(const std::string &addr, const std::string &port){ - //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; - - // resolve the address - boost::asio::ip::udp::resolver resolver(_io_service); - boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); - _receiver_endpoint = *resolver.resolve(query); - - // Create and open the socket - _socket = new boost::asio::ip::udp::socket(_io_service); - _socket->open(boost::asio::ip::udp::v4()); - - // Allow broadcasting - boost::asio::socket_base::broadcast option(true); - _socket->set_option(option); - -} - -udp_broadcast_impl::~udp_broadcast_impl(void){ - delete _socket; -} - -size_t udp_broadcast_impl::send(const boost::asio::const_buffer &buff){ - return _socket->send_to(boost::asio::buffer(buff), _receiver_endpoint); -} - -size_t udp_broadcast_impl::recv(const boost::asio::mutable_buffer &buff, double timeout){ - if (not wait_available(*_socket, timeout)) return 0; - boost::asio::ip::udp::endpoint sender_endpoint; - return _socket->receive_from(boost::asio::buffer(buff), sender_endpoint); -} - /*********************************************************************** * UDP public make functions **********************************************************************/ udp_simple::sptr udp_simple::make_connected( const std::string &addr, const std::string &port ){ - return sptr(new udp_connected_impl(addr, port)); + return sptr(new udp_simple_impl(addr, port, false, true /* no bcast, connect */)); } udp_simple::sptr udp_simple::make_broadcast( const std::string &addr, const std::string &port ){ - return sptr(new udp_broadcast_impl(addr, port)); + return sptr(new udp_simple_impl(addr, port, true, false /* bcast, no connect */)); } -- cgit v1.2.3 From 4787c77bb089bf50a47207bbad55bd65a44769e4 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sun, 20 Feb 2011 02:41:03 -0800 Subject: udp: try non-blocking recv first for performance --- host/lib/transport/udp_common.hpp | 4 +++- host/lib/transport/udp_simple.cpp | 4 +--- host/lib/transport/udp_zero_copy.cpp | 24 ++++++++++++++---------- 3 files changed, 18 insertions(+), 14 deletions(-) (limited to 'host/lib/transport/udp_simple.cpp') diff --git a/host/lib/transport/udp_common.hpp b/host/lib/transport/udp_common.hpp index 44067b5dc..47775d9c4 100644 --- a/host/lib/transport/udp_common.hpp +++ b/host/lib/transport/udp_common.hpp @@ -23,13 +23,15 @@ namespace uhd{ namespace transport{ + typedef boost::shared_ptr socket_sptr; + /*! * Wait for the socket to become ready for a receive operation. * \param sock_fd the open socket file descriptor * \param timeout the timeout duration in seconds * \return true when the socket is ready for receive */ - UHD_INLINE bool wait_for_recv(int sock_fd, double timeout){ + UHD_INLINE bool wait_for_recv_ready(int sock_fd, double timeout){ //setup timeval for timeout timeval tv; //If the tv_usec > 1 second on some platforms, select will diff --git a/host/lib/transport/udp_simple.cpp b/host/lib/transport/udp_simple.cpp index 094f570ff..1ee036d52 100644 --- a/host/lib/transport/udp_simple.cpp +++ b/host/lib/transport/udp_simple.cpp @@ -28,8 +28,6 @@ namespace asio = boost::asio; **********************************************************************/ class udp_simple_impl : public udp_simple{ public: - typedef boost::shared_ptr socket_sptr; - udp_simple_impl( const std::string &addr, const std::string &port, bool bcast, bool connect ):_connected(connect){ @@ -58,7 +56,7 @@ public: } size_t recv(const asio::mutable_buffer &buff, double timeout){ - if (not wait_for_recv(_socket->native(), timeout)) return 0; + if (not wait_for_recv_ready(_socket->native(), timeout)) return 0; return _socket->receive(asio::buffer(buff)); } diff --git a/host/lib/transport/udp_zero_copy.cpp b/host/lib/transport/udp_zero_copy.cpp index 793fc6fba..dda3bb547 100644 --- a/host/lib/transport/udp_zero_copy.cpp +++ b/host/lib/transport/udp_zero_copy.cpp @@ -139,7 +139,7 @@ public: asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); //create, open, and connect the socket - _socket = new asio::ip::udp::socket(_io_service); + _socket = socket_sptr(new asio::ip::udp::socket(_io_service)); _socket->open(asio::ip::udp::v4()); _socket->connect(receiver_endpoint); _sock_fd = _socket->native(); @@ -161,10 +161,6 @@ public: } } - ~udp_zero_copy_asio_impl(void){ - delete _socket; - } - //get size for internal socket buffer template size_t get_buff_size(void) const{ Opt option; @@ -182,16 +178,24 @@ public: /******************************************************************* * Receive implementation: * - * Use select to perform a blocking receive with timeout. + * Perform a non-blocking receive for performance, + * and then fall back to a blocking receive with timeout. * Return the managed receive buffer with the new length. * When the caller is finished with the managed buffer, * the managed receive buffer is released back into the queue. ******************************************************************/ managed_recv_buffer::sptr get_recv_buff(double timeout){ udp_zero_copy_asio_mrb *mrb = NULL; - bool recv_ready = wait_for_recv(_sock_fd, timeout); - if (recv_ready and _pending_recv_buffs.pop_with_timed_wait(mrb, timeout)){ - return mrb->get_new(::recv(_sock_fd, mrb->cast(), _recv_frame_size, 0)); + if (_pending_recv_buffs.pop_with_timed_wait(mrb, timeout)){ + + #ifdef MSG_DONTWAIT //try a non-blocking recv() if supported + ssize_t ret = ::recv(_sock_fd, mrb->cast(), _recv_frame_size, MSG_DONTWAIT); + if (ret > 0) return mrb->get_new(ret); + #endif + + if (wait_for_recv_ready(_sock_fd, timeout)) return mrb->get_new( + ::recv(_sock_fd, mrb->cast(), _recv_frame_size, 0) + ); } return managed_recv_buffer::sptr(); } @@ -247,7 +251,7 @@ private: //asio guts -> socket and service asio::io_service _io_service; - asio::ip::udp::socket *_socket; + socket_sptr _socket; int _sock_fd; }; -- cgit v1.2.3