Commit 682436e8 authored by Stephen Morris's avatar Stephen Morris

[trac499] Completed TCPSocket and unit tests

parent b13c2fd0
......@@ -13,6 +13,7 @@ CLEANFILES = *.gcno *.gcda
# which would make the build fail with -Werror (our default setting).
lib_LTLIBRARIES = libasiolink.la
libasiolink_la_SOURCES = asiolink.h
libasiolink_la_SOURCES += asiolink_utilities.h
libasiolink_la_SOURCES += asiodef.cc asiodef.h
libasiolink_la_SOURCES += dns_answer.h
libasiolink_la_SOURCES += dns_lookup.h
......
// 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.
#ifndef __ASIOLINK_UTILITIES_H
#define __ASIOLINK_UTILITIES_H
#include <cstddef>
namespace asiolink {
/// \brief Read Unsigned 16-Bit Integer from Buffer
///
/// This is essentially a copy of the isc::dns::InputBuffer::readUint16. It
/// should really be moved into a separate library.
///
/// \param buffer Data buffer at least two bytes long of which the first two
/// bytes are assumed to represent a 16-bit integer in network-byte
/// order.
///
/// \return Value of 16-bit integer
inline uint16_t
readUint16(const void* buffer) {
const uint8_t* byte_buffer = static_cast<const uint8_t*>(buffer);
uint16_t result = (static_cast<uint16_t>(byte_buffer[0])) << 8;
result |= static_cast<uint16_t>(byte_buffer[1]);
return (result);
}
/// \brief Write Unisgned 16-Bit Integer to Buffer
///
/// This is essentially a copy of isc::dns::OutputBuffer::writeUint16. It
/// should really be moved into a separate library.
///
/// \param value 16-bit value to convert
/// \param buffer Data buffer at least two bytes long into which the 16-bit
/// value is written in network-byte order.
inline void
writeUint16(uint16_t value, void* buffer) {
uint8_t* byte_buffer = static_cast<uint8_t*>(buffer);
byte_buffer[0] = static_cast<uint8_t>((value & 0xff00U) >> 8);
byte_buffer[1] = static_cast<uint8_t>(value & 0x00ffU);
}
} // namespace asiolink
#endif // __ASIOLINK_UTILITIES_H
......@@ -41,7 +41,7 @@ public:
IOError(file, line, what) {}
};
/// \brief Socket not open
/// \brief Error setting socket options
///
/// Thrown if attempt to change socket options fails.
class SocketSetError : public IOError {
......@@ -50,7 +50,7 @@ public:
IOError(file, line, what) {}
};
/// \brief Buffer Overflow
/// \brief Buffer overflow
///
/// Thrown if an attempt is made to receive into an area beyond the end of
/// the receive data buffer.
......@@ -108,24 +108,23 @@ public:
/// \brief Return the "native" representation of the socket.
///
/// In practice, this is the file descriptor of the socket for
/// UNIX-like systems so the current implementation simply uses
/// \c int as the type of the return value.
/// We may have to need revisit this decision later.
/// In practice, this is the file descriptor of the socket for UNIX-like
/// systems so the current implementation simply uses \c int as the type of
/// the return value. We may have to need revisit this decision later.
///
/// In general, the application should avoid using this method;
/// it essentially discloses an implementation specific "handle" that
/// can change the internal state of the socket (consider the
/// application closes it, for example).
/// But we sometimes need to perform very low-level operations that
/// requires the native representation. Passing the file descriptor
/// to a different process is one example.
/// This method is provided as a necessary evil for such limited purposes.
/// In general, the application should avoid using this method; it
/// essentially discloses an implementation specific "handle" that can
/// change the internal state of the socket (consider what would happen if
/// the application closes it, for example). But we sometimes need to
/// perform very low-level operations that requires the native
/// representation. Passing the file descriptor to a different process is
/// one example. This method is provided as a necessary evil for such
//// limited purposes.
///
/// This method never throws an exception.
///
/// \return The native representation of the socket. This is the socket
/// file descriptor for UNIX-like systems.
/// file descriptor for UNIX-like systems.
virtual int getNative() const = 0;
/// \brief Return the transport protocol of the socket.
......@@ -135,16 +134,15 @@ public:
///
/// This method never throws an exception.
///
/// \return IPPROTO_UDP for UDP sockets
/// \return IPPROTO_TCP for TCP sockets
/// \return \c IPPROTO_UDP for UDP sockets, \c IPPROTO_TCP for TCP sockets
virtual int getProtocol() const = 0;
/// \brief Is Open() synchronous?
///
/// On a UDP socket, an "open" operation is merely a call to "open()" on
/// the underlying socket (so completes immediately), but on a TCP socket it
/// also includings connecting to the remote end (which is done as an
/// asynchronous operation).
/// On a TCP socket, an "open" operation is a call to the socket's "open()"
/// method followed by a connection to the remote system: it is an
/// asynchronous operation. On a UDP socket, it is just a call to "open()"
/// and completes synchronously.
///
/// For TCP, signalling of the completion of the operation is done by
/// by calling the callback function in the normal way. This could be done
......@@ -154,31 +152,31 @@ public:
/// asynchronously.
///
/// Owing to the way that the stackless coroutines are implemented, we need
/// to know _before_ executing the operation whether or not the open is
/// asynchronous. So this method simply provides that information.
/// to know _before_ executing the "open" function whether or not it is
/// asynchronous. So this method is called to provide that information.
///
/// (The reason there is a need to know is because the call to open() passes
/// in the state of the coroutine at the time the call is made. On an
/// asynchronous I/O, we need to set the state to point to the statement
/// after the call to open() before we pass the corotuine to the open()
/// call. Unfortunately, the macros that do this also yield control - which
/// we don't want to do if the open is synchronous. Hence we need to know
/// before we make the call to open() whether that call will complete
/// asynchronously.)
/// after the call to open() _before_ we pass the corouine to the open()
/// call. Unfortunately, the macros that set the state of the coroutine
/// also yield control - which we don't want to do if the open is
/// synchronous. Hence we need to know before we make the call to open()
/// whether that call will complete asynchronously.)
virtual bool isOpenSynchronous() const = 0;
/// \brief Open AsioSocket
///
/// Opens the socket for asynchronous I/O. The open will complete
/// synchronously on UCP or asynchronously on TCP (in which case a callback
/// will be queued): what will happen can be found by calling the method
/// isOpenSynchronous().
/// will be queued).
///
/// \param endpoint Pointer to the endpoint object. This is ignored for
/// a UDP socket (the target is specified in the send call), but should
/// be of type TCPEndpoint for a TCP connection.
/// a UDP socket (the target is specified in the send call), but
/// should be of type TCPEndpoint for a TCP connection.
/// \param callback I/O Completion callback, called when the operation has
/// completed, but only if the operation was asynchronous.
/// completed, but only if the operation was asynchronous. (It is
/// ignored on a UDP socket.)
virtual void open(const IOEndpoint* endpoint, C& callback) = 0;
/// \brief Send Asynchronously
......@@ -196,7 +194,7 @@ public:
/// \brief Receive Asynchronously
///
/// This correstponds to async_receive_from() for UDP sockets and
/// This corresponds to async_receive_from() for UDP sockets and
/// async_receive() for TCP. In both cases, an endpoint argument is
/// supplied to receive the source of the communication. For TCP it will
/// be filled in with details of the connection.
......@@ -214,22 +212,17 @@ public:
/// This applies to TCP receives, where the data is a byte stream and a
/// receive is not guaranteed to receive the entire message. DNS messages
/// over TCP are prefixed by a two-byte count field. This method takes the
/// amount received so far and the amount received in this I/O and checks
/// if the message is complete, returning the appropriate indication. As
/// a side-effect, it also updates the amount received.
/// amount received so far and checks if the message is complete.
///
/// For a UDP receive, all the data is received in one I/O, so this is
/// effectively a no-op (although it does update the amount received).
/// effectively a no-op).
///
/// \param data Data buffer containing data to date
/// \param length Amount of data received in last asynchronous I/O
/// \param cumulative On input, amount of data received before the last
/// I/O. On output, the total amount of data received to date.
/// \param length Total amount of data in the buffer.
///
/// \return true if the receive is complete, false if another receive is
/// needed.
virtual bool receiveComplete(void* data, size_t length,
size_t& cumulative) = 0;
/// needed.
virtual bool receiveComplete(const void* data, size_t length) = 0;
/// \brief Cancel I/O On AsioSocket
virtual void cancel() = 0;
......
......@@ -222,8 +222,9 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
CORO_YIELD data_->socket->asyncReceive(data_->data.get(),
static_cast<size_t>(MIN_LENGTH), data_->cumulative,
data_->remote.get(), *this);
data_->cumulative += length;
std::cerr << "IOFetch: resuming after asynchronous receive\n";
} while (!data_->socket->receiveComplete(data_->data.get(), length,
} while (!data_->socket->receiveComplete(data_->data.get(),
data_->cumulative));
// The message is not rendered yet, so we can't print it easily
......
......@@ -24,7 +24,6 @@
#include <sys/socket.h>
#include <unistd.h> // for some IPC/network system calls
#include <iostream>
#include <cstddef>
#include <boost/bind.hpp>
......@@ -34,6 +33,7 @@
#include <dns/buffer.h>
#include <asiolink/asiolink_utilities.h>
#include <asiolink/io_asio_socket.h>
#include <asiolink/io_endpoint.h>
#include <asiolink/io_service.h>
......@@ -65,15 +65,15 @@ public:
/// \brief Constructor from an ASIO TCP socket.
///
/// \param socket The ASIO representation of the TCP socket. It
/// is assumed that the caller will open and close the socket, so
/// these operations are a no-op for that socket.
/// \param socket The ASIO representation of the TCP socket. It is assumed
/// that the caller will open and close the socket, so these
/// operations are a no-op for that socket.
TCPSocket(asio::ip::tcp::socket& socket);
/// \brief Constructor
///
/// Used when the TCPSocket is being asked to manage its own internal
/// socket. It is assumed that open() and close() will not be used.
/// socket. In this case, the open() and close() methods are used.
///
/// \param service I/O Service object used to manage the socket.
TCPSocket(IOService& service);
......@@ -100,10 +100,10 @@ public:
/// \brief Open Socket
///
/// Opens the UDP socket. This is an asynchronous operation, completion of
/// Opens the TCP socket. This is an asynchronous operation, completion of
/// which will be signalled via a call to the callback function.
///
/// \param endpoint Endpoint to which the socket will connect to.
/// \param endpoint Endpoint to which the socket will connect.
/// \param callback Callback object.
virtual void open(const IOEndpoint* endpoint, C& callback);
......@@ -115,7 +115,8 @@ public:
///
/// \param data Data to send
/// \param length Length of data to send
/// \param endpoint Target of the send
/// \param endpoint Target of the send. (Unused for a TCP socket because
/// that was determined when the connection was opened.)
/// \param callback Callback object.
virtual void asyncSend(const void* data, size_t length,
const IOEndpoint* endpoint, C& callback);
......@@ -136,21 +137,15 @@ public:
/// \brief Checks if the data received is complete.
///
/// As all the data is received in one I/O, so this is, this is effectively
/// a no-op (although it does update the amount of data received).
/// Checks if all the data has been received by checking that the amount
/// of data received is equal to the number in the first two bytes of the
/// message plus two (for the count field itself).
///
/// \param data Data buffer containing data to date. (This is ignored
/// for TCP receives.)
/// \param length Amount of data received in last asynchronous I/O
/// \param cumulative On input, amount of data received before the last
/// I/O. On output, the total amount of data received to date.
/// \param data Data buffer containing data to date (ignored)
/// \param length Amount of data in the buffer.
///
/// \return true if the receive is complete, false if another receive is
/// needed.
virtual bool receiveComplete(void*, size_t length, size_t& cumulative) {
cumulative = length;
return (true);
}
/// \return true if the receive is complete, false if not.
virtual bool receiveComplete(const void* data, size_t length);
/// \brief Cancel I/O On Socket
virtual void cancel();
......@@ -176,6 +171,10 @@ private:
// achieved by altering isc::dns::buffer to have pairs of methods:
// getLength()/getTCPLength(), getData()/getTCPData(), with the getTCPXxx()
// methods taking into account a two-byte count field.)
//
// The option of sending the data in two operations, the count followed by
// the data was discounted as that would lead to two callbacks which would
// cause problems with the stackless coroutine code.
isc::dns::OutputBufferPtr send_buffer_; ///< Send buffer
};
......@@ -212,8 +211,6 @@ TCPSocket<C>::open(const IOEndpoint* endpoint, C& callback) {
// Ignore opens on already-open socket. Don't throw a failure because
// of uncertainties as to what precedes whan when using asynchronous I/O.
// At also allows us a treat a passed-in socket as a self-managed socket.
std::cerr << "TCPSocket::open(): open_ flags is " << isopen_ << "\n";
if (!isopen_) {
if (endpoint->getFamily() == AF_INET) {
socket_.open(asio::ip::tcp::v4());
......@@ -266,8 +263,6 @@ TCPSocket<C>::asyncSend(const void* data, size_t length, const IOEndpoint*,
send_buffer_->writeData(data, length);
// ... and send it
std::cerr << "TCPSocket::asyncSend(): sending " << count << " data bytes\n";
socket_.async_send(asio::buffer(send_buffer_->getData(),
send_buffer_->getLength()), callback);
} catch (boost::numeric::bad_numeric_cast& e) {
......@@ -281,34 +276,70 @@ TCPSocket<C>::asyncSend(const void* data, size_t length, const IOEndpoint*,
}
}
// Receive a message. Note that the "cumulative" argument is ignored - every TCP
// receive is put into the buffer beginning at the start - there is no concept
// receiving a subsequent part of a message. Same critera as before concerning
// the need for the socket to be open.
// Receive a message. Note that the "offset" argument is used as an index
// into the buffer in order to decide where to put the data. It is up to the
// caller to initialize the data to zero
template <typename C> void
TCPSocket<C>::asyncReceive(void* data, size_t length, size_t,
TCPSocket<C>::asyncReceive(void* data, size_t length, size_t offset,
IOEndpoint* endpoint, C& callback)
{
if (isopen_) {
// Upconvert the endpoint again.
// Upconvert to a TCPEndpoint. We need to do this because although
// IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
// does not contain a method for getting at the underlying endpoint
// type - that is in the derived class and the two classes differ on
// return type.
assert(endpoint->getProtocol() == IPPROTO_TCP);
const TCPEndpoint* tcp_endpoint =
static_cast<const TCPEndpoint*>(endpoint);
std::cerr << "TCPSocket::asyncReceive(): receiving from " <<
tcp_endpoint->getAddress().toText() <<
", port " << tcp_endpoint->getPort() << "\n";
// TODO: Complete TCPSocket::asyncReceive()
TCPEndpoint* tcp_endpoint = static_cast<TCPEndpoint*>(endpoint);
// Write the endpoint details from the comminications link. Ideally
// we should make IOEndpoint assignable, but this runs in to all sorts
// of problems concerning the management of the underlying Boost
// endpoint (e.g. if it is not self-managed, is the copied one
// self-managed?) The most pragmatic solution is to let Boost take care
// of everything and copy details of the underlying endpoint.
tcp_endpoint->getASIOEndpoint() = socket_.remote_endpoint();
// Ensure we can write into the buffer and if so, set the pointer to
// where the data will be written.
if (offset >= length) {
isc_throw(BufferOverflow, "attempt to read into area beyond end of "
"TCP receive buffer");
}
void* buffer_start = static_cast<void*>(static_cast<uint8_t*>(data) + offset);
// ... and kick off the read.
socket_.async_receive(asio::buffer(buffer_start, length - offset), callback);
} else {
isc_throw(SocketNotOpen,
"attempt to receive from a TCP socket that is not open");
}
}
// Is the receive complete?
template <typename C> bool
TCPSocket<C>::receiveComplete(const void* data, size_t length) {
bool complete = false;
// If we have read at least two bytes, we can work out how much we should be
// reading.
if (length >= 2) {
// Convert first two bytes to a count and check that the following data
// is that length.
// TODO: Should we check to see if we have received too much data?
uint16_t expected = readUint16(data);
complete = ((expected + 2) == length);
}
return (complete);
}
// Cancel I/O on the socket. No-op if the socket is not open.
template <typename C> void
TCPSocket<C>::cancel() {
if (isopen_) {
......
......@@ -18,6 +18,7 @@ TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
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 += asiolink_utilities_unittest.cc
run_unittests_SOURCES += io_address_unittest.cc
run_unittests_SOURCES += io_endpoint_unittest.cc
run_unittests_SOURCES += io_fetch_unittest.cc
......
// 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 asiolink utilties
///
/// Tests the fuctionality of the asiolink utilities code by comparing them
/// with the equivalent methods in isc::dns::[Input/Output]Buffer.
#include <cstddef>
#include <gtest/gtest.h>
#include <dns/buffer.h>
#include <asiolink/asiolink_utilities.h>
using namespace asiolink;
using namespace isc::dns;
TEST(asioutil, readUint16) {
// Reference buffer
uint8_t data[2];
isc::dns::InputBuffer buffer(data, sizeof(data));
// Avoid possible compiler warnings by only setting uint8_t variables to
// uint8_t values.
uint8_t i8 = 0;
uint8_t j8 = 0;
for (int i = 0; i < (2 << 8); ++i, ++i8) {
for (int j = 0; j < (2 << 8); ++j, ++j8) {
data[0] = i8;
data[1] = j8;
buffer.setPosition(0);
EXPECT_EQ(buffer.readUint16(), readUint16(data));
}
}
}
TEST(asioutil, writeUint16) {
// Reference buffer
isc::dns::OutputBuffer buffer(2);
uint8_t test[2];
// Avoid possible compiler warnings by only setting uint16_t variables to
// uint16_t values.
uint16_t i16 = 0;
for (uint32_t i = 0; i < (2 << 16); ++i, ++i16) {
// Write the reference data
buffer.clear();
buffer.writeUint16(i16);
// ... and the test data
writeUint16(i16, test);
// ... and compare
const uint8_t* ref = static_cast<const uint8_t*>(buffer.getData());
EXPECT_EQ(ref[0], test[0]);
EXPECT_EQ(ref[1], test[1]);
}
}
......@@ -263,8 +263,8 @@ TEST(UDPSocket, SequenceTest) {
EXPECT_EQ(SERVER_PORT, client_remote_endpoint.getPort());
// Finally, check that the receive received a complete buffer's worth of data.
EXPECT_TRUE(client.receiveComplete(&data[0], client_cb.getLength(),
client_cumulative));
client_cumulative += client_cb.getLength();
EXPECT_TRUE(client.receiveComplete(&data[0], client_cumulative));
EXPECT_EQ(client_cb.getLength(), client_cumulative);
// Close client and server.
......
......@@ -53,15 +53,15 @@ public:
/// \brief Constructor from an ASIO UDP socket.
///
/// \param socket The ASIO representation of the UDP socket. It
/// is assumed that the caller will open and close the socket, so
/// these operations are a no-op for that socket.
/// \param socket The ASIO representation of the UDP socket. It is assumed
/// that the caller will open and close the socket, so these
/// operations are a no-op for that socket.
UDPSocket(asio::ip::udp::socket& socket);
/// \brief Constructor
///
/// Used when the UDPSocket is being asked to manage its own internal
/// socket. It is assumed that open() and close() will not be used.
/// socket. In this case, the open() and close() methods are used.
///
/// \param service I/O Service object used to manage the socket.
UDPSocket(IOService& service);
......@@ -90,9 +90,11 @@ public:
///
/// Opens the UDP socket. This is a synchronous operation.
///
/// \param endpoint Endpoint to which the socket will connect to.
/// \param callback Unused.
virtual void open(const IOEndpoint* endpoint, C&);
/// \param endpoint Endpoint to which the socket will send data. This is
/// used to determine the address family trhat should be used for the
/// underlying socket.
/// \param callback Unused as the operation is synchronous.
virtual void open(const IOEndpoint* endpoint, C& callback);
/// \brief Send Asynchronously
///
......@@ -110,8 +112,8 @@ public:
/// \brief Receive Asynchronously
///
/// Calls the underlying socket's async_receive_from() method to read a
/// packet of data from a remote endpoint. Arrival of the data is
/// signalled via a call to the callback function.
/// packet of data from a remote endpoint. Arrival of the data is signalled
/// via a call to the callback function.
///
/// \param data Buffer to receive incoming message
/// \param length Length of the data buffer
......@@ -123,19 +125,15 @@ public:
/// \brief Checks if the data received is complete.
///
/// As all the data is received in one I/O, so this is, this is effectively
/// a no-op (although it does update the amount of data received).
/// For a UDP socket all the data is received in one I/O, so this is
/// effectively a no-op (although it does update the amount of data
/// received).
///
/// \param data Data buffer containing data to date. (This is ignored
/// for UDP receives.)
/// \param length Amount of data received in last asynchronous I/O
/// \param cumulative On input, amount of data received before the last
/// I/O. On output, the total amount of data received to date.
/// \param data Data buffer containing data to date (ignored)
/// \param length Amount of data in the buffer.
///
/// \return true if the receive is complete, false if another receive is
/// needed. Always true for a UDP socket.
virtual bool receiveComplete(void*, size_t length, size_t& cumulative) {
cumulative = length;
/// \return Always true
virtual bool receiveComplete(const void*, size_t) {
return (true);
}
......@@ -185,10 +183,11 @@ UDPSocket<C>::~UDPSocket()
template <typename C> void
UDPSocket<C>::open(const IOEndpoint* endpoint, C&) {
// Ignore opens on already-open socket. Don't throw a failure because
// of uncertainties as to what precedes whan when using asynchronous I/O.
// At also allows us a treat a passed-in socket as a self-managed socket.
// Ignore opens on already-open socket. (Don't throw a failure because
// of uncertainties as to what precedes whan when using asynchronous I/O.)
// It also allows us a treat a passed-in socket in exactly the same way as
// a self-managed socket (in that we can call the open() and close() methods
// of this class).
if (!isopen_) {
if (endpoint->getFamily() == AF_INET) {
socket_.open(asio::ip::udp::v4());
......@@ -198,8 +197,7 @@ UDPSocket<C>::open(const IOEndpoint* endpoint, C&) {
}
isopen_ = true;
// Ensure it can send and receive 4K buffers.
// Ensure it can send and receive at least 4K buffers.
asio::ip::udp::socket::send_buffer_size snd_size;
socket_.get_option(snd_size);
if (snd_size.value() < MIN_SIZE) {
......@@ -227,13 +225,14 @@ UDPSocket<C>::asyncSend(const void* data, size_t length,
// Upconvert to a UDPEndpoint. We need to do this because although
// IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
// doing cont contain a method for getting at the underlying endpoint
// type - those are in the derived class and the two classes differ on
// does not contain a method for getting at the underlying endpoint
// type - that is in the derived class and the two classes differ on
// return type.
assert(endpoint->getProtocol() == IPPROTO_UDP);
const UDPEndpoint* udp_endpoint =
static_cast<const UDPEndpoint*>(endpoint);
// ... and send the message.
socket_.async_send_to(asio::buffer(data, length),
udp_endpoint->getASIOEndpoint(), callback);
} else {
......@@ -242,10 +241,8 @@ UDPSocket<C>::asyncSend(const void* data, size_t length,
}
}
// Receive a message. Note that the "cumulative" argument is ignored - every UDP
// receive is put into the buffer beginning at the start - there is no concept