mSize(size),
mR(0),
mW(0),
+ mOriginalW(0),
mBoolR(0),
mBoolW(0),
mBoolNumR(0),
mSize(size),
mR(0),
mW(size),
+ mOriginalW(size),
mBoolR(0),
mBoolW(0),
mBoolNumR(0),
mBoolNumW(0)
{
if (mBuffer) memcpy(mBuffer, data, size);
+ else throw std::length_error("out of memory");
}
mSize(copy.mSize),
mR(copy.mR),
mW(copy.mW),
+ mOriginalW(copy.mOriginalW),
mBoolR(copy.mBoolR),
mBoolW(copy.mBoolW),
mBoolNumR(copy.mBoolNumR),
mBoolNumW(copy.mBoolNumW)
{
if (mBuffer) memcpy(mBuffer, copy.mBuffer, mSize);
+ else throw std::length_error("out of memory");
}
Packet& Packet::operator=(const Packet& copy)
mSize = copy.mSize;
mR = copy.mR;
mW = copy.mW;
+ mOriginalW = copy.mOriginalW;
mBoolR = copy.mBoolR;
mBoolW = copy.mBoolW;
mBoolNumR = copy.mBoolNumR;
mBoolNumW = copy.mBoolNumW;
if (mBuffer) memcpy(mBuffer, copy.mBuffer, mSize);
+ else throw std::length_error("out of memory");
return *this;
}
mBoolW = mW;
unsigned char byte = 0;
- if (write(&byte, 1) == 0) return *this;
+ if (write(&byte, 1) == 0) throw std::length_error("out of memory");
}
if (value) mBuffer[mBoolW] |= (1 << bit);
Packet& Packet::operator<<(uint8_t value)
{
- write(&value, sizeof(value));
+ if (write(&value, sizeof(value)) != sizeof(value))
+ {
+ throw std::length_error("out of memory");
+ }
return *this;
}
Packet& Packet::operator<<(uint16_t value)
{
value = htons(value);
- write(&value, sizeof(value));
+ if (write(&value, sizeof(value)) != sizeof(value))
+ {
+ throw std::length_error("out of memory");
+ }
return *this;
}
Packet& Packet::operator<<(uint32_t value)
{
value = htonl(value);
- write(&value, sizeof(value));
+ if (write(&value, sizeof(value)) != sizeof(value))
+ {
+ throw std::length_error("out of memory");
+ }
return *this;
}
Packet& Packet::operator<<(uint64_t value)
{
value = htonll(value);
- write(&value, sizeof(value));
+ if (write(&value, sizeof(value)) != sizeof(value))
+ {
+ throw std::length_error("out of memory");
+ }
return *this;
}
// XXX: assumes the ieee-754
uint32_t* integer = reinterpret_cast<uint32_t*>(&value);
*integer = htonl(*integer);
- write(integer, sizeof(value));
+ if (write(integer, sizeof(value)) != sizeof(value))
+ {
+ throw std::length_error("out of memory");
+ }
return *this;
}
// XXX: assumes the ieee-754
uint64_t* integer = reinterpret_cast<uint64_t*>(&value);
*integer = htonll(*integer);
- write(integer, sizeof(value));
+ if (write(integer, sizeof(value)) != sizeof(value))
+ {
+ throw std::length_error("out of memory");
+ }
return *this;
}
mBoolR = mR;
unsigned char byte = 0;
- if (read(&byte, 1) == 0) return *this;
+ if (read(&byte, 1) == 0) throw std::out_of_range("end of packet");
}
value = 1 & (mBuffer[mBoolR] >> bit);
Packet& Packet::operator>>(uint8_t& value)
{
- read(&value, sizeof(value));
+ if (read(&value, sizeof(value)) != sizeof(value))
+ {
+ throw std::out_of_range("end of packet");
+ }
return *this;
}
Packet& Packet::operator>>(uint16_t& value)
{
- read(&value, sizeof(value));
+ if (read(&value, sizeof(value)) != sizeof(value))
+ {
+ throw std::out_of_range("end of packet");
+ }
value = ntohs(value);
return *this;
}
Packet& Packet::operator>>(uint32_t& value)
{
- read(&value, sizeof(value));
+ if (read(&value, sizeof(value)) != sizeof(value))
+ {
+ throw std::out_of_range("end of packet");
+ }
value = ntohl(value);
return *this;
}
Packet& Packet::operator>>(uint64_t& value)
{
- read(&value, sizeof(value));
+ if (read(&value, sizeof(value)) != sizeof(value))
+ {
+ throw std::out_of_range("end of packet");
+ }
value = ntohll(value);
return *this;
}
{
// XXX: assumes the ieee-754
uint32_t* integer = reinterpret_cast<uint32_t*>(&value);
- read(integer, sizeof(value));
+ if (read(integer, sizeof(value)) != sizeof(value))
+ {
+ throw std::out_of_range("end of packet");
+ }
*integer = htonl(*integer);
return *this;
}
{
// XXX: assumes the ieee-754
uint64_t* integer = reinterpret_cast<uint64_t*>(&value);
- read(integer, sizeof(value));
+ if (read(integer, sizeof(value)) != sizeof(value))
+ {
+ throw std::out_of_range("end of packet");
+ }
*integer = htonll(*integer);
return *this;
}
}
+void Packet::reset()
+{
+ mR = 0;
+ mW = mOriginalW;
+ mBoolR = 0;
+ mBoolW = 0;
+ mBoolNumR = 0;
+ mBoolNumW = 0;
+}
+
+
} // namespace Mf
*
**************************************************************************/
+/**
+ * \file Packet.hh
+ * Classes for building and interpreting datagram packets.
+ */
+
#ifndef _MOOF_PACKET_HH_
#define _MOOF_PACKET_HH_
#include <cstring>
+#include <stdexcept>
#include <string>
#include <vector>
namespace Mf {
+/**
+ * Represents a packet of serialized variables ready for transfer over the
+ * network. This method is most suitable for representing datagram
+ * packets, but it may also be useful for seralizing data for persistent
+ * state storage. The semantics are similar to that of a FIFO queue or
+ * stream where packets are written and read by inserting and extracting
+ * variables to and from the packet, although the actual order of the
+ * variables in the buffer may be different. At any time, a pointer to a
+ * buffer and the size of the buffer can be retrieved. This class also
+ * handles endian differences by serializing variables in network byte
+ * order (big endian).
+ */
class Packet
{
public:
+ /**
+ * Construct a packet with an initial capacity.
+ * \param capacity Initial capacity of the packet.
+ */
Packet(size_t size = PAGE_SIZE);
- Packet(const char* data, size_t size);
-
- Packet(const Packet& copy);
- Packet& operator=(const Packet& copy);
- ~Packet();
+ /**
+ * Construct a packet with some bytes from a buffer. The bytes will be
+ * copied into the packet, so you don't need to keep the original
+ * buffer.
+ * \param data The bytes.
+ * \param size The number of bytes.
+ */
+ Packet(const char* data, size_t size);
+ /**
+ * Insert a variable into the packet, serializing it. This usually
+ * increases the size of the packet by the size of the data type.
+ * \param value The value to insert.
+ * \return This.
+ */
Packet& operator<<(bool value);
Packet& operator<<(int8_t value);
Packet& operator<<(int16_t value);
Packet& operator<<(float value);
Packet& operator<<(double value);
+ /**
+ * Write some bytes to the packet.
+ * \param bytes The bytes.
+ * \param size The number of bytes.
+ * return The number of bytes actually written.
+ */
size_t write(const void* bytes, size_t size);
+
+ /**
+ * Extract a variable from the packet. This usually decreases the size
+ * of the packet by the size of the data type.
+ * \param value Reference to the variable to extract.
+ * \return This.
+ */
Packet& operator>>(bool& value);
Packet& operator>>(int8_t& value);
Packet& operator>>(int16_t& value);
Packet& operator>>(float& value);
Packet& operator>>(double& value);
+ /**
+ * Read some bytes from the packet.
+ * \param bytes The buffer to hold the bytes read.
+ * \param size The size of the read buffer.
+ * \return The number of bytes actually read.
+ */
size_t read(void* bytes, size_t size);
+ /**
+ * Clear the contents of the packet, setting the size of the packet to
+ * zero.
+ */
void clear();
+ /**
+ * Reset the read/write markers to their initial positions, putting the
+ * packet in the state it was at right after construction.
+ */
+ void reset();
+
+
+ /**
+ * Get a pointer to an internal structure holding the serialized bytes
+ * of the packet.
+ * return The pointer.
+ */
const char* bytes() const
{
return mBuffer + mR;
}
+ /**
+ * Get the size of the buffer holding the serialized bytes of the
+ * packet.
+ * \return The number of bytes.
+ */
size_t size() const
{
return mW - mR;
}
+ // The rest of this stuff is just to implement correct copy semantics.
+
+ Packet(const Packet& copy);
+ Packet& operator=(const Packet& copy);
+ ~Packet();
+
+
private:
char* mBuffer;
size_t mR;
size_t mW;
+ size_t mOriginalW;
size_t mBoolR;
size_t mBoolW;
{
uint16_t length = strlen(value);
packet << length;
- packet.write(value, length);
+ if (packet.write(value, length) != length)
+ {
+ throw std::length_error("out of memory");
+ }
return packet;
}
template <class T>
inline Packet& operator<<(Packet& packet, const std::basic_string<T>& value)
{
- packet << (uint16_t)value.length();
- packet.write(value.data(), value.length() * sizeof(T));
+ packet << static_cast<uint16_t>(value.length());
+ size_t numBytes = value.length() * sizeof(T);
+ if (packet.write(value.data(), numBytes) != numBytes)
+ {
+ throw std::length_error("out of memory");
+ }
return packet;
}
packet >> length;
T str[length];
- size_t charsRead = packet.read(str, length * sizeof(T));
- value.assign(str, charsRead);
+ size_t numBytes = length * sizeof(T);
+ if (packet.read(str, numBytes) != numBytes)
+ {
+ throw std::out_of_range("end of packet");
+ }
+ value.assign(str, length);
return packet;
}
template <class T>
inline Packet& operator<<(Packet& packet, const std::vector<T>& value)
{
- packet << (uint16_t)value.size();
+ packet << static_cast<uint16_t>(value.size());
typename std::vector<T>::const_iterator it;
for (it = value.begin(); it != value.end(); ++it)
{
*
**************************************************************************/
+/**
+ * \file Network.hh
+ * Network-related classes, including a reinterpreted sockets API.
+ */
+
#ifndef _MOOF_SOCKET_HH_
#define _MOOF_SOCKET_HH_
#include <algorithm>
#include <cstring>
+#include <iostream>
#include <sstream>
#include <string>
#include <vector>
* Construct an address with a specified host. The address can be used
* to connect to a host.
* \param service The service name or port number.
- * \param host The numeric IP address of the host.
+ * \param name The numeric IP address of the host.
* \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM.
* \param family The family; can be AF_INET or AF_INET6.
*/
SocketAddress(const std::string& service,
- const std::string& host,
+ const std::string& name,
int type = SOCK_STREAM,
int family = AF_UNSPEC)
{
- init(service, host, type, family);
+ init(service, name, type, family);
}
/**
mType(addr->ai_socktype)
{
memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen);
- getServiceAndHostName(mService, mHost);
+ getNameAndService(mService, mName);
}
/**
mType(type)
{
memcpy(&mAddr.sa, addr, size);
- getServiceAndHostName(mService, mHost);
+ getNameAndService(mService, mName);
}
* Initialize the address with a specified host. The address can be
* used to connect to a host.
* \param service The service name or port number.
- * \param host The numeric IP address of the host.
+ * \param name The numeric IP address of the host.
* \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM.
* \param family The family; can be AF_INET or AF_INET6.
*/
void init(const std::string& service,
- const std::string& host,
+ const std::string& name,
int type = SOCK_STREAM,
int family = AF_UNSPEC)
{
- struct addrinfo* addr = resolve(service.c_str(), host.c_str(),
+ struct addrinfo* addr = resolve(service.c_str(), name.c_str(),
type, family,
AI_ADDRCONFIG | AI_NUMERICHOST | AI_V4MAPPED);
if (addr)
memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen);
mService = service;
- mHost = host;
+ mName = name;
freeaddrinfo(addr);
}
memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen);
mService = service;
- getHost(mHost);
+ getName(mName);
freeaddrinfo(addr);
}
* the address, or a resolved numeric host if none was used.
* \return The host.
*/
- const std::string& host() const
+ const std::string& name() const
{
- return mHost;
+ return mName;
}
/**
* method may take some time to return. Use the ResolveTask class to
* resolve addresses asynchronously.
* \param service The service name or port number.
- * \param host The name of the local or remote host.
+ * \param name The name of the local or remote host.
* \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM.
* \param family The family; can be AF_INET or AF_INET6.
* \param resolved The list to be filled with addresses.
* \return 0 on success, -1 on error.
*/
static int resolve(const std::string& service,
- const std::string& host,
+ const std::string& name,
int type,
int family,
std::vector<SocketAddress>& resolved)
{
- struct addrinfo* list = resolve(service.c_str(), host.c_str(),
+ struct addrinfo* list = resolve(service.c_str(), name.c_str(),
type, family,
AI_ADDRCONFIG | AI_V4MAPPED);
int result = collectAddresses(list, resolved);
}
+ /**
+ * Resolve the hostname of the address. The default behavior is to
+ * avoid a reverse lookup by giving the numeric address. You can
+ * change that behavior with the getnameinfo flags.
+ * \param name The place to store the hostname or IP address.
+ * \param flags The getnameinfo flags.
+ */
+ void getName(std::string& name, int flags = NI_NUMERICHOST)
+ {
+ char node[256] = {'\0'};
+ int result = getnameinfo(&mAddr.sa, mSize,
+ node, sizeof(node),
+ 0, 0,
+ flags);
+ if (result == 0) name.assign(node);
+ }
+
+ /**
+ * Resolve the service name of the address.
+ * \param service The place to store the service name or port number.
+ * \param flags The getnameinfo flags.
+ */
+ void getService(std::string& service, int flags)
+ {
+ flags |= mType == SOCK_DGRAM ? NI_DGRAM : 0;
+
+ char serv[64] = {'\0'};
+ int result = getnameinfo(&mAddr.sa, mSize,
+ 0, 0,
+ serv, sizeof(serv),
+ flags);
+ if (result == 0) service.assign(serv);
+ }
+
+ /**
+ * Resolve the service and hostname of the address. The default
+ * behavior is to avoid a reverse lookup by giving the numeric address.
+ * You can change that behavior with the getnameinfo flags.
+ * \param name The place to store the hostname or IP address.
+ * \param service The place to store the service name or port number.
+ * \param flags The getnameinfo flags.
+ */
+ void getNameAndService(std::string& name,
+ std::string& service,
+ int flags = NI_NUMERICHOST)
+ {
+ flags |= mType == SOCK_DGRAM ? NI_DGRAM : 0;
+
+ char serv[64] = {'\0'};
+ char node[256] = {'\0'};
+ int result = getnameinfo(&mAddr.sa, mSize,
+ node, sizeof(node),
+ serv, sizeof(serv),
+ flags);
+ if (result == 0)
+ {
+ service.assign(serv);
+ name.assign(node);
+ }
+ }
+
+
private:
static struct addrinfo* resolve(const char* service,
}
- void getService(std::string& service)
- {
- char value[64] = {'\0'};
- int result = getnameinfo(&mAddr.sa, mSize,
- 0, 0,
- value, sizeof(value),
- (mType == SOCK_DGRAM) ? NI_DGRAM : 0);
- if (result == 0) service.assign(value);
- }
-
- void getHost(std::string& host)
- {
- char value[256] = {'\0'};
- int result = getnameinfo(&mAddr.sa, mSize,
- value, sizeof(value),
- 0, 0,
- NI_NUMERICHOST);
- if (result == 0) host.assign(value);
- }
-
- void getServiceAndHostName(std::string& service, std::string& host)
- {
- char serv[64] = {'\0'};
- char node[256] = {'\0'};
- int result = getnameinfo(&mAddr.sa, mSize,
- node, sizeof(node),
- serv, sizeof(serv),
- NI_NUMERICHOST |
- ((mType == SOCK_DGRAM) ? NI_DGRAM : 0));
- if (result == 0)
- {
- service.assign(serv);
- host.assign(node);
- }
- }
-
-
union
{
sockaddr sa;
size_t mSize;
int mType;
- std::string mHost;
+ std::string mName;
std::string mService;
};
* Construct a socket with a specified host. The socket can be used to
* connect to a host.
* \param service The service name or port number.
- * \param host The numeric IP address of the host.
+ * \param name The numeric IP address of the host.
* \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM.
* \param family The family; can be AF_INET or AF_INET6.
* \param flags The socket options.
*/
- Socket(const std::string& service, const std::string& name,
- int type = SOCK_STREAM, int family = AF_UNSPEC,
- int flags = 0) :
+ Socket(const std::string& service,
+ const std::string& name,
+ int type = SOCK_STREAM,
+ int family = AF_UNSPEC,
+ int flags = 0) :
mImpl(SocketAddress(service, name, type, family), flags) {}
/**
* \param family The family; can be AF_INET or AF_INET6.
* \param flags The socket options.
*/
- Socket(const std::string& service, int type = SOCK_STREAM,
- int family = AF_UNSPEC, int flags = 0) :
+ Socket(const std::string& service,
+ int type = SOCK_STREAM,
+ int family = AF_UNSPEC,
+ int flags = 0) :
mImpl(SocketAddress(service, type, family), flags) {}
* Set an integer socket option.
* \param option The option to set.
* \param value The new value.
+ * \param level The layer to handle the option.
* \return 0 on success, -1 on failure.
*/
- int set(int option, int value = 0)
+ template <class T>
+ int set(int option, const T& value, int level = SOL_SOCKET)
{
- if (option == SO_NONBLOCK)
- {
-#ifdef HAVE_FCNTL
- int flags = fcntl(mImpl.fd, F_GETFL);
- return fcntl(mImpl.fd,
- F_SETFL,
- flags | (value ? O_NONBLOCK : 0));
-#else
- return ioctl(mImpl.fd, FIONBIO, value);
-#endif
- }
- return setsockopt(mImpl.fd, SOL_SOCKET, option,
- &value, sizeof(value));
+ return setsockopt(mImpl.fd, level, option, &value, sizeof(value));
}
/**
* Set a string socket option.
* \param option The option to set.
* \param value The new value.
+ * \param level The layer to handle the option.
* \return 0 on success, -1 on failure.
*/
- int set(int option, const std::string& value)
+ int set(int option, const std::string& value, int level = SOL_SOCKET)
{
- return setsockopt(mImpl.fd, SOL_SOCKET, option,
+ return setsockopt(mImpl.fd, level, option,
value.data(), value.length());
}
* Get an integer socket option.
* \param option The option to set.
* \param value The new value.
+ * \param level The layer to handle the option.
* \return 0 on success, -1 on failure.
*/
- int get(int option, int& value)
+ template <class T>
+ int get(int option, T& value, int level = SOL_SOCKET) const
{
- if (option == SO_NONBLOCK)
- {
-#ifdef HAVE_FCNTL
- int flags = fcntl(mImpl.fd, F_GETFL);
- return flags & O_NONBLOCK;
-#else
- return ioctl(mImpl.fd, FIONBIO, &value);
-#endif
- }
- socklen_t optlen = sizeof(value);
- return getsockopt(mImpl.fd, SOL_SOCKET, option, &value, &optlen);
+ int size = sizeof(value);
+ return getsockopt(mImpl.fd, level, option, &value, &size);
}
/**
* Get a string socket option.
* \param option The option to set.
* \param value The new value.
+ * \param level The layer to handle the option.
* \return 0 on success, -1 on failure.
*/
- int get(int option, std::string& value)
+ int get(int option, std::string& value, int level = SOL_SOCKET) const
{
- char str[256] = {'\0'};
- socklen_t optlen = sizeof(str);
- int result = getsockopt(mImpl.fd, SOL_SOCKET, option,
- &str, &optlen);
- value = str;
+ char str[256] = {'\0'};
+ socklen_t size = sizeof(str);
+
+ int result = getsockopt(mImpl.fd, level, option, &str, &size);
+ value.assign(str, size);
return result;
}
+ void setBlocking(bool isBlocking)
+ {
+ int value = isBlocking;
+#ifdef HAVE_FCNTL
+ int flags = fcntl(mImpl.fd, F_GETFL);
+ fcntl(mImpl.fd, F_SETFL, flags | (value ? O_NONBLOCK : 0));
+#else
+ ioctl(mImpl.fd, FIONBIO, value);
+#endif
+ }
+
+ bool isBlocking() const
+ {
+#ifdef HAVE_FCNTL
+ int flags = fcntl(mImpl.fd, F_GETFL);
+ return flags & O_NONBLOCK;
+#else
+ int value;
+ ioctl(mImpl.fd, FIONBIO, &value);
+ return value;
+#endif
+ }
+
+
/**
* Write some bytes to the socket. Use this for connected sockets.
* \param bytes The bytes.
};
+class SocketMultiplexer
+{
+public:
+
+ typedef boost::function<int(SocketMultiplexer&,
+ Packet&,
+ const SocketAddress&)> Function;
+
+ SocketMultiplexer(Socket sock) :
+ mSocket(sock) {}
+
+
+ void setSocket(Socket sock)
+ {
+ Mutex::ScopedLock lock(mMutex);
+ mSocket = sock;
+ }
+
+ Socket& socket()
+ {
+ return mSocket;
+ }
+
+
+ std::vector<Function>& protocols()
+ {
+ return mProtocols;
+ }
+
+
+ void update(Scalar t, Scalar dt)
+ {
+ SocketAddress address;
+ Packet packet;
+ ssize_t bytes = mSocket.read(packet, address);
+
+ if (bytes > 0)
+ {
+ std::vector<Function>::iterator it;
+ for (it = mProtocols.begin(); it < mProtocols.end(); ++it)
+ {
+ packet.reset();
+ if ((*it)(*this, packet, address)) break;
+ }
+ }
+ }
+
+
+ int background()
+ {
+ return 0;
+ }
+
+
+private:
+
+ Socket mSocket;
+ std::vector<Function> mProtocols;
+ Mutex mMutex;
+};
+
+
/**
* An asynchronous task to resolve addresses.
*/
/**
* Construct a resolver task from a service and hostname.
* \param service Server name or port number.
- * \param host The hostname or numeric address.
+ * \param name The hostname or numeric address.
* \param type The type of communication.
* \param family The requested protocol family.
*/
ResolverTask(const std::string& service,
- const std::string& host,
+ const std::string& name,
int type = SOCK_STREAM,
int family = AF_UNSPEC) :
mIsDone(false)
{
mFunction = boost::bind(&ResolverTask::resolve,
- this, service, host, type, family);
+ this, service, name, type, family);
}
private:
int resolve(const std::string& service,
- const std::string& host,
+ const std::string& name,
int type,
int family)
{
- int status = SocketAddress::resolve(service, host,
+ int status = SocketAddress::resolve(service, name,
type, family,
mAddressList);
mIsDone = true;
};
+std::ostream& operator<<(std::ostream& stream, const SocketAddress& addr)
+{
+ stream << addr.name() << ":" << addr.service();
+ return stream;
+}
+
+std::ostream& operator<<(std::ostream& stream, const Socket& sock)
+{
+ stream << sock.address();
+ return stream;
+}
+
+
} // namespace Mf
#endif // _MOOF_SOCKET_HH_