#ifndef _MOOF_SOCKET_HH_
#define _MOOF_SOCKET_HH_
+#include <algorithm>
#include <cstring>
#include <sstream>
#include <string>
namespace Mf {
+/**
+ * A class to represent the address of a remote host, including the type of
+ * service and socket communication.
+ */
class SocketAddress
{
public:
+ /**
+ * Construct an unspecified address.
+ */
SocketAddress() :
mSize(0),
mType(0)
{
- mAddress.sa.sa_family = AF_UNSPEC;
- mAddress.v4.sin_port = 0;
+ mAddr.sa.sa_family = AF_UNSPEC;
+ mAddr.in.sin_port = 0;
}
- SocketAddress(const std::string& service, const std::string& name,
- int type = SOCK_STREAM, int family = AF_UNSPEC)
+ /**
+ * 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 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,
+ int type = SOCK_STREAM,
+ int family = AF_UNSPEC)
{
- init(service, name, type, family);
+ init(service, host, type, family);
}
+ /**
+ * Construct an address without a specified host. The address can be
+ * used to accept on a local port.
+ * \param service The service name or port number.
+ * \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,
- int type = SOCK_STREAM, int family = AF_UNSPEC)
+ int type = SOCK_STREAM,
+ int family = AF_UNSPEC)
{
init(service, type, family);
}
- SocketAddress(const struct addrinfo* addr, const std::string& name)
+ /**
+ * Construct an address from the information in an addrinfo structure.
+ * \param addr The addrinfo structure.
+ */
+ SocketAddress(const struct addrinfo* addr) :
+ mSize(addr->ai_addrlen),
+ mType(addr->ai_socktype)
{
- mType = addr->ai_socktype;
- memcpy(&mAddress.sa, addr->ai_addr, addr->ai_addrlen);
- mName = name;
- mSize = addr->ai_addrlen;
+ memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen);
+ getServiceAndHostName(mService, mHost);
}
- SocketAddress(const struct sockaddr* addr, size_t size,
- int type = SOCK_STREAM)
+ /**
+ * Construct an address from a sockaddr structure.
+ * \param addr The sockaddr structure.
+ * \param size The size of the sockaddr structure.
+ * \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM.
+ */
+ SocketAddress(const struct sockaddr* addr,
+ size_t size,
+ int type = SOCK_STREAM) :
+ mSize(size),
+ mType(type)
{
- mType = type;
- memcpy(&mAddress.sa, addr, size);
- mSize = size;
- setNameFromAddress();
+ memcpy(&mAddr.sa, addr, size);
+ getServiceAndHostName(mService, mHost);
}
+ /**
+ * Get an IPv4 broadcast address.
+ * \param service The service name or port number.
+ * \return The socket address.
+ */
static SocketAddress broadcast(const std::string& service)
{
std::istringstream stream(service);
}
- void init(const std::string& service, const std::string& name = "",
- int type = SOCK_STREAM, int family = AF_UNSPEC)
- {
- ASSERT(type == SOCK_STREAM || type == SOCK_DGRAM);
- ASSERT(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC);
-
- struct addrinfo hints;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = family;
- hints.ai_socktype = type;
- hints.ai_flags = AI_PASSIVE;
-
- struct addrinfo* addr;
- int status = getaddrinfo(name.length() > 0 ? name.c_str() : 0,
- service.c_str(), &hints, &addr);
- if (status == 0)
+ /**
+ * 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 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,
+ int type = SOCK_STREAM,
+ int family = AF_UNSPEC)
+ {
+ struct addrinfo* addr = resolve(service.c_str(), host.c_str(),
+ type, family,
+ AI_ADDRCONFIG | AI_NUMERICHOST | AI_V4MAPPED);
+ if (addr)
{
- mType = addr->ai_socktype;
- memcpy(&mAddress.sa, addr->ai_addr, addr->ai_addrlen);
mSize = addr->ai_addrlen;
+ mType = addr->ai_socktype;
+ memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen);
- if (name != "") mName = name;
- else setNameFromAddress();
+ mService = service;
+ mHost = host;
freeaddrinfo(addr);
}
else
{
- Mf::logWarning(gai_strerror(status));
mType = 0;
mSize = 0;
- mAddress.sa.sa_family = AF_UNSPEC;
- mAddress.v4.sin_port = 0;
+ mAddr.sa.sa_family = AF_UNSPEC;
+ mAddr.in.sin_port = 0;
}
}
+ /**
+ * Initialize the address without a specified host. The address can be
+ * used to accept on a local port.
+ * \param service The service name or port number.
+ * \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,
- int type = SOCK_STREAM, int family = AF_UNSPEC)
+ int type = SOCK_STREAM,
+ int family = AF_UNSPEC)
{
- init(service, "", type, family);
+ struct addrinfo* addr = resolve(service.c_str(), 0,
+ type, family,
+ AI_PASSIVE);
+ if (addr)
+ {
+ mSize = addr->ai_addrlen;
+ mType = addr->ai_socktype;
+ memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen);
+
+ mService = service;
+ getHost(mHost);
+
+ freeaddrinfo(addr);
+ }
+ else
+ {
+ mType = 0;
+ mSize = 0;
+ mAddr.sa.sa_family = AF_UNSPEC;
+ mAddr.in.sin_port = 0;
+ }
}
- const std::string& name() const
+ /**
+ * Get the name of the service. This could also be a port number if
+ * there is no service name associated with the number.
+ * \return The service.
+ */
+ const std::string& service() const
{
- return mName;
+ return mService;
}
- void setName(const std::string& name)
+ /**
+ * Get the name of the host. This may be the host used to construct
+ * the address, or a resolved numeric host if none was used.
+ * \return The host.
+ */
+ const std::string& host() const
{
- mName = name;
+ return mHost;
}
+ /**
+ * Get the port number of the address service.
+ * \return Port number.
+ */
unsigned short port() const
{
- return ntohs(mAddress.v4.sin_port);
+ return ntohs(mAddr.in.sin_port);
}
+ /**
+ * Get the type of socket associated with the service of this address.
+ * \return Socket type; either SOCK_STREAM or SOCK_DGRAM.
+ */
int type() const
{
return mType;
}
+ /**
+ * Get the family of the protocol associated with the address.
+ * \return Protocol family; either AF_INET, AF_INET6, or AF_UNSPEC.
+ */
int family() const
{
- return mAddress.sa.sa_family;
+ return mAddr.sa.sa_family;
}
+ /**
+ * Get the sockaddr structure of the address.
+ * \return The sockaddr structure.
+ */
const struct sockaddr* address() const
{
- return mSize != 0 ? &mAddress.sa : 0;
+ return mSize != 0 ? &mAddr.sa : 0;
}
+ /**
+ * Get the size of the sockaddr structure of the address.
+ * \return The size of the sockaddr structure.
+ */
size_t size() const
{
return mSize;
}
- static int resolve(const std::string& service, const std::string& name,
- int type, int family, std::vector<SocketAddress>& resolved)
+ /**
+ * Get a list of addresses resolved to by the given search criteria.
+ * This can be used to perform lookups for name resolution, so this
+ * 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 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,
+ int type,
+ int family,
+ std::vector<SocketAddress>& resolved)
+ {
+ struct addrinfo* list = resolve(service.c_str(), host.c_str(),
+ type, family,
+ AI_ADDRCONFIG | AI_V4MAPPED);
+ int result = collectAddresses(list, resolved);
+ freeaddrinfo(list);
+ return result;
+ }
+
+ /**
+ * Get a list of addresses resolved to by the given search criteria.
+ * The addresses will be suitable for accepting on a local port.
+ * \param service The service name or port number.
+ * \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,
+ int type,
+ int family,
+ std::vector<SocketAddress>& resolved)
+ {
+ struct addrinfo* list = resolve(service.c_str(), 0,
+ type, family,
+ AI_PASSIVE);
+ int result = collectAddresses(list, resolved);
+ freeaddrinfo(list);
+ return result;
+ }
+
+
+private:
+
+ static struct addrinfo* resolve(const char* service,
+ const char* node,
+ int type,
+ int family,
+ int flags)
{
ASSERT(type == SOCK_STREAM || type == SOCK_DGRAM);
ASSERT(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC);
- resolved.clear();
-
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = type;
- hints.ai_flags = AI_PASSIVE;
+ hints.ai_flags = flags;
+
+ struct addrinfo* addr;
+ int status = getaddrinfo(node, service, &hints, &addr);
- struct addrinfo* list;
- int status = getaddrinfo(name.length() > 0 ? name.c_str() : 0,
- service.length() > 0 ? service.c_str() : 0, &hints, &list);
if (status == 0)
{
- for (struct addrinfo* addr = list;
- addr != 0; addr = addr->ai_next)
- {
- resolved.push_back(SocketAddress(addr, name));
- }
-
- freeaddrinfo(list);
+ return addr;
}
else
{
Mf::logWarning(gai_strerror(status));
- return -1;
+ return 0;
}
+ }
+
+ static int collectAddresses(struct addrinfo* addresses,
+ std::vector<SocketAddress>& resolved)
+ {
+ if (addresses)
+ {
+ resolved.clear();
- return 0;
+ for (struct addrinfo* addr = addresses;
+ addr != 0;
+ addr = addr->ai_next)
+ {
+ resolved.push_back(SocketAddress(addr));
+ }
+
+ return 0;
+ }
+ else return -1;
}
-private:
+ 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 setNameFromAddress()
+ void getHost(std::string& host)
{
-#if defined(_WIN32)
- // inet_ntop was introduced in Vista
- mName = inet_ntoa(mAddress.v4.sin_addr);
-#else
- char name[INET6_ADDRSTRLEN] = {'\0'};
- inet_ntop(mAddress.sa.sa_family, &mAddress.sa, name, sizeof(name));
- mName = name;
-#endif
+ 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;
- sockaddr_in v4;
- sockaddr_in6 v6;
+ sockaddr_in in;
sockaddr_storage storage;
- } mAddress;
+ } mAddr;
size_t mSize;
- std::string mName;
int mType;
+
+ std::string mHost;
+ std::string mService;
};
+/**
+ * The socket class represents a connection or between this node and a
+ * remote node.
+ */
class Socket
{
-public:
-
- Socket(const SocketAddress& address) :
- mFd(-1),
- mIsConnected(false),
- mAddress(address)
+ struct Impl
{
- mFd = socket(address.family(), address.type(), 0);
- }
+ SocketAddress address;
+ int fd;
+ bool isConnected;
+
+ Impl() :
+ fd(-1),
+ isConnected(false) {}
+ Impl(const SocketAddress& address, int flags = 0) :
+ address(address),
+ fd(::socket(address.family(), address.type(), flags)),
+ isConnected(false) {}
+ } mImpl;
+
+
+public:
+
+ /**
+ * Construct a socket with no associated peer.
+ */
+ Socket() {}
+
+ /**
+ * Construct a socket with an address.
+ * \param address The address.
+ * \param flags The socket options.
+ */
+ Socket(const SocketAddress& address, int flags = 0) :
+ mImpl(address, flags) {}
+
+ /**
+ * 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 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) :
+ mImpl(SocketAddress(service, name, type, family), flags) {}
+
+ /**
+ * Construct a socket without a specified host. The socket can be used
+ * to accept sockets on a local port.
+ * \param service The service name or port number.
+ * \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, int type = SOCK_STREAM,
+ int family = AF_UNSPEC, int flags = 0) :
+ mImpl(SocketAddress(service, type, family), flags) {}
+
+
+ /**
+ * Deconstruct the socket, closing it.
+ */
~Socket()
{
-#if defined(_WIN32)
- if (mFd != -1) closesocket(mFd);
-#else
- if (mFd != -1) close(mFd);
-#endif
+ close();
}
+ /**
+ * Get whether or not the socket is connected.
+ * \return True if the socket is connected, false otherwise.
+ */
bool isConnected() const
{
- return mIsConnected;
+ return mImpl.isConnected;
}
+ /**
+ * Get the address associated with the socket.
+ */
const SocketAddress& address() const
{
- return mAddress;
+ return mImpl.address;
}
+ /**
+ * Connect the socket to its peer.
+ * \return 0 on success, -1 on failure.
+ */
int connect()
{
- int result = ::connect(mFd, mAddress.address(), mAddress.size());
- mIsConnected = result != -1;
+ int result = ::connect(mImpl.fd,
+ mImpl.address.address(),
+ mImpl.address.size());
+ mImpl.isConnected = result != -1;
return result;
}
+ /**
+ * Disconnect a connected socket from its peer.
+ * \param flags Specify the socket directions to close.
+ * \return 0 on success, -1 on failure.
+ */
+ int disconnect(int flags = SHUT_RDWR)
+ {
+ return shutdown(mImpl.fd, flags);
+ }
+
+
+ /**
+ * Bind the socket to interface and port number specified in the
+ * address.
+ * \return 0 on success, -1 on failure.
+ */
int bind()
{
- return ::bind(mFd, mAddress.address(), mAddress.size());
+ return ::bind(mImpl.fd,
+ mImpl.address.address(),
+ mImpl.address.size());
}
+ /**
+ * Listen on the socket for incoming connections. This is only useful
+ * for sockets of type SOCK_STREAM.
+ * \param backlog The number of unaccepted connections to queue.
+ * \return 0 on success, -1 on failure.
+ */
int listen(int backlog = SOMAXCONN)
{
- return ::listen(mFd, backlog > 0 ? backlog : SOMAXCONN);
+ return ::listen(mImpl.fd, backlog > 0 ? backlog : SOMAXCONN);
}
- Socket accept()
+ /**
+ * Accept a new connection on the socket. This is only useful for
+ * sockets of type SOCK_STREAM.
+ * \param socket Set to the new socket on return.
+ * \return 0 on success, -1 on failure.
+ */
+ int accept(Socket& socket)
{
- return Socket(mFd);
+ Socket temp = Socket(mImpl.fd);
+ if (temp.mImpl.fd != -1)
+ {
+ socket = temp;
+ return socket.mImpl.fd;
+ }
+ return -1;
}
+ /**
+ * Set an integer socket option.
+ * \param option The option to set.
+ * \param value The new value.
+ * \return 0 on success, -1 on failure.
+ */
int set(int option, int value = 0)
{
if (option == SO_NONBLOCK)
{
#ifdef HAVE_FCNTL
- int flags = fcntl(mFd, F_GETFL);
- return fcntl(mFd, F_SETFL, (value ? O_NONBLOCK : 0) | flags);
+ int flags = fcntl(mImpl.fd, F_GETFL);
+ return fcntl(mImpl.fd,
+ F_SETFL,
+ flags | (value ? O_NONBLOCK : 0));
#else
- return ioctl(mFd, FIONBIO, value);
+ return ioctl(mImpl.fd, FIONBIO, value);
#endif
}
- return setsockopt(mFd, SOL_SOCKET, option, &value, sizeof(value));
+ return setsockopt(mImpl.fd, SOL_SOCKET, option,
+ &value, sizeof(value));
}
+ /**
+ * Set a string socket option.
+ * \param option The option to set.
+ * \param value The new value.
+ * \return 0 on success, -1 on failure.
+ */
int set(int option, const std::string& value)
{
- return setsockopt(mFd, SOL_SOCKET, option,
- value.data(), value.length());
+ return setsockopt(mImpl.fd, SOL_SOCKET, option,
+ value.data(), value.length());
}
+ /**
+ * Get an integer socket option.
+ * \param option The option to set.
+ * \param value The new value.
+ * \return 0 on success, -1 on failure.
+ */
int get(int option, int& value)
{
if (option == SO_NONBLOCK)
{
#ifdef HAVE_FCNTL
- int flags = fcntl(mFd, F_GETFL);
+ int flags = fcntl(mImpl.fd, F_GETFL);
return flags & O_NONBLOCK;
#else
- return ioctl(mFd, FIONBIO, &value);
+ return ioctl(mImpl.fd, FIONBIO, &value);
#endif
}
socklen_t optlen = sizeof(value);
- return getsockopt(mFd, SOL_SOCKET, option, &value, &optlen);
+ return getsockopt(mImpl.fd, SOL_SOCKET, option, &value, &optlen);
}
+ /**
+ * Get a string socket option.
+ * \param option The option to set.
+ * \param value The new value.
+ * \return 0 on success, -1 on failure.
+ */
int get(int option, std::string& value)
{
- char str[64] = {'\0'};
+ char str[256] = {'\0'};
socklen_t optlen = sizeof(str);
- int result = getsockopt(mFd, SOL_SOCKET, option, &str, &optlen);
+ int result = getsockopt(mImpl.fd, SOL_SOCKET, option,
+ &str, &optlen);
value = str;
return result;
}
- ssize_t write(const void* bytes, size_t size)
+ /**
+ * Write some bytes to the socket. Use this for connected sockets.
+ * \param bytes The bytes.
+ * \param size The number of bytes.
+ * \param flags The send options.
+ * \return The number of bytes written.
+ */
+ ssize_t write(const void* bytes, size_t size, int flags = 0)
{
- return send(mFd, bytes, size, 0);
+ return send(mImpl.fd, bytes, size, flags);
}
+
+ /**
+ * Write some bytes to the socket using the given address. Use this
+ * for unconnected sockets.
+ * \param bytes The bytes.
+ * \param size The number of bytes.
+ * \param address The address to send to.
+ * \param flags The send options.
+ * \return The number of bytes written.
+ */
ssize_t write(const void* bytes, size_t size,
- const SocketAddress& address)
+ const SocketAddress& address, int flags = 0)
{
- return sendto(mFd, bytes, size, 0,
+ return sendto(mImpl.fd, bytes, size, flags,
address.address(), address.size());
}
- ssize_t write(const Packet& packet)
+ /**
+ * Write a packet to the socket. Use this for connected sockets.
+ * \param packet The packet.
+ * \param flags The send options.
+ * \return The number of bytes written.
+ */
+ ssize_t write(const Packet& packet, int flags = 0)
{
- return write(packet.bytes(), packet.size());
+ return write(packet.bytes(), packet.size(), flags);
}
- ssize_t write(const Packet& packet, const SocketAddress& address)
+ /**
+ * Write a packet to the socket using the given address. Use this for
+ * unconnected sockets.
+ * \param packet The packet.
+ * \param address The address to send to.
+ * \param flags The send options.
+ * \return The number of bytes written.
+ */
+ ssize_t write(const Packet& packet, const SocketAddress& address,
+ int flags = 0)
{
- return write(packet.bytes(), packet.size(), address);
+ return write(packet.bytes(), packet.size(), address, flags);
}
- ssize_t read(void* bytes, size_t size)
+ /**
+ * Read some bytes from the socket. Use this for connected sockets.
+ * \param bytes The buffer to store the bytes.
+ * \param size The size of the buffer.
+ * \param flags The recv options.
+ * \return The number of bytes read.
+ */
+ ssize_t read(void* bytes, size_t size, int flags = 0)
{
- return recv(mFd, bytes, size, 0);
+ return recv(mImpl.fd, bytes, size, flags);
}
- ssize_t read(void* bytes, size_t size, SocketAddress& address)
+ /**
+ * Read some bytes from the socket using the given address. Use this
+ * for unconnected sockets.
+ * \param bytes The buffer to store the bytes.
+ * \param size The size of the buffer.
+ * \param address The address to read from.
+ * \param flags The recv options.
+ * \return The number of bytes read.
+ */
+ ssize_t read(void* bytes, size_t size, SocketAddress& address,
+ int flags = 0)
{
union
{
} addr;
socklen_t length = sizeof(addr);
- ssize_t result = recvfrom(mFd, bytes, size, 0, &addr.sa, &length);
+ ssize_t result = recvfrom(mImpl.fd, bytes, size, flags,
+ &addr.sa, &length);
if (result != -1)
{
- address = SocketAddress(&addr.sa, length, mAddress.type());
+ address = SocketAddress(&addr.sa, length, mImpl.address.type());
}
return result;
}
- ssize_t read(Packet& packet)
+ /**
+ * Read a packet from the socket. Use this for connected sockets.
+ * \param packet Set to the packet read on return.
+ * \param flags The recv options.
+ * \return The number of bytes read.
+ */
+ ssize_t read(Packet& packet, int flags = 0)
{
char buffer[65536];
- ssize_t result = read(buffer, sizeof(buffer));
+ ssize_t result = read(buffer, sizeof(buffer), flags);
if (result != -1) packet = Packet(buffer, result);
return result;
}
- ssize_t read(Packet& packet, SocketAddress& address)
+ /**
+ * Read a packet from the socket using the given address. Use this for
+ * unconnected sockets.
+ * \param packet Set to the packet read on return.
+ * \param address The address to read from.
+ * \param flags The recv options.
+ * \return The number of bytes read.
+ */
+ ssize_t read(Packet& packet, SocketAddress& address, int flags = 0)
{
char buffer[65536];
- ssize_t result = read(buffer, sizeof(buffer), address);
+ ssize_t result = read(buffer, sizeof(buffer), address, flags);
if (result != -1) packet = Packet(buffer, result);
return result;
}
+ // The rest of this junk is used to implement the "move" semantics
+ // correctly, since it makes no sense for socket objects to be copied.
+
+ Socket(Socket& move) :
+ mImpl(move.mImpl)
+ {
+ move.mImpl.fd = -1;
+ move.mImpl.isConnected = false;
+ }
+
+ Socket(Impl move) :
+ mImpl(move) {}
+
+ Socket& operator=(Socket& move)
+ {
+ close();
+ mImpl = move.mImpl;
+ move.mImpl.fd = -1;
+ move.mImpl.isConnected = false;
+ return *this;
+ }
+
+ Socket& operator=(Impl move)
+ {
+ close();
+ mImpl = move;
+ return *this;
+ }
+
+ operator Impl()
+ {
+ Impl impl(mImpl);
+ mImpl.fd = -1;
+ mImpl.isConnected = false;
+ return impl;
+ }
+
+
private:
Socket(int fd)
} addr;
socklen_t length = sizeof(addr);
- mFd = ::accept(fd, &addr.sa, &length);
- mAddress = SocketAddress(&addr.sa, length);
+ mImpl.fd = ::accept(fd, &addr.sa, &length);
+ if (mImpl.fd != -1)
+ {
+ mImpl.isConnected = true;
+ mImpl.address = SocketAddress(&addr.sa, length);
+ }
}
-
- int mFd;
- bool mIsConnected;
- SocketAddress mAddress;
+ void close()
+ {
+#if defined(_WIN32)
+ if (mImpl.fd != -1) closesocket(mImpl.fd);
+#else
+ if (mImpl.fd != -1) ::close(mImpl.fd);
+#endif
+ }
};
+/**
+ * An asynchronous task to resolve addresses.
+ */
class ResolverTask : public ThreadedTask
{
public:
- ResolverTask(const std::string& service, const std::string& name,
- int type = SOCK_STREAM, int family = AF_UNSPEC) :
+ /**
+ * Construct a resolver task from a service and hostname.
+ * \param service Server name or port number.
+ * \param host 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,
+ int type = SOCK_STREAM,
+ int family = AF_UNSPEC) :
mIsDone(false)
{
mFunction = boost::bind(&ResolverTask::resolve,
- this, service, name, type, family);
+ this, service, host, type, family);
}
+ /**
+ * Get whether or not the task is done.
+ * \return True if the task has finished, false otherwise.
+ */
bool isDone() const
{
return mIsDone;
}
+ /**
+ * Start the task. This does nothing if the task was already run or is
+ * currently running.
+ */
void run()
{
- if (!mThread) mThread = Mf::detachFunction(mFunction);
+ if (!isDone() && !mThread.isValid())
+ {
+ mThread = Thread::detach(mFunction);
+ }
}
+ /**
+ * Get the addresses resolved. This is filled and safe to access after
+ * the task finishes.
+ * \return List of addresses.
+ * \see isDone()
+ */
const std::vector<SocketAddress>& addresses() const
{
return mAddressList;
private:
- int resolve(const std::string& service, const std::string& name,
- int type, int family)
+ int resolve(const std::string& service,
+ const std::string& host,
+ int type,
+ int family)
{
- int status = SocketAddress::resolve(service, name,
- type, family, mAddressList);
+ int status = SocketAddress::resolve(service, host,
+ type, family,
+ mAddressList);
mIsDone = true;
return status;
}
std::vector<SocketAddress> mAddressList;
bool mIsDone;
- Function mFunction;
+ Thread::Function mFunction;
};