separate sockaddr stuff out of NetworkAddress... now called NetworkEndpoint
#ifndef NETWORK_SOCKET_HH
#define NETWORK_SOCKET_HH
// forward-declare
class NetworkSocket;
#include "../Error.hh"
#include "Address.hh"
#include "SockAddr.hh"
#include "Reactor.hh"
/*
* Platform-specific includes
*/
#ifndef WIN32
// linux
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#define closesocket close
#else
#error "This network code won't compile on win32 :)"
#endif
/**
* We use ClanLib's Socket API, but with our own extensions...
*/
class NetworkSocket {
private:
/**
* Socket family/type/protocol
*/
struct socket_type {
/** Socket domain */
int family;
/** Socket type */
int socktype;
/** Socket protocol */
int protocol;
/** Simple constructor */
socket_type (int family = 0, int socktype = 0, int protocol = 0) : family(family), socktype(socktype), protocol(protocol) { }
};
/** These are nonzero if given via the constructor, used to filter out unwanted addrinfos */
socket_type sock_type;
/** The file descriptor */
int fd;
/** Our current type, intialized via constructor, but updated by lazy_socket */
socket_type type;
/**
* Has the socket been explicitly bind()'d? If so, force ourselves to use this socket in connect().
*/
bool bound : 1;
/**
* Registered to reactor?
*/
bool registered : 1;
/**
* Do we want to know about recv()s?
*/
bool want_read : 1;
/**
* Is the write buffer full?
*/
bool want_write : 1;
/**
* The reactor that we use, defaults to NetworkReactor::current
*/
NetworkReactor *reactor;
/**
* Read/write signals
*/
CL_Signal_v0 _sig_read, _sig_write;
public:
/**
* Construct a socket of the specific type. Family and protocol can be left as NULL, but type should usually
* be specified. The given reactor is used for polling, defaults to NetworkReactor::current
*/
NetworkSocket (int family, int socktype, int protocol = 0, NetworkReactor *reactor = NULL);
/**
* Create a socket from the given pre-existing fd
*/
NetworkSocket (int fd, socket_type type, NetworkReactor *reactor = NULL);
/**
* Force-close the socket if it's still open
*/
~NetworkSocket (void);
private:
// XXX: nocopy
/**
* Reset bound+poll
*/
void reset (void);
/**
* Create a new socket of the given type, unless we already have one
*/
void lazy_socket (int family, int type, int protocol);
/**
* Close and reset, ignoring errors
*/
void force_close (void);
public:
/**
* Get the socket fd... promise not to break it
*/
int get_socket (void) const { return fd; }
/**
* Bind to a specific local address
*/
void bind (const NetworkEndpoint &addr);
/**
* Put socket into listen mode
*/
void listen (int backlog);
/**
* Get local address
*/
NetworkAddress get_local_address (void);
/**
* Get remote address
*/
NetworkAddress get_remote_address (void);
/**
* Make send/recv/connect non-blocking
*/
void set_nonblocking (bool nonblocking);
/**
* Accept a new connection, optionally giving the connection's source address
*/
NetworkSocket* accept (NetworkAddress *src);
/**
* Establish a new connection
*/
void connect (const NetworkEndpoint &addr);
/**
* Send, optionally using the specific destination
*
* @param buf bytes to send
* @param size how many bytes to try and send
* @param dest optional specific destination address
* @return number of bytes sent, zero if busy
* @throw NetworkSocketError on error
*/
size_t send (const char *buf, size_t size, const NetworkAddress *dest = NULL);
/**
* Recv, optionally storing the source in src
*
* @param buf where to recv into
* @param size how many bytes to try and receive
* @param src optionally store source address
* @return number of bytes received, zero if none available
* @throw NetworkSocketEOFError if the connection was closed
* @throw NetworkSocketError on error
*/
size_t recv (char *buf, size_t size, NetworkAddress *src = NULL);
/**
* Close and reset the socket
*/
void close (void);
/**
* Triggered when socket becomes readable
*/
CL_Signal_v0& sig_read (void) { return _sig_read; }
/**
* Triggered when socket becomes writeable after a send that returned zero
*/
CL_Signal_v0& sig_write (void) { return _sig_write; }
/**
* Register to NetworkReactor unless already registered
*/
void register_poll (void);
/**
* Trigger sig_read() once socket is ready for recv?
*/
void set_poll_read (bool want_read) { this->want_read = want_read; if (!registered) register_poll(); }
/**
* Trigger sig_write() once socket is ready for send?
*/
void set_poll_write (bool want_write) { this->want_write = want_write; if (!registered) register_poll(); }
/**
* What events this socket is interested in.
*/
NetworkPollMask get_poll (void) {
return (want_read ? POLL_READ : 0) | (want_write ? POLL_WRITE : 0);
}
/**
* Notify of events
*/
void notify (NetworkPollMask mask) {
if (mask & POLL_READ) _sig_read();
if (mask & POLL_WRITE) _sig_write();
}
};
/**
* Base class for expcetions thrown by socket methods
*/
class NetworkSocketError : public Error {
protected:
static std::string build_str (const NetworkSocket &socket, const char *op, const char *err);
public:
NetworkSocketError (const NetworkSocket &socket, const char *op, const char *err);
};
/**
* Errno-enabled exception, most common type of NetworkSocketError
*/
class NetworkSocketErrno : public NetworkSocketError {
public:
NetworkSocketErrno (const NetworkSocket &socket, const char *op);
};
/**
* Recv returned EOF
*/
class NetworkSocketEOFError : public NetworkSocketError {
public:
NetworkSocketEOFError (const NetworkSocket &socket, const char *op) :
NetworkSocketError(socket, op, "EOF") { }
};
#endif /* NETWORK_SOCKET_HH */