--- a/src/Config.hh Tue Dec 16 20:30:35 2008 +0000
+++ b/src/Config.hh Tue Dec 16 23:21:26 2008 +0000
@@ -22,8 +22,11 @@
const uint16_t MAP_HEIGHT = 800;
const float MAP_SCALE = 1; // One "pixel" in "real" units
-// Simulation
-const uint16_t PHYSICS_TICK_MS = 10;
+/** Engine timeout, this determines our minimum tick rate */
+const TimeMS ENGINE_TIMEOUT_MS = 10;
+
+/** Physics tick interval */
+const TimeMS PHYSICS_TICK_MS = 10;
/** Input handling keyboard poll interval */
const TimeMS INPUT_POLL_INTERVAL = 10;
--- a/src/Engine.cc Tue Dec 16 20:30:35 2008 +0000
+++ b/src/Engine.cc Tue Dec 16 23:21:26 2008 +0000
@@ -1,6 +1,8 @@
#include "Engine.hh"
#include "SinglePlayer.hh"
+#include "Network/Reactor.hh"
+#include "Config.hh"
#include <iostream>
@@ -41,14 +43,26 @@
}
void Engine::run (void) {
+ // our NetworkReactor
+ NetworkReactor *reactor = NetworkReactor::current;
+
+ // timeout info
+ timeval timeout;
+
while (is_running) {
// this does.... magical things
CL_System::keep_alive();
-
- // if I can't find some better way to do this in ClanLib by next thursday, then it f*%!ing sucks
- // ideally, we should be able to have a main loop that does timed waits on I/O, fufilling some set of timers
- // but as far as I can tell, ClanLib doesn't have anything like that
- CL_System::sleep(10);
+
+ // setup our timeout to ENGINE_TIMEOUT_MS
+ timeout.tv_sec = 0;
+ timeout.tv_usec = ENGINE_TIMEOUT_MS * 1000;
+
+ /*
+ * Thursday came and went, I re-wrote clan-event.
+ *
+ * We use the NetworkReactor for sleeping, as it handles it effeciently even if we're not using network.
+ */
+ reactor->poll(&timeout);
}
}
--- a/src/Network/Address.cc Tue Dec 16 20:30:35 2008 +0000
+++ b/src/Network/Address.cc Tue Dec 16 23:21:26 2008 +0000
@@ -80,6 +80,9 @@
// copy over to address
memcpy(&this->address, addr, len);
+ // and update address_length
+ this->address_length = len;
+
// and the hostname + service
hostname = host_buf;
service = serv_buf;
--- a/src/Network/Address.hh Tue Dec 16 20:30:35 2008 +0000
+++ b/src/Network/Address.hh Tue Dec 16 23:21:26 2008 +0000
@@ -68,6 +68,10 @@
* Construct a NetworkAddress from a machine-readable address of the given length
*/
NetworkAddress (const sockaddr *addr, socklen_t len);
+
+ /*
+ * We can use the default copy-constructor and assignment operator
+ */
public:
/**
--- a/src/Network/Buffer.cc Tue Dec 16 20:30:35 2008 +0000
+++ b/src/Network/Buffer.cc Tue Dec 16 23:21:26 2008 +0000
@@ -153,36 +153,44 @@
memcpy(buf + offset, buf_ptr, buf_size);
}
-void NetworkBufferOutput::flush_write (void) {
+bool NetworkBufferOutput::flush_write (void) {
size_t ret;
// ignore if we don't have any data buffered
if (offset == 0)
- return;
+ return false;
// attempt to write as much as possible
ret = socket->send(buf, offset);
// busy?
if (ret == 0)
- return;
+ return true;
// trim the buffer
trim(ret);
+
+ // anything left?
+ return (offset > 0);
}
-void NetworkBufferOutput::write_prefix (char *buf, uint16_t prefix) {
+bool NetworkBufferOutput::write_prefix (char *buf, uint16_t prefix) {
uint16_t nval = htons(prefix);
push_write((char*) &nval, sizeof(uint16_t));
push_write(buf, prefix);
+
+ // anything in the buffer?
+ return (offset > 0);
}
-void NetworkBufferOutput::write_prefix (char *buf, uint32_t prefix) {
+bool NetworkBufferOutput::write_prefix (char *buf, uint32_t prefix) {
uint32_t nval = htonl(prefix);
push_write((char*) &nval, sizeof(uint32_t));
push_write(buf, prefix);
+
+ // anything in the buffer?
+ return (offset > 0);
}
-
--- a/src/Network/Buffer.hh Tue Dec 16 20:30:35 2008 +0000
+++ b/src/Network/Buffer.hh Tue Dec 16 23:21:26 2008 +0000
@@ -204,18 +204,21 @@
public:
/**
* If we have data in our buffer, flush it out using send().
+ *
+ * @return true if there's still buffered data left to write, false otherwise
*/
- void flush_write (void);
+ bool flush_write (void);
// @{
/**
* Write out the given data, writing first the prefix, and then the data itself, using push_write.
- *
+ *
* @param buf the data to write
* @param prefix the amount of data
+ * @return true if we had to buffer data, false otherwise
*/
- void write_prefix (char *buf, uint16_t prefix);
- void write_prefix (char *buf, uint32_t prefix);
+ bool write_prefix (char *buf, uint16_t prefix);
+ bool write_prefix (char *buf, uint32_t prefix);
// @}
};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Network/Error.cc Tue Dec 16 23:21:26 2008 +0000
@@ -0,0 +1,20 @@
+
+#include "Error.hh"
+
+#include <cerrno>
+#include <sstream>
+
+std::string NetworkErrno::build_str (std::string op, int err) {
+ std::stringstream ss;
+
+ ss << op << ": " << strerror(errno);
+
+ return ss.str();
+}
+
+NetworkErrno::NetworkErrno (std::string op) :
+ Error(build_str(op, errno)), err(errno)
+{
+
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Network/Error.hh Tue Dec 16 23:21:26 2008 +0000
@@ -0,0 +1,21 @@
+#ifndef NETWORK_ERROR_HH
+#define NETWORK_ERROR_HH
+
+#include "../Error.hh"
+
+/**
+ * An error class that appends the current value of errno as an error message
+ *
+ * <op>: <strerror>
+ */
+class NetworkErrno : public Error {
+ protected:
+ static std::string build_str (std::string op, int err);
+
+ public:
+ int err;
+
+ NetworkErrno (std::string op);
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Network/Reactor.cc Tue Dec 16 23:21:26 2008 +0000
@@ -0,0 +1,103 @@
+
+#include "Reactor.hh"
+
+
+NetworkReactor::NetworkReactor (void) :
+ sockets()
+{
+
+}
+
+void NetworkReactor::poll (timeval *timeout) {
+ // start counting the maximum fd from -1, so that select is given nfds=0 if our list is empty
+ int fd_max = 0;
+
+ // zero our fdsets
+ fd_set read, write;
+ FD_ZERO(&read);
+ FD_ZERO(&write);
+
+ // inspect our sockets
+ for (std::list<NetworkSocket*>::iterator it = sockets.begin(); it != sockets.end(); it++) {
+ NetworkSocket *socket = *it;
+
+ // ask socket what events it wants
+ NetworkPollMask mask = socket->get_poll();
+
+ // set read/write
+ if (mask) {
+ // socket file descriptor...
+ int fd = socket->get_socket();
+
+ // ignore invalid FDs
+ if (fd < 0)
+ continue;
+
+ // update fd_max?
+ if (fd > fd_max)
+ fd_max = fd;
+
+ // read...
+ if (mask & POLL_READ)
+ FD_SET(socket->get_socket(), &read);
+
+ // write...
+ if (mask & POLL_WRITE)
+ FD_SET(socket->get_socket(), &write);
+ }
+ }
+
+ // run select
+ int ret;
+
+ // we never care about except
+ // pass NULL fdsets if they are empty
+ if ((ret = select(
+ fd_max + 1,
+ fd_max ? &read : NULL,
+ fd_max ? &write : NULL,
+ NULL,
+ timeout
+ )) < 0)
+ throw NetworkReactorError("select");
+
+ // ignore if we just timed out
+ if (ret == 0)
+ return;
+
+ // notify up to <ret> socket-events
+ for (std::list<NetworkSocket*>::iterator it = sockets.begin(); it != sockets.end() && ret > 0; it++) {
+ NetworkSocket *socket = *it;
+
+ // socket file descriptor...
+ int fd = socket->get_socket();
+
+ // ignore invalid FDs
+ if (fd < 0)
+ continue;
+
+ // mask of events detected
+ NetworkPollMask mask = 0;
+
+ // detect read
+ if (FD_ISSET(fd, &read)) {
+ mask |= POLL_READ;
+ ret--;
+ }
+
+ // detect write
+ if (FD_ISSET(fd, &write)) {
+ mask |= POLL_WRITE;
+ ret--;
+ }
+
+ // notify?
+ if (mask)
+ socket->notify(mask);
+ }
+}
+
+// the global reactor
+static NetworkReactor g_reactor;
+NetworkReactor *NetworkReactor::current = &g_reactor;
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Network/Reactor.hh Tue Dec 16 23:21:26 2008 +0000
@@ -0,0 +1,83 @@
+#ifndef NETWORK_REACTOR_HH
+#define NETWORK_REACTOR_HH
+
+// forward-declare
+class NetworkReactor;
+
+/**
+ * Events to poll for
+ */
+enum NetworkPollBit {
+ POLL_READ = 0x01,
+ POLL_WRITE = 0x02,
+};
+
+/**
+ * Poll event bitmask of NetworkPollBit's
+ */
+typedef int NetworkPollMask;
+
+#include "Socket.hh"
+#include "Error.hh"
+
+/*
+ * Platform-specific includes
+ */
+#ifndef WIN32
+ // linux
+ #include <sys/select.h>
+#else
+ #error "This network code won't compile on win32 :)"
+#endif
+
+#include <list>
+
+/**
+ * A Reactor manages a set of NetworkSockets, providing readyness notification and a poll method
+ */
+class NetworkReactor {
+ protected:
+ std::list<NetworkSocket*> sockets;
+
+ public:
+ /**
+ * Construct the empty reactor
+ */
+ NetworkReactor (void);
+
+ /**
+ * Our static global reactor
+ */
+ static NetworkReactor *current;
+
+ /**
+ * Add a NetworkSocket to our list of sockets. The desired notification states are fetched directly from the
+ * socket itself.
+ *
+ * @param socket the socket to watch
+ */
+ void add_socket (NetworkSocket *socket) { sockets.push_back(socket); }
+
+ /**
+ * Remove a NetworkSocket from our list of sockets.
+ *
+ * @param socket the socket to stop watching
+ */
+ void remove_socket (NetworkSocket *socket) { sockets.remove(socket); }
+
+ /**
+ * Poll our sockets and drive any I/O, optionally sleeping for the given timeout. This is efficient if our
+ * sockets list is empty.
+ */
+ void poll (timeval *timeout = NULL);
+};
+
+/**
+ * Reactor error
+ */
+class NetworkReactorError : public NetworkErrno {
+ public:
+ NetworkReactorError (std::string op) : NetworkErrno(op) { }
+};
+
+#endif
--- a/src/Network/Session.cc Tue Dec 16 20:30:35 2008 +0000
+++ b/src/Network/Session.cc Tue Dec 16 23:21:26 2008 +0000
@@ -36,15 +36,18 @@
// connect TCP
NetworkTCPClient *tcp_client = new NetworkTCPClient(addr);
-
+
// create UDP socket on same address
- udp_client = new NetworkUDP(tcp_client->getLocalAddress());
+ udp_client = new NetworkUDP(tcp_client->get_local_address());
+
+ // remote address
+ NetworkAddress remote_address = tcp_client->get_remote_address();
// build client
- NetworkNode *client_node = build_node(tcp_client, udp_client, addr, NETWORK_NODE_CLIENT_SERVER);
+ NetworkNode *client_node = build_node(tcp_client, udp_client, remote_address, NETWORK_NODE_CLIENT_SERVER);
// add to nodes
- nodes[addr] = client_node;
+ nodes[remote_address] = client_node;
// connect signals
slots.connect(udp_client->sig_packet(), this, &NetworkSession::on_udp_packet);
@@ -68,7 +71,7 @@
void NetworkSession::on_tcp_client (NetworkTCPTransport *tcp_client) {
// get remote address manually, because NetworkTCPServer doesn't pass it in to us
- NetworkAddress addr = tcp_client->getRemoteAddress();
+ NetworkAddress addr = tcp_client->get_remote_address();
// build client
NetworkNode *client_node = build_node(tcp_client, udp_srv, addr, NETWORK_NODE_SERVER_CLIENT);
--- a/src/Network/Socket.cc Tue Dec 16 20:30:35 2008 +0000
+++ b/src/Network/Socket.cc Tue Dec 16 23:21:26 2008 +0000
@@ -2,61 +2,86 @@
#include "Socket.hh"
#include "../Engine.hh"
+#include <cerrno>
+#include <cstring>
#include <sstream>
-NetworkSocket::NetworkSocket (int family, int socktype, int protocol) :
- fd(-1), family(family), socktype(socktype), protocol(protocol), bound(false)
+static std::string dump_addrinfo (addrinfo *r) {
+ std::stringstream ss;
+
+ ss << "ai_family=" << r->ai_family << ", ai_canonname=" << r->ai_canonname << ": " << strerror(errno);
+
+ return ss.str();
+}
+
+static std::string dump_errno (void) {
+ return std::string(strerror(errno));
+}
+
+NetworkSocket::NetworkSocket (int family, int socktype, int protocol, NetworkReactor *reactor) :
+ sock_type(family, socktype, protocol), fd(-1), type(family, socktype, protocol), registered(0), reactor(reactor ? reactor : NetworkReactor::current)
{
-
+ reset();
}
-NetworkSocket::NetworkSocket (int fd) :
- fd(fd), family(0), socktype(0), protocol(0), bound(false)
+NetworkSocket::NetworkSocket (int fd, socket_type type, NetworkReactor *reactor) :
+ sock_type(type), fd(fd), type(type), registered(0), reactor(reactor ? reactor : NetworkReactor::current)
{
-
+ reset();
}
NetworkSocket::~NetworkSocket (void) {
// close any remaining socket
if (fd >= 0)
force_close();
+
+ // unregister from reactor?
+ if (registered)
+ reactor->remove_socket(this);
}
+void NetworkSocket::reset (void) {
+ bound = false;
+ want_read = false;
+ want_write = false;
+}
+
void NetworkSocket::lazy_socket (int family, int socktype, int protocol) {
// if we already have a socket, exit
if (fd >= 0)
return;
- // check that we don't have conflicting family/type/protocol
+ // ignore if we've requested a specific sock_type
if (
- (this->family && family != this->family) ||
- (this->socktype && socktype != this->socktype) ||
- (this->protocol && protocol != this->protocol)
+ (sock_type.family && family != sock_type.family) ||
+ (sock_type.socktype && socktype != sock_type.socktype) ||
+ (sock_type.protocol && protocol != sock_type.protocol)
)
throw NetworkSocketError(*this, "socket.create", "family/socktype/protocol mismatch");
// create the socket or fail
if ((fd = ::socket(family, socktype, protocol)) < 0)
- throw NetworkSocketOSError(*this, "socket");
+ throw NetworkSocketErrno(*this, "socket");
// update our family/type/protocol
- this->family = family;
- this->socktype = socktype;
- this->protocol = protocol;
+ type.family = family;
+ type.socktype = socktype;
+ type.protocol = protocol;
}
void NetworkSocket::force_close (void) {
// use closesocket
if (::closesocket(fd))
- Engine::log(WARN, "socket.force_close") << "error closing socket: " /* XXX: errno */;
+ Engine::log(WARN, "socket.force_close") << "error closing socket: " << dump_errno();
- // invalidate fd
+ // reset state
fd = -1;
+ reset();
}
void NetworkSocket::bind (const NetworkAddress &addr) {
// get our addrinfo
- addrinfo *r, *results = addr.get_addrinfo(family, socktype, protocol, AI_PASSIVE);
+ addrinfo *r, *results = addr.get_addrinfo(type.family, type.socktype, type.protocol, AI_PASSIVE);
// find the right address to bind to
for (r = results; r; r = r->ai_next) {
@@ -65,13 +90,13 @@
lazy_socket(r->ai_family, r->ai_socktype, r->ai_protocol);
} catch (NetworkSocketError &e) {
- Engine::log(WARN, "socket.bind") << "unable to create socket for " << r << ": " << e.what();
+ Engine::log(WARN, "socket.bind") << "unable to create socket for " << dump_addrinfo(r) << ": " << e.what();
continue;
}
// bind it, warn on errors
if (::bind(fd, r->ai_addr, r->ai_addrlen)) {
- Engine::log(WARN, "socket.bind") << "unable to bind on " << r /* XXX: errno */ ;
+ Engine::log(WARN, "socket.bind") << "unable to bind on " << dump_addrinfo(r) << ": " << dump_errno();
// close the bad socket
force_close();
@@ -98,7 +123,7 @@
void NetworkSocket::listen (int backlog) {
// just call listen
if (::listen(fd, backlog))
- throw NetworkSocketOSError(*this, "listen");
+ throw NetworkSocketErrno(*this, "listen");
}
NetworkAddress NetworkSocket::get_local_address (void) {
@@ -107,7 +132,7 @@
// do getsockname()
if (::getsockname(fd, (sockaddr *) &addr, &addrlen))
- throw NetworkSocketOSError(*this, "getsockname");
+ throw NetworkSocketErrno(*this, "getsockname");
// return addr
return NetworkAddress((sockaddr *) &addr, addrlen);
@@ -119,16 +144,16 @@
// do getpeername()
if (::getpeername(fd, (sockaddr *) &addr, &addrlen))
- throw NetworkSocketOSError(*this, "getpeername");
+ throw NetworkSocketErrno(*this, "getpeername");
// return addr
return NetworkAddress((sockaddr *) &addr, addrlen);
}
void NetworkSocket::set_nonblocking (bool nonblocking) {
- // XXX: linux-specific
+ // linux-specific
if (fcntl(fd, F_SETFL, O_NONBLOCK, nonblocking ? 1 : 0) == -1)
- throw NetworkSocketOSError(*this, "fcntl(F_SETFL, O_NONBLOCK)");
+ throw NetworkSocketErrno(*this, "fcntl(F_SETFL, O_NONBLOCK)");
}
NetworkSocket* NetworkSocket::accept (NetworkAddress *src) {
@@ -137,11 +162,11 @@
socklen_t addrlen = sizeof(addr);
// try and get the FD
- if ((new_fd = ::accept(fd, (sockaddr *) &addr, &addrlen)))
- throw NetworkSocketOSError(*this, "accept");
+ if ((new_fd = ::accept(fd, (sockaddr *) &addr, &addrlen)) < 0)
+ throw NetworkSocketErrno(*this, "accept");
// allocate new NetworkSocket for new_fd
- NetworkSocket *socket = new NetworkSocket(new_fd);
+ NetworkSocket *socket = new NetworkSocket(new_fd, type, reactor);
// update src
if (src)
@@ -153,7 +178,7 @@
void NetworkSocket::connect (const NetworkAddress &addr) {
// get our addrinfo
- addrinfo *r, *results = addr.get_addrinfo(family, socktype, protocol);
+ addrinfo *r, *results = addr.get_addrinfo(type.family, type.socktype, type.protocol);
// find the right address to bind to
for (r = results; r; r = r->ai_next) {
@@ -162,13 +187,13 @@
lazy_socket(r->ai_family, r->ai_socktype, r->ai_protocol);
} catch (NetworkSocketError &e) {
- Engine::log(WARN, "socket.connect") << "unable to create socket for " << r << ": " << e.what();
+ Engine::log(WARN, "socket.connect") << "unable to create socket for " << dump_addrinfo(r) << ": " << e.what();
continue;
}
// connect it, warn on errors
if (::connect(fd, r->ai_addr, r->ai_addrlen)) {
- Engine::log(WARN, "socket.connect") << "unable to connect to " << r /* XXX: errno */ ;
+ Engine::log(WARN, "socket.connect") << "unable to connect to " << dump_addrinfo(r) << ": " << dump_errno();
// close unless bound, to not keep invalid sockets hanging around
if (!bound)
@@ -203,12 +228,12 @@
// sendto()
if ((ret = ::sendto(fd, buf, size, 0, addr, addr_len)) < 0 && errno != EAGAIN)
- throw NetworkSocketOSError(*this, "sendto");
+ throw NetworkSocketErrno(*this, "sendto");
} else {
// send()
if ((ret = ::send(fd, buf, size, 0)) < 0 && errno != EAGAIN)
- throw NetworkSocketOSError(*this, "send");
+ throw NetworkSocketErrno(*this, "send");
}
@@ -219,9 +244,13 @@
return 0;
}
- // EAGAIN?
- if (ret < 0)
+ // EAGAIN?
+ if (ret < 0) {
+ // set want_write so we get a sig_write
+ want_write = true;
+
return 0;
+ }
// return number of bytes sent
return ret;
@@ -237,15 +266,18 @@
// recvfrom()
if ((ret = ::recvfrom(fd, buf, size, 0, (sockaddr *) &addr, &addr_len)) < 0 && errno != EAGAIN)
- throw NetworkSocketOSError(*this, "recvfrom");
+ throw NetworkSocketErrno(*this, "recvfrom");
- // modify src...
- src->set_sockaddr((sockaddr *) &addr, addr_len);
+ // update source address if recvfrom suceeded
+ if (ret > 0) {
+ // modify src...
+ src->set_sockaddr((sockaddr *) &addr, addr_len);
+ }
} else {
// recv
if ((ret = ::recv(fd, buf, size, 0)) < 0 && errno != EAGAIN)
- throw NetworkSocketOSError(*this, "recv");
+ throw NetworkSocketErrno(*this, "recv");
}
@@ -264,12 +296,23 @@
void NetworkSocket::close (void) {
// use closesocket
if (::closesocket(fd))
- throw NetworkSocketOSError(*this, "close");
+ throw NetworkSocketErrno(*this, "close");
- // invalidate fd
+ // reset
fd = -1;
+ reset();
}
+void NetworkSocket::register_poll (void) {
+ if (registered) return;
+
+ reactor->add_socket(this);
+ registered = true;
+}
+
+/*
+ * NetworkSocketError
+ */
std::string NetworkSocketError::build_str (const NetworkSocket &socket, const char *op, const char *err) {
std::stringstream ss;
@@ -279,8 +322,14 @@
}
NetworkSocketError::NetworkSocketError (const NetworkSocket &socket, const char *op, const char *err) :
- Error(build_str(socket, op, err)) {
-
+ Error(build_str(socket, op, err))
+{
// nothing
}
+NetworkSocketErrno::NetworkSocketErrno (const NetworkSocket &socket, const char *op) :
+ NetworkSocketError(socket, op, strerror(errno))
+{
+
+}
+
--- a/src/Network/Socket.hh Tue Dec 16 20:30:35 2008 +0000
+++ b/src/Network/Socket.hh Tue Dec 16 23:21:26 2008 +0000
@@ -1,8 +1,12 @@
#ifndef NETWORK_SOCKET_HH
#define NETWORK_SOCKET_HH
+// forward-declare
+class NetworkSocket;
+
#include "../Error.hh"
#include "Address.hh"
+#include "Reactor.hh"
/*
* Platform-specific includes
@@ -19,30 +23,61 @@
#error "This network code won't compile on win32 :)"
#endif
-#include <cerrno>
-#include <cstring>
-
/**
* 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;
- /** Socket domain */
- int family;
-
- /** Socket type */
- int socktype;
-
- /** Socket protocol */
- int protocol;
+ /** 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;
+ 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
@@ -52,14 +87,14 @@
public:
/**
* Construct a socket of the specific type. Family and protocol can be left as NULL, but type should usually
- * be specified.
+ * be specified. The given reactor is used for polling, defaults to NetworkReactor::current
*/
- NetworkSocket (int family, int socktype, int protocol = 0);
+ NetworkSocket (int family, int socktype, int protocol = 0, NetworkReactor *reactor = NULL);
/**
* Create a socket from the given pre-existing fd
*/
- NetworkSocket (int fd);
+ NetworkSocket (int fd, socket_type type, NetworkReactor *reactor = NULL);
/**
* Force-close the socket if it's still open
@@ -70,12 +105,17 @@
// 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, ignoring errors
+ * Close and reset, ignoring errors
*/
void force_close (void);
@@ -138,7 +178,7 @@
size_t recv (char *buf, size_t size, NetworkAddress *src = NULL);
/**
- * Close the socket
+ * Close and reset the socket
*/
void close (void);
@@ -151,6 +191,36 @@
* 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();
+ }
};
/**
@@ -167,10 +237,9 @@
/**
* Errno-enabled exception, most common type of NetworkSocketError
*/
-class NetworkSocketOSError : public NetworkSocketError {
+class NetworkSocketErrno : public NetworkSocketError {
public:
- NetworkSocketOSError (const NetworkSocket &socket, const char *op) :
- NetworkSocketError(socket, op, strerror(errno)) { }
+ NetworkSocketErrno (const NetworkSocket &socket, const char *op);
};
/**
--- a/src/Network/TCP.cc Tue Dec 16 20:30:35 2008 +0000
+++ b/src/Network/TCP.cc Tue Dec 16 23:21:26 2008 +0000
@@ -11,6 +11,9 @@
// connect signals
slots.connect(socket->sig_read(), this, &NetworkTCPTransport::on_read);
slots.connect(socket->sig_write(), this, &NetworkTCPTransport::on_write);
+
+ // activate read polling, but leave write polling for later
+ socket->set_poll_read(true);
}
NetworkTCPTransport::~NetworkTCPTransport (void) {
@@ -43,20 +46,21 @@
// log and disconnect
Engine::log(ERROR, "tcp.on_read") << "socket error: " << e.what();
- _sig_disconnect();
+ handle_disconnect();
}
}
void NetworkTCPTransport::on_write (void) {
try {
- // just flush the output buffer
- out.flush_write();
+ // just flush the output buffer, and deactivate output polling if done
+ if (!out.flush_write())
+ socket->set_poll_write(false);
} catch (NetworkSocketError &e) {
// log and disconnect
Engine::log(ERROR, "tcp.on_write") << "socket error: " << e.what();
- _sig_disconnect();
+ handle_disconnect();
}
}
@@ -67,8 +71,9 @@
throw CL_Error("send prefix overflow");
try {
- // just write to the output buffer
- out.write_prefix((char *) packet.get_buf(), prefix);
+ // just write to the output buffer, but activate output polling if needed
+ if (out.write_prefix((char *) packet.get_buf(), prefix))
+ socket->set_poll_write(true);
} catch (NetworkSocketError &e) {
const char *err = e.what();
@@ -79,6 +84,15 @@
throw;
}
}
+
+void NetworkTCPTransport::handle_disconnect (void) {
+ // disable events on our socket
+ socket->set_poll_read(false);
+ socket->set_poll_write(false);
+
+ // signal
+ _sig_disconnect();
+}
/*
* NetworkTCPServer
@@ -97,9 +111,11 @@
// use nonblocking sockets
socket.set_nonblocking(true);
+
+ // activate polling
+ socket.set_poll_read(true);
}
-
void NetworkTCPServer::on_accept (void) {
// accept a new socket
NetworkSocket *client_sock = socket.accept(NULL);
--- a/src/Network/TCP.hh Tue Dec 16 20:30:35 2008 +0000
+++ b/src/Network/TCP.hh Tue Dec 16 23:21:26 2008 +0000
@@ -76,12 +76,12 @@
/**
* Get this TCP socket's local address
*/
- NetworkAddress getLocalAddress (void) { return socket->get_local_address(); }
+ NetworkAddress get_local_address (void) { return socket->get_local_address(); }
/**
* Get this TCP socket's remote address
*/
- NetworkAddress getRemoteAddress (void) { return socket->get_remote_address(); }
+ NetworkAddress get_remote_address (void) { return socket->get_remote_address(); }
/**
* Write the given packet to this socket output, buffering the data if need be
@@ -89,6 +89,11 @@
* @param packet the packet to send
*/
void write_packet (const NetworkPacketBuffer &packet);
+
+ /**
+ * Handle disconnect, disable socket
+ */
+ void handle_disconnect (void);
/**
* A full packet has been received from the remote end
--- a/src/Network/UDP.cc Tue Dec 16 20:30:35 2008 +0000
+++ b/src/Network/UDP.cc Tue Dec 16 23:21:26 2008 +0000
@@ -15,6 +15,9 @@
// nonblocking
socket.set_nonblocking(true);
+
+ // activate polling
+ socket.set_poll_read(true);
}
NetworkUDP::NetworkUDP (const NetworkAddress &bind_addr) :
@@ -28,6 +31,9 @@
// nonblocking
socket.set_nonblocking(true);
+
+ // activate polling
+ socket.set_poll_read(true);
}
void NetworkUDP::on_recv (void) {