Skip to content

Commit

Permalink
Add unix domain socket support
Browse files Browse the repository at this point in the history
  • Loading branch information
Martijn Otto committed Jul 29, 2015
1 parent ad067ab commit ec8f43d
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 41 deletions.
13 changes: 13 additions & 0 deletions include/tcp/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,19 @@ class Server
Server(Loop *loop, uint16_t port) :
Server(loop, Net::Ip(), port) {}

/**
* Constructor to listen on a unix domain socket
*
* @param loop Event loop
* @param path Path for the socket
*/
Server(Loop *loop, const char *path) :
_socket(loop, path)
{
// listen to the socket
if (!_socket.listen()) throw Exception(strerror(errno));
}

/**
* Constructor to listen to a random port
* @param loop Event loop
Expand Down
134 changes: 93 additions & 41 deletions include/tcp/socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
*
* @copyright 2014 Copernica BV
*/


/**
* Dependencies
*/
#include <sys/un.h>

/**
* Set up namespace
*/
Expand All @@ -27,14 +32,14 @@ class Socket : public Fd
{
// structure to initialize
struct sockaddr_in info;

// fill the members
info.sin_family = AF_INET;
info.sin_port = htons(port);

// copy address
memcpy(&info.sin_addr, ip.internal(), sizeof(struct in_addr));

// bind
return ::bind(_fd, (struct sockaddr *)&info, sizeof(struct sockaddr_in)) == 0;
}
Expand All @@ -49,20 +54,20 @@ class Socket : public Fd
{
// structure to initialize
struct sockaddr_in6 info;

// fill the members
info.sin6_family = AF_INET6;
info.sin6_port = htons(port);
info.sin6_flowinfo = 0;
info.sin6_scope_id = 0;

// copy the address
memcpy(&info.sin6_addr, ip.internal(), sizeof(struct in6_addr));

// bind
return ::bind(_fd, (struct sockaddr *)&info, sizeof(struct sockaddr_in6)) == 0;
}

/**
* Bind the socket to an IP and port
* @param ip IP to bind to
Expand All @@ -78,39 +83,65 @@ class Socket : public Fd
}
}

/**
* Bind the socket to a unix domain path
*
* @param path The path to bind to
* @return Did we successfully bind
*/
bool bind(const char *path)
{
// structure to initialize
struct sockaddr_un info;

// fill the members
info.sun_family = AF_UNIX;
strncpy(info.sun_path, path, sizeof(info) - 1);

// cleanup leftover sockets
unlink(path);

// we must give the total length to bind, which must be
// the size of the family + plus the length of the path
size_t length = sizeof(info.sun_family) + std::strlen(info.sun_path);

// bind the socket now
return ::bind(_fd, reinterpret_cast<struct sockaddr*>(&info), length) == 0;
}

/**
* Constructor to create a socket object by wrapping it around an existing socket
* @param loop Event loop
* @param fd The socket filedescriptor
*/
Socket(Loop *loop, int fd) : Fd(loop, fd)
Socket(Loop *loop, int fd) : Fd(loop, fd)
{
// must be valid
if (_fd < 0) throw Exception(strerror(errno));

// mark the socket as a "keepalive" socket, to send a sort of ping
// every once in a while to ensure that the other end is connected,
// even when no traffic was seen on the socket for a long time
int keepalive = 1;
setsockopt(_fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(int));
}

public:
/**
* Constructor to directly bind the socket to an IP and port
*
*
* Watch out! The constructor will throw an exception in case of an error.
*
*
* @param loop Event loop
* @param ip IP address to bind to
* @param port Port number to bind to (or 0 to use a random port)
*/
Socket(Loop *loop, const Net::Ip &ip = Net::Ip(), uint16_t port = 0) :
Socket(Loop *loop, const Net::Ip &ip = Net::Ip(), uint16_t port = 0) :
Fd(loop, socket(ip.version() == 6 ? AF_INET6 : AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0))
{
// this should succeed
if (_fd < 0) throw Exception(strerror(errno));

// we are going to bind the socket
if (!bind(ip, port)) throw Exception(strerror(errno));

Expand All @@ -120,19 +151,40 @@ class Socket : public Fd
int keepalive = 1;
setsockopt(_fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(int));
}


/**
* Constructor to directly bind the socket to a unix domain path
*
* Watch out! The constructor will throw an exception in case of an error.
*
* @param loop Event loop
* @param path The path to listen on
*/
Socket(Loop *loop, const char *path) :
Fd(loop, socket(AF_UNIX, SOCK_STREAM, 0))
{
// this should succeed
if (_fd < 0) throw Exception(strerror(errno));

// we are going to bind the socket
if (!bind(path)) throw Exception(strerror(errno));

// no keepalive configuration here, this does not
// really apply to unix domain sockets in any case
}

/**
* Sockets can not be copied
* @param socket
*/
Socket(const Socket &socket) = delete;

/**
* Move constructor
* @param socket
*/
Socket(Socket &&socket) : Fd(std::move(socket)) {}

/**
* Destructor
*/
Expand All @@ -141,7 +193,7 @@ class Socket : public Fd
// close the filedescriptor
close();
}

/**
* Is the socket connected?
* @return bool
Expand All @@ -150,7 +202,7 @@ class Socket : public Fd
{
// filedescriptor must be valid
if (_fd < 0) return false;

// check peer address
return PeerAddress(_fd).valid();
}
Expand All @@ -165,14 +217,14 @@ class Socket : public Fd
{
// structure to initialize
struct sockaddr_in info;

// fill the members
info.sin_family = AF_INET;
info.sin_port = htons(port);

// copy address
memcpy(&info.sin_addr, ip.internal(), sizeof(struct in_addr));

// connect
return ::connect(_fd, (struct sockaddr *)&info, sizeof(struct sockaddr_in)) == 0 || errno == EINPROGRESS;
}
Expand All @@ -187,20 +239,20 @@ class Socket : public Fd
{
// structure to initialize
struct sockaddr_in6 info;

// fill the members
info.sin6_family = AF_INET6;
info.sin6_port = htons(port);
info.sin6_flowinfo = 0;
info.sin6_scope_id = 0;

// copy the address
memcpy(&info.sin6_addr, ip.internal(), sizeof(struct in6_addr));

// connect
return ::connect(_fd, (struct sockaddr *)&info, sizeof(struct sockaddr_in6)) == 0 || errno == EINPROGRESS;
}

/**
* Connect the socket to a remote IP
* @param ip
Expand All @@ -225,7 +277,7 @@ class Socket : public Fd
{
return connect(address.ip(), address.port());
}

/**
* Listen to a socket
* @param backlog
Expand All @@ -239,14 +291,14 @@ class Socket : public Fd
// set SO_REUSADDR for listening sockets
int reuse = 1;
setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));

// done
return true;
}

/**
* Accept a connection on the socket
*
*
* Watch out! This method will throw an exception in case of an error.
*
* @return Socket
Expand All @@ -259,9 +311,9 @@ class Socket : public Fd

/**
* Send data to the connection
*
*
* This method is directly forwarded to the ::send() system call.
*
*
* @param buf Pointer to a buffer
* @param len Size of the buffer
* @param flags Optional additional flags
Expand All @@ -274,9 +326,9 @@ class Socket : public Fd

/**
* Send data to the connection
*
*
* This method is directly forwarded to the ::writev() system call
*
*
* @param iov Array of struct iovec objects
* @param iovcnt Number of items in the array
* @return ssize_t Number of bytes sent
Expand All @@ -285,12 +337,12 @@ class Socket : public Fd
{
return ::writev(_fd, iov, iovcnt);
}

/**
* Receive data from the connection
*
*
* This method is directly forwarded to the ::recv() system call.
*
*
* @param buf Pointer to a buffer
* @param len Size of the buffer
* @param flags Optional additional flags
Expand All @@ -300,7 +352,7 @@ class Socket : public Fd
{
return ::recv(_fd, buf, len, flags);
}

/**
* Close the socket
* @return bool
Expand All @@ -309,10 +361,10 @@ class Socket : public Fd
{
// try to close
if (::close(_fd) < 0 && errno != EBADF) return false;

// forget the filedescriptor
_fd = -1;

// done
return true;
}
Expand Down

0 comments on commit ec8f43d

Please sign in to comment.