*
**************************************************************************/
+/**
+ * \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(mName, mService);
}
/**
mType(type)
{
memcpy(&mAddr.sa, addr, size);
- getServiceAndHostName(mService, mHost);
+ getNameAndService(mName, mService);
}
* 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.
* \param flags The send options.
* \return The number of bytes written.
*/
- ssize_t write(const void* bytes, size_t size,
- const SocketAddress& address, int flags = 0)
+ ssize_t write(const void* bytes,
+ size_t size,
+ const SocketAddress& address,
+ int flags = 0)
{
return sendto(mImpl.fd, bytes, size, flags,
- address.address(), address.size());
+ address.address(), address.size());
}
/**
* \param flags The send options.
* \return The number of bytes written.
*/
- ssize_t write(const Packet& packet, const SocketAddress& address,
- int flags = 0)
+ ssize_t write(const Packet& packet,
+ const SocketAddress& address,
+ int flags = 0)
{
return write(packet.bytes(), packet.size(), address, flags);
}
*/
ssize_t read(void* bytes, size_t size, int flags = 0)
{
- return recv(mImpl.fd, bytes, size, flags);
+ ssize_t result = recv(mImpl.fd, bytes, size, flags);
+ if (result == 0) mImpl.isConnected = false;
+ return result;
}
/**
* \param flags The recv options.
* \return The number of bytes read.
*/
- ssize_t read(void* bytes, size_t size, SocketAddress& address,
- int flags = 0)
+ ssize_t read(void* bytes,
+ size_t size,
+ SocketAddress& address,
+ int flags = 0)
{
union
{
{
address = SocketAddress(&addr.sa, length, mImpl.address.type());
}
+ else if (result == 0)
+ {
+ mImpl.isConnected = false;
+ }
return result;
}
};
+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_