new NetworkReactor, fix everything to actually work
authorterom
Tue, 16 Dec 2008 23:21:26 +0000
changeset 380 d193dd1d8a7e
parent 379 2a8e780844d2
child 381 9b35bc329d23
new NetworkReactor, fix everything to actually work
src/Config.hh
src/Engine.cc
src/Network/Address.cc
src/Network/Address.hh
src/Network/Buffer.cc
src/Network/Buffer.hh
src/Network/Error.cc
src/Network/Error.hh
src/Network/Reactor.cc
src/Network/Reactor.hh
src/Network/Session.cc
src/Network/Socket.cc
src/Network/Socket.hh
src/Network/TCP.cc
src/Network/TCP.hh
src/Network/UDP.cc
--- 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) {