diff options
-rw-r--r-- | src/lib/asiolink/Makefile.am | 3 | ||||
-rw-r--r-- | src/lib/asiolink/asiolink.h | 3 | ||||
-rw-r--r-- | src/lib/asiolink/io_completion_cb.h | 3 | ||||
-rw-r--r-- | src/lib/asiolink/io_fetch.cc | 3 | ||||
-rw-r--r-- | src/lib/asiolink/tests/Makefile.am | 4 | ||||
-rw-r--r-- | src/lib/asiolink/tests/io_address_unittest.cc | 5 | ||||
-rw-r--r-- | src/lib/asiolink/tests/recursive_query_unittest.cc | 7 | ||||
-rw-r--r-- | src/lib/asiolink/tests/udp_endpoint_unittest.cc | 55 | ||||
-rw-r--r-- | src/lib/asiolink/tests/udp_socket_unittest.cc | 278 | ||||
-rw-r--r-- | src/lib/asiolink/udp_endpoint.h | 29 | ||||
-rw-r--r-- | src/lib/asiolink/udp_socket.cc | 129 | ||||
-rw-r--r-- | src/lib/asiolink/udp_socket.h | 58 |
12 files changed, 547 insertions, 30 deletions
diff --git a/src/lib/asiolink/Makefile.am b/src/lib/asiolink/Makefile.am index b996f9d4d1..fe4d7b08a5 100644 --- a/src/lib/asiolink/Makefile.am +++ b/src/lib/asiolink/Makefile.am @@ -26,8 +26,9 @@ libasiolink_la_SOURCES += io_socket.cc io_socket.h libasiolink_la_SOURCES += io_message.h libasiolink_la_SOURCES += io_address.cc io_address.h libasiolink_la_SOURCES += io_endpoint.cc io_endpoint.h -libasiolink_la_SOURCES += udp_endpoint.h udp_socket.h +libasiolink_la_SOURCES += udp_endpoint.h libasiolink_la_SOURCES += udp_server.h udp_server.cc +libasiolink_la_SOURCES += udp_socket.h udp_socket.cc libasiolink_la_SOURCES += udp_query.h udp_query.cc libasiolink_la_SOURCES += tcp_endpoint.h tcp_socket.h libasiolink_la_SOURCES += tcp_server.h tcp_server.cc diff --git a/src/lib/asiolink/asiolink.h b/src/lib/asiolink/asiolink.h index 03951ae9df..9e402e32b0 100644 --- a/src/lib/asiolink/asiolink.h +++ b/src/lib/asiolink/asiolink.h @@ -34,6 +34,9 @@ #include <asiolink/io_socket.h> #include <asiolink/io_error.h> +#include <asiolink/udp_endpoint.h> +#include <asiolink/udp_socket.h> + /// \namespace asiolink /// \brief A wrapper interface for the ASIO library. /// diff --git a/src/lib/asiolink/io_completion_cb.h b/src/lib/asiolink/io_completion_cb.h index 422b821329..c6943af5b1 100644 --- a/src/lib/asiolink/io_completion_cb.h +++ b/src/lib/asiolink/io_completion_cb.h @@ -15,9 +15,11 @@ #ifndef __IO_COMPLETION_CB_H #define __IO_COMPLETION_CB_H +#include <asio/error.hpp> #include <asio/error_code.hpp> #include <coroutine.h> +namespace asiolink { /// \brief Asynchronous I/O Completion Callback /// @@ -83,5 +85,6 @@ private: IOCompletionCallback* self_; ///< Pointer to real object }; +} // namespace asiolink #endif // __IO_COMPLETION_CB_H diff --git a/src/lib/asiolink/io_fetch.cc b/src/lib/asiolink/io_fetch.cc index 6ec4e6fb7b..06691140df 100644 --- a/src/lib/asiolink/io_fetch.cc +++ b/src/lib/asiolink/io_fetch.cc @@ -172,6 +172,9 @@ IOFetch::operator()(error_code ec, size_t length) { /// be unnecessary.) data_->buffer->writeData(data_->data.get(), length); + // Finished with this socket, so close it. + data_->socket->close(); + /// We are done stop(SUCCESS); } diff --git a/src/lib/asiolink/tests/Makefile.am b/src/lib/asiolink/tests/Makefile.am index 9b1783c525..7580065f1f 100644 --- a/src/lib/asiolink/tests/Makefile.am +++ b/src/lib/asiolink/tests/Makefile.am @@ -17,13 +17,15 @@ if HAVE_GTEST TESTS += run_unittests run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc -run_unittests_SOURCES += udp_query_unittest.cc run_unittests_SOURCES += io_address_unittest.cc run_unittests_SOURCES += io_endpoint_unittest.cc run_unittests_SOURCES += io_socket_unittest.cc run_unittests_SOURCES += io_service_unittest.cc run_unittests_SOURCES += interval_timer_unittest.cc run_unittests_SOURCES += recursive_query_unittest.cc +run_unittests_SOURCES += udp_endpoint_unittest.cc +run_unittests_SOURCES += udp_query_unittest.cc +run_unittests_SOURCES += udp_socket_unittest.cc run_unittests_SOURCES += run_unittests.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(LOG4CXX_LDFLAGS) diff --git a/src/lib/asiolink/tests/io_address_unittest.cc b/src/lib/asiolink/tests/io_address_unittest.cc index 59bc253a7d..eb48900824 100644 --- a/src/lib/asiolink/tests/io_address_unittest.cc +++ b/src/lib/asiolink/tests/io_address_unittest.cc @@ -56,3 +56,8 @@ TEST(IOAddressTest, Equality) { EXPECT_TRUE(IOAddress("2001:db8::1234") != IOAddress("192.0.2.3")); EXPECT_FALSE(IOAddress("2001:db8::1234") == IOAddress("192.0.2.3")); } + +TEST(IOAddressTest, Family) { + EXPECT_EQ(AF_INET, IOAddress("192.0.2.1").getFamily()); + EXPECT_EQ(AF_INET6, IOAddress("2001:0DB8:0:0::0012").getFamily()); +}
\ No newline at end of file diff --git a/src/lib/asiolink/tests/recursive_query_unittest.cc b/src/lib/asiolink/tests/recursive_query_unittest.cc index ad4e5b4815..8a66f2492b 100644 --- a/src/lib/asiolink/tests/recursive_query_unittest.cc +++ b/src/lib/asiolink/tests/recursive_query_unittest.cc @@ -41,8 +41,13 @@ // if we include asio.hpp unless we specify a special compiler option. // If we need to test something at the level of underlying ASIO and need // their definition, that test should go to asiolink/internal/tests. -#include <asiolink/asiolink.h> +#include <asiolink/recursive_query.h> #include <asiolink/io_socket.h> +#include <asiolink/io_service.h> +#include <asiolink/io_message.h> +#include <asiolink/io_error.h> +#include <asiolink/dns_lookup.h> +#include <asiolink/simple_callback.h> using isc::UnitTestUtil; using namespace std; diff --git a/src/lib/asiolink/tests/udp_endpoint_unittest.cc b/src/lib/asiolink/tests/udp_endpoint_unittest.cc new file mode 100644 index 0000000000..18135ec26b --- /dev/null +++ b/src/lib/asiolink/tests/udp_endpoint_unittest.cc @@ -0,0 +1,55 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include <config.h> + +#include <string> + +#include <gtest/gtest.h> + +#include <asio.hpp> +#include <asiolink/io_address.h> +#include <asiolink/udp_endpoint.h> + +using namespace asiolink; +using namespace std; + +// This test checks that the endpoint can manage its own internal +// asio::ip::udp::endpoint object. + +TEST(UDPEndpointTest, v4Address) { + const string test_address("192.0.2.1"); + const unsigned short test_port = 5301; + + IOAddress address(test_address); + UDPEndpoint endpoint(address, test_port); + + EXPECT_TRUE(address == endpoint.getAddress()); + EXPECT_EQ(test_port, endpoint.getPort()); + EXPECT_EQ(IPPROTO_UDP, endpoint.getProtocol()); + EXPECT_EQ(AF_INET, endpoint.getFamily()); +} + +TEST(UDPEndpointTest, v6Address) { + const string test_address("2001:db8::1235"); + const unsigned short test_port = 5302; + + IOAddress address(test_address); + UDPEndpoint endpoint(address, test_port); + + EXPECT_TRUE(address == endpoint.getAddress()); + EXPECT_EQ(test_port, endpoint.getPort()); + EXPECT_EQ(IPPROTO_UDP, endpoint.getProtocol()); + EXPECT_EQ(AF_INET6, endpoint.getFamily()); +} diff --git a/src/lib/asiolink/tests/udp_socket_unittest.cc b/src/lib/asiolink/tests/udp_socket_unittest.cc new file mode 100644 index 0000000000..b24a869e0d --- /dev/null +++ b/src/lib/asiolink/tests/udp_socket_unittest.cc @@ -0,0 +1,278 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + + +/// \brief Test of UDPSocket +/// +/// Tests the fuctionality of a UDPSocket by working through an open-send- +/// receive-close sequence and checking that the asynchronous notifications +/// work. + +#include <string> + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <algorithm> +#include <cstdlib> +#include <cstddef> +#include <vector> + +#include <gtest/gtest.h> + +#include <boost/bind.hpp> +#include <boost/shared_ptr.hpp> + +#include <asio.hpp> + +#include <asiolink/io_completion_cb.h> +#include <asiolink/io_service.h> +#include <asiolink/udp_endpoint.h> +#include <asiolink/udp_socket.h> + +using namespace asio; +using namespace asiolink; +using asio::ip::udp; +using namespace std; + +namespace { + +const char* SERVER_ADDRESS = "127.0.0.1"; +const unsigned short SERVER_PORT = 5301; + +// FIXME Shouldn't we send something that is real message? +const char OUTBOUND_DATA[] = "Data sent from client to server"; +const char INBOUND_DATA[] = "Returned data from server to client"; +} + +/// +/// An instance of this object is passed to the asynchronous I/O functions +/// and the operator() method is called when when an asynchronous I/O +/// completes. The arguments to the completion callback are stored for later +/// retrieval. +class UDPCallback : public IOCompletionCallback { +public: + + struct PrivateData { + PrivateData() : + error_code_(), length_(0), called_(false), name_("") + {} + + asio::error_code error_code_; ///< Completion error code + size_t length_; ///< Number of bytes transferred + bool called_; ///< Set true when callback called + std::string name_; ///< Which of the objects this is + }; + + /// \brief Constructor + /// + /// Constructs the object. It also creates the data member pointed to by + /// a shared pointer. When used as a callback object, this is copied as it + /// is passed into the asynchronous function. This means that there are two + /// objects and inspecting the one we passed in does not tell us anything. + /// + /// Therefore we use a boost::shared_ptr. When the object is copied, the + /// shared pointer is copied, which leaves both objects pointing to the same + /// data. + /// + /// \param which Which of the two callback objects this is + UDPCallback(std::string which) : ptr_(new PrivateData()) + { + setName(which); + } + + /// \brief Destructor + /// + /// No code needed, destroying the shared pointer destroys the private data. + virtual ~UDPCallback() + {} + + /// \brief Callback Function + /// + /// Called when an asynchronous I/O completes, this stores the + /// completion error code and the number of bytes transferred. + /// + /// \param ec I/O completion error code passed to callback function. + /// \param length Number of bytes transferred + virtual void operator()(asio::error_code ec, size_t length = 0) { + ptr_->error_code_ = ec; + setLength(length); + setCalled(true); + } + + /// \brief Get I/O completion error code + int getCode() { + return (ptr_->error_code_.value()); + } + + /// \brief Set I/O completion code + /// + /// \param code New value of completion code + void setCode(int code) { + ptr_->error_code_ = asio::error_code(code, asio::error_code().category()); + } + + /// \brief Get number of bytes transferred in I/O + size_t getLength() { + return (ptr_->length_); + } + + /// \brief Set number of bytes transferred in I/O + /// + /// \param length New value of length parameter + void setLength(size_t length) { + ptr_->length_ = length; + } + + /// \brief Get flag to say when callback was called + bool getCalled() { + return (ptr_->called_); + } + + /// \brief Set flag to say when callback was called + /// + /// \param called New value of called parameter + void setCalled(bool called) { + ptr_->called_ = called; + } + + /// \brief Return instance of callback name + std::string getName() { + return (ptr_->name_); + } + + /// \brief Set callback name + /// + /// \param name New value of the callback name + void setName(const std::string& name) { + ptr_->name_ = name; + } + +private: + boost::shared_ptr<PrivateData> ptr_; ///< Pointer to private data +}; + + +// Tests the operation of a UDPSocket by opening it, sending an asynchronous +// message to a server, receiving an asynchronous message from the server and +// closing. +TEST(UDPSocket, SequenceTest) { + + // Common objects. + IOAddress server_address(SERVER_ADDRESS); // Address of target server + UDPEndpoint endpoint(server_address, SERVER_PORT); // Endpoint of target server + IOService service; // Service object for async control + + // The client - the UDPSocket being tested + UDPSocket client(service); // Socket under test + UDPCallback client_cb("Client"); // Async I/O callback function + + // The server - with which the client communicates. For convenience, we + // use the same io_service, and use the endpoint object created for + // the client to send to as the endpoint object in the constructor. + UDPCallback server_cb("Server"); + udp::socket server(service.get_io_service(), endpoint.getASIOEndpoint()); + server.set_option(socket_base::reuse_address(true)); + + // Assertion to ensure that the server buffer is large enough + char data[UDPSocket::MAX_SIZE]; + ASSERT_GT(sizeof(data), sizeof(OUTBOUND_DATA)); + + // Open the client socket - the operation should be synchronous + EXPECT_FALSE(client.open(&endpoint, client_cb)); + + // Issue read on the server. Completion callback should not have run. + server_cb.setCalled(false); + server_cb.setCode(42); // Answer to Life, the Universe and Everything! + UDPEndpoint server_remote_endpoint; + server.async_receive_from(buffer(data, sizeof(data)), + server_remote_endpoint.getASIOEndpoint(), server_cb); + EXPECT_FALSE(server_cb.getCalled()); + + // Write something to the server using the client - the callback should not + // be called until we call the io_service.run() method. + client_cb.setCalled(false); + client_cb.setCode(7); // Arbitrary number + client.async_send(OUTBOUND_DATA, sizeof(OUTBOUND_DATA), &endpoint, client_cb); + EXPECT_FALSE(client_cb.getCalled()); + + // Execute the two callbacks. + service.run_one(); + service.run_one(); + + EXPECT_TRUE(client_cb.getCalled()); + EXPECT_EQ(0, client_cb.getCode()); + EXPECT_EQ(sizeof(OUTBOUND_DATA), client_cb.getLength()); + + EXPECT_TRUE(server_cb.getCalled()); + EXPECT_EQ(0, server_cb.getCode()); + EXPECT_EQ(sizeof(OUTBOUND_DATA), server_cb.getLength()); + + EXPECT_TRUE(equal(&data[0], &data[server_cb.getLength() - 1], OUTBOUND_DATA)); + + // Now return data from the server to the client. Issue the read on the + // client. + client_cb.setLength(12345); // Arbitrary number + client_cb.setCalled(false); + client_cb.setCode(32); // Arbitrary number + UDPEndpoint client_remote_endpoint; // To receive address of remote system + client.async_receive(data, sizeof(data), &client_remote_endpoint, client_cb); + + // Issue the write on the server side to the source of the data it received. + server_cb.setLength(22345); // Arbitrary number + server_cb.setCalled(false); + server_cb.setCode(232); // Arbitrary number + server.async_send_to(buffer(INBOUND_DATA, sizeof(INBOUND_DATA)), + server_remote_endpoint.getASIOEndpoint(), server_cb); + + + // Expect two callbacks to run + service.run_one(); + service.run_one(); + + EXPECT_TRUE(client_cb.getCalled()); + EXPECT_EQ(0, client_cb.getCode()); + EXPECT_EQ(sizeof(INBOUND_DATA), client_cb.getLength()); + + EXPECT_TRUE(server_cb.getCalled()); + EXPECT_EQ(0, server_cb.getCode()); + EXPECT_EQ(sizeof(INBOUND_DATA), server_cb.getLength()); + + EXPECT_TRUE(equal(&data[0], &data[server_cb.getLength() - 1], INBOUND_DATA)); + + // Check that the address/port received by the client corresponds to the + // address and port the server is listening on. + EXPECT_TRUE(server_address == client_remote_endpoint.getAddress()); + EXPECT_EQ(SERVER_PORT, client_remote_endpoint.getPort()); + + // Close client and server. + EXPECT_NO_THROW(client.close()); + EXPECT_NO_THROW(server.close()); +} diff --git a/src/lib/asiolink/udp_endpoint.h b/src/lib/asiolink/udp_endpoint.h index 27541e0489..0958af6e4d 100644 --- a/src/lib/asiolink/udp_endpoint.h +++ b/src/lib/asiolink/udp_endpoint.h @@ -33,6 +33,16 @@ public: /// \name Constructors and Destructor. /// //@{ + + /// \brief Default Constructor + /// + /// Creates an internal endpoint. This is expected to be set by some + /// external call. + UDPEndpoint() : + asio_endpoint_placeholder_(new asio::ip::udp::endpoint()), + asio_endpoint_(*asio_endpoint_placeholder_) + {} + /// \brief Constructor from a pair of address and port. /// /// \param address The IP address of the endpoint. @@ -50,27 +60,27 @@ public: /// corresponding ASIO class, \c udp::endpoint. /// /// \param asio_endpoint The ASIO representation of the UDP endpoint. - UDPEndpoint(const asio::ip::udp::endpoint& asio_endpoint) : + UDPEndpoint(asio::ip::udp::endpoint& asio_endpoint) : asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint) {} /// \brief The destructor. - ~UDPEndpoint() { delete asio_endpoint_placeholder_; } + virtual ~UDPEndpoint() { delete asio_endpoint_placeholder_; } //@} - inline IOAddress getAddress() const { + virtual IOAddress getAddress() const { return (asio_endpoint_.address()); } - inline uint16_t getPort() const { + virtual uint16_t getPort() const { return (asio_endpoint_.port()); } - inline short getProtocol() const { + virtual short getProtocol() const { return (asio_endpoint_.protocol().protocol()); } - inline short getFamily() const { + virtual short getFamily() const { return (asio_endpoint_.protocol().family()); } @@ -79,10 +89,13 @@ public: inline const asio::ip::udp::endpoint& getASIOEndpoint() const { return (asio_endpoint_); } + inline asio::ip::udp::endpoint& getASIOEndpoint() { + return (asio_endpoint_); + } private: - const asio::ip::udp::endpoint* asio_endpoint_placeholder_; - const asio::ip::udp::endpoint& asio_endpoint_; + asio::ip::udp::endpoint* asio_endpoint_placeholder_; + asio::ip::udp::endpoint& asio_endpoint_; }; } // namespace asiolink diff --git a/src/lib/asiolink/udp_socket.cc b/src/lib/asiolink/udp_socket.cc new file mode 100644 index 0000000000..fb6ab9cf77 --- /dev/null +++ b/src/lib/asiolink/udp_socket.cc @@ -0,0 +1,129 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include <config.h> +#include <iostream> +#include <unistd.h> // for some IPC/network system calls +#include <sys/socket.h> +#include <netinet/in.h> + +#include <boost/bind.hpp> + +#include <asio.hpp> +#include <asio/deadline_timer.hpp> + +#include <boost/shared_ptr.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> + +#include <dns/buffer.h> +#include <dns/message.h> +#include <dns/messagerenderer.h> +#include <log/dummylog.h> +#include <dns/opcode.h> +#include <dns/rcode.h> + +#include <coroutine.h> +#include <asiolink/asiolink.h> + +using namespace asio; +using asio::ip::udp; + +using namespace std; +using namespace isc::dns; + +namespace asiolink { + +// Constructor - create socket on the fly + +UDPSocket::UDPSocket(IOService& service) : + socket_ptr_(new asio::ip::udp::socket(service.get_io_service())), + socket_(*socket_ptr_) +{ +} + +// Destructor + +UDPSocket::~UDPSocket() +{ + delete socket_ptr_; +} + +// Open the socket. Throws an error on failure +// TODO: Make the open more resolient + +bool +UDPSocket::open(const IOEndpoint* endpoint, IOCompletionCallback&) { + if (endpoint->getFamily() == AF_INET) { + socket_.open(asio::ip::udp::v4()); + } + else { + socket_.open(asio::ip::udp::v6()); + } + + // Ensure it can send and receive 4K buffers. + socket_.set_option(asio::socket_base::send_buffer_size(MAX_SIZE)); + socket_.set_option(asio::socket_base::receive_buffer_size(MAX_SIZE)); +; + // Allow reuse of an existing port/address + socket_.set_option(asio::socket_base::reuse_address(true)); + + return (false); +} + +// Send a message. + +void +UDPSocket::async_send(const void* data, size_t length, + const IOEndpoint* endpoint, IOCompletionCallback& callback) +{ + // Upconverting. Not nice, but we have the problem that in the abstract + // layer we are given an IOEndpoint. For UDP code it is a UDPEndpoint + // and for TCP code a TCPEndpoint. However the member that we are + // after - the asio endpoint - is different for UPD and TCP and there is + // no common ancestor. Hence the promotion here. + assert(endpoint->getProtocol() == IPPROTO_UDP); + const UDPEndpoint* udp_endpoint = static_cast<const UDPEndpoint*>(endpoint); + + socket_.async_send_to(buffer(data, length), udp_endpoint->getASIOEndpoint(), + callback); +} + +// UDPSocket::receive_from + +void +UDPSocket::async_receive(void* data, size_t length, IOEndpoint* endpoint, + IOCompletionCallback& callback) +{ + // Upconvert the endpoint again. + assert(endpoint->getProtocol() == IPPROTO_UDP); + UDPEndpoint* udp_endpoint = static_cast<UDPEndpoint*>(endpoint); + + socket_.async_receive_from(buffer(data, length), + udp_endpoint->getASIOEndpoint(), callback); +} + +// Cancel I/O on the socket +void +UDPSocket::cancel() { + socket_.cancel(); +} + +// Close the socket down + +void +UDPSocket::close() { + socket_.close(); +} + +} // namespace asiolink diff --git a/src/lib/asiolink/udp_socket.h b/src/lib/asiolink/udp_socket.h index b22a0f3343..4522141be8 100644 --- a/src/lib/asiolink/udp_socket.h +++ b/src/lib/asiolink/udp_socket.h @@ -19,7 +19,9 @@ #error "asio.hpp must be included before including this, see asiolink.h as to why" #endif +#include <cstddef> #include <asiolink/io_socket.h> +#include <asiolink/io_service.h> namespace asiolink { @@ -29,28 +31,46 @@ namespace asiolink { /// Other notes about \c TCPSocket applies to this class, too. class UDPSocket : public IOSocket { private: - UDPSocket(const UDPSocket& source); - UDPSocket& operator=(const UDPSocket& source); + /// \brief Class is non-copyable + UDPSocket(const UDPSocket&); + UDPSocket& operator=(const UDPSocket&); + public: + enum { + MAX_SIZE = 4096 // Send and receive size + }; + /// \brief Constructor from an ASIO UDP socket. /// /// \param socket The ASIO representation of the UDP socket. - UDPSocket(asio::ip::udp::socket& socket) : socket_(socket) {} + UDPSocket(asio::ip::udp::socket& socket) : + socket_ptr_(NULL), socket_(socket) + {} + + /// \brief Constructor + /// + /// Used when the UDPSocket is being asked to manage its own internal + /// socket. + UDPSocket(IOService& service); + + /// \brief Destructor + virtual ~UDPSocket(); virtual int getNative() const { return (socket_.native()); } virtual int getProtocol() const { return (IPPROTO_UDP); } /// \brief Open Socket /// - /// No-op for UDP sockets + /// Opens the UDP socket. In the model for transport-layer agnostic I/O, + /// an "open" operation includes a connection to the remote end (which + /// may take time). This does not happen for UDP, so the method returns + /// "false" to indicate that the operation completed synchronously. /// - /// \param endpoint Unused. + /// \param endpoint Endpoint to which the socket will connect to. /// \param callback Unused. /// /// \return false to indicate that the "operation" completed synchronously. - virtual bool open(const IOEndpoint*, IOCompletionCallback&) { - return false; - } + virtual bool open(const IOEndpoint* endpoint, IOCompletionCallback&); /// \brief Send Asynchronously /// @@ -62,9 +82,8 @@ public: /// \param length Length of data to send /// \param endpoint Target of the send /// \param callback Callback object. - virtual void async_send(const void*, size_t, - const IOEndpoint*, IOCompletionCallback&) { - } + virtual void async_send(const void* data, size_t length, + const IOEndpoint* endpoint, IOCompletionCallback& callback); /// \brief Receive Asynchronously /// @@ -77,21 +96,22 @@ public: /// \param length Length of the data buffer /// \param endpoint Source of the communication /// \param callback Callback object - virtual void async_receive(void* data, size_t, IOEndpoint*, - IOCompletionCallback&) { - } + virtual void async_receive(void* data, size_t length, IOEndpoint* endpoint, + IOCompletionCallback& callback); /// \brief Cancel I/O On Socket - virtual void cancel() { - } + virtual void cancel(); /// \brief Close socket - virtual void close() { - } + virtual void close(); private: - asio::ip::udp::socket& socket_; + // Two variables to hold the socket - a socket and a pointer to it. This + // handles the case where a socket is passed to the UDPSocket on + // construction, or where it is asked to manage its own socket. + asio::ip::udp::socket* socket_ptr_; ///< Pointer to the socket + asio::ip::udp::socket& socket_; ///< Socket }; } // namespace asiolink |