break the network code. Too late to set up a branch for this now
authorterom
Mon, 15 Dec 2008 23:56:42 +0000
changeset 378 5589abf5e61b
parent 377 01d3c340b372
child 379 2a8e780844d2
break the network code. Too late to set up a branch for this now
src/Engine.cc
src/Network/Address.cc
src/Network/Address.hh
src/Network/Buffer.cc
src/Network/Buffer.hh
src/Network/Socket.cc
src/Network/Socket.hh
src/Network/TCP.cc
src/Network/TCP.hh
src/Network/UDP.cc
--- a/src/Engine.cc	Mon Dec 15 16:41:00 2008 +0000
+++ b/src/Engine.cc	Mon Dec 15 23:56:42 2008 +0000
@@ -22,7 +22,7 @@
 
 void Engine::setupNetworkClient (const std::string &connect_host, const std::string &connect_port) {
     // connect_to
-    CL_IPAddress connect_addr(connect_host, connect_port);
+    NetworkAddress connect_addr(connect_host, connect_port);
 
     // create the client
     net_client = new NetworkClient(*this, game_state, connect_addr);
--- a/src/Network/Address.cc	Mon Dec 15 16:41:00 2008 +0000
+++ b/src/Network/Address.cc	Mon Dec 15 23:56:42 2008 +0000
@@ -3,10 +3,104 @@
 
 #include <sstream>
 
+NetworkAddress::NetworkAddress (void) :
+    hostname(), service(), address_length(0)
+{
+
+}
+
+NetworkAddress::NetworkAddress (std::string service) :
+    hostname(), service(service), address_length(0)
+{
+
+}
+        
+NetworkAddress::NetworkAddress (std::string hostname, std::string service) :
+    hostname(hostname), service(service), address_length(0)
+{
+
+}
+        
+NetworkAddress::NetworkAddress (const sockaddr *addr, socklen_t len) :
+    hostname(hostname), service(service), address_length(0)
+{
+    // proxy to set_sockaddr
+    set_sockaddr(addr, len);
+}
+        
+addrinfo* NetworkAddress::get_addrinfo (int family, int socktype, int protocol, int flags) const {
+    addrinfo hints, *results;
+    int err;
+    
+    // initialize flags
+    hints.ai_flags = flags;
+    hints.ai_family = family;
+    hints.ai_socktype = socktype;
+    hints.ai_protocol = protocol;
+    hints.ai_addrlen = 0;
+    hints.ai_canonname = NULL;
+    hints.ai_next = NULL;
+
+    // hostname + service may be NULL
+    const char *hostname = this->hostname.empty() ? NULL : this->hostname.c_str();
+    const char *service = this->service.empty() ? NULL : this->service.c_str();
+
+    // do getaddrinfo()
+    if ((err = getaddrinfo(hostname, service, &hints, &results)))
+        throw NetworkAddressError(*this, "getaddrinfo", gai_strerror(err));
+
+    // done
+    return results;
+}
+        
+const sockaddr* NetworkAddress::get_sockaddr (socklen_t &len_ref) const {
+    // we can't figure this out ourselves...
+    if (address_length == 0)
+        throw Error("NetworkAddress::get_sockaddr called without a valid sockaddr set");
+    
+    // update len
+    len_ref = address_length;
+    
+    // and return a pointer to our internal address
+    return (const sockaddr*) &address;
+}
+
+void NetworkAddress::set_sockaddr (const sockaddr *addr, socklen_t len) {
+    char host_buf[NI_MAXHOST], serv_buf[NI_MAXSERV];
+    int err;
+
+    // invalid length?
+    if (len > sizeof(this->address))
+        throw NetworkAddressError(*this, "set_sockaddr", "invalid sockaddr length");
+
+    // do getnameinfo()
+    if ((err = getnameinfo(addr, len, host_buf, NI_MAXHOST, serv_buf, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV)))
+        throw NetworkAddressError(*this, "getnameinfo", gai_strerror(err));
+    
+    // copy over to address
+    memcpy(&this->address, addr, len);
+}
+
 std::ostream& operator<< (std::ostream &s, const NetworkAddress &addr) {
-    s << "[" << addr.get_address() << ":" << addr.get_port() << "]";
+    s << "[" << addr.get_hostname() << ":" << addr.get_service() << "]";
 
     return s;
 
 }
  
+std::string NetworkAddressError::build_str (const NetworkAddress &addr, const char *op, const char *msg) {
+    std::stringstream ss;
+
+    ss << op << ": " << addr << ": " << msg;
+
+    return ss.str();
+}
+
+NetworkAddressError::NetworkAddressError (const NetworkAddress &addr, const char *op, const char *msg) :
+    Error(build_str(addr, op, msg))
+{
+
+}
+
+
+
--- a/src/Network/Address.hh	Mon Dec 15 16:41:00 2008 +0000
+++ b/src/Network/Address.hh	Mon Dec 15 23:56:42 2008 +0000
@@ -1,16 +1,159 @@
 #ifndef NETWORK_ADDRESS_HH
 #define NETWORK_ADDRESS_HH
 
-#include <ClanLib/Network/Socket/ip_address.h>
+#include "../Error.hh"
+
+/*
+ * Platform-specific includes
+ */
+#ifndef WIN32
+    // linux
+    #include <sys/types.h>
+    #include <sys/socket.h>
+    #include <netdb.h>
+#else
+    #error "This network code won't compile on win32 :)"
+#endif
+
+#include <string>
+
+// assume...
+#if INET6_ADDRSTRLEN < INET_ADDRSTRLEN
+    #error INET6_ADDRSTRLEN is smaller than INET_ADDRSTRLEN
+#endif
+
+/**
+ * Length of a network address
+ */
+const socklen_t NETWORK_ADDRESS_LENGTH = INET6_ADDRSTRLEN;
 
 /**
  * We use ClanLib's IPAddress API, but with our own name
  */ 
-typedef CL_IPAddress NetworkAddress;
+class NetworkAddress {
+    private:
+        /**
+         * Our human-readable hostname
+         */
+        std::string hostname;
+
+        /**
+         * Our human-readable service
+         */
+        std::string service;
+
+        /**
+         * Our machine-readable address and its length
+         */
+        sockaddr_storage address;
+        socklen_t address_length;
+
+    public:
+        /**
+         * Construct an empty NetworkAddress
+         */
+        NetworkAddress (void);
+
+        /**
+         * Construct a NetworkAddress with a NULL hostname, and a specific service
+         */
+        NetworkAddress (std::string service);
+
+        /**
+         * Construct a NetworkAddress on a specific hostname and service
+         */
+        NetworkAddress (std::string hostname, std::string service);
+        
+        /**
+         * Construct a NetworkAddress from a machine-readable address of the given length
+         */
+        NetworkAddress (const sockaddr *addr, socklen_t len);
+   
+    public:    
+        /**
+         * Get a addrinfo* for this address using the given family/type/protocol/flags.
+         *
+         * Remember to free the returned pointer using freeaddrinfo() after use.
+         *
+         * @param family the socket family for hints.ai_family
+         * @param socktype the socket type for hints.ai_socktype
+         * @param protoocl the socket protocol for hints.ai_protocol
+         * @param flags the flags for hints.ai_flags
+         * @return linked list of addrinfo's
+         * @throw NetworkAddressError if resolving this address fails
+         */
+        addrinfo* get_addrinfo (int family, int socktype, int protocol = 0, int flags = 0) const;
+
+        /**
+         * Get a sockaddr* for this address. This is only valid for NetworkAddress's which have had set_sockaddr called
+         * on them.
+         *
+         * @param len_ref updated to the sockaddr length
+         * @return borrowed sockaddr pointer
+         */
+        const sockaddr* get_sockaddr (socklen_t &len_ref) const;
+
+        /**
+         * Set a sockaddr for this address
+         *
+         * @param addr the address to copy, of len bytes
+         * @param len the size of the sockaddr
+         */
+        void set_sockaddr (const sockaddr *addr, socklen_t len);
+
+        /**
+         * Get the human-readable hostname
+         */
+        std::string get_hostname (void) const { return hostname; }
+
+        /**
+         * Get the human-readable service name
+         */
+        std::string get_service (void) const { return service; }
+
+        /**
+         * Equal-to comparison operator. This requires that the machine-readable address be available.
+         */
+        bool operator== (const NetworkAddress &other) const {
+            return (address_length == other.address_length) && memcmp(&address, &other.address, address_length) == 0;
+        }
+
+        /**
+         * Not-qqual-to comparison operator. This requires that the machine-readable address be available.
+         */
+        bool operator!= (const NetworkAddress &other) const {
+            return (address_length != other.address_length) || memcmp(&address, &other.address, address_length) != 0;
+        }
+
+        /**
+         * Less-than comparison operator. Smaller addresses are always lesser.
+         */
+        bool operator< (const NetworkAddress &other) const {
+            return (address_length < other.address_length) || memcmp(&address, &other.address, other.address_length) < 0;
+        }
+
+        /**
+         * Greater-than comparison operator. Bigger addresses are always greater.
+         */
+        bool operator> (const NetworkAddress &other) const {
+            return (address_length > other.address_length) || memcmp(&address, &other.address, address_length) > 0;
+        }
+};
 
 /**
  * Formatted as  [<addr>:<port>]
  */
 std::ostream& operator<< (std::ostream &s, const NetworkAddress &addr);
 
+/**
+ *
+ */
+class NetworkAddressError : public Error {
+    protected:
+        static std::string build_str (const NetworkAddress &addr, const char *op, const char *msg);
+
+    public:
+        NetworkAddressError (const NetworkAddress &addr, const char *op, const char *msg);
+};
+
 #endif /* NETWORK_ADDRESS_HH */
--- a/src/Network/Buffer.cc	Mon Dec 15 16:41:00 2008 +0000
+++ b/src/Network/Buffer.cc	Mon Dec 15 23:56:42 2008 +0000
@@ -8,7 +8,7 @@
 /*
  * NetworkBufferBase
  */
-NetworkBufferBase::NetworkBufferBase (NetworkSocket &socket, size_t size_hint) :
+NetworkBufferBase::NetworkBufferBase (NetworkSocket *socket, size_t size_hint) :
     socket(socket), buf(0), size(0), offset(0) {
     
     // allocate initial buffer
@@ -59,15 +59,14 @@
 /*
  * NetworkBufferInput
  */
-NetworkBufferInput::NetworkBufferInput (NetworkSocket &socket, size_t size_hint) :
+NetworkBufferInput::NetworkBufferInput (NetworkSocket *socket, size_t size_hint) :
     NetworkBufferBase(socket, size_hint)
 {
 
 }
 
 bool NetworkBufferInput::try_read (size_t item_size) {
-    int ret;
-    size_t to_read = item_size;
+    size_t ret, to_read = item_size;
 
     // keept reads at at least NETWORK_BUFFER_CHUNK_SIZE bytes
     if (to_read < NETWORK_BUFFER_CHUNK_SIZE)
@@ -77,22 +76,11 @@
     resize(to_read);
 
     // read once
-    try {
-        ret = socket.recv(buf + offset, to_read);
-
-    } catch (CL_Error &e) {
-        if (errno == EAGAIN)
-            return false;
+    ret = socket->recv(buf + offset, to_read);
 
-        else
-            throw NetworkSocketOSError(socket, "recv");
-    }
-    
-    // handle EOF
+    // nothing left?
     if (ret == 0)
-        throw NetworkSocketEOFError(socket, "recv");
-
-    assert(ret >= 0);
+        return false;
 
     // update offset
     offset += ret;
@@ -129,7 +117,7 @@
 /*
  * NetworkBufferOutput
  */
-NetworkBufferOutput::NetworkBufferOutput (NetworkSocket &socket, size_t size_hint) :
+NetworkBufferOutput::NetworkBufferOutput (NetworkSocket *socket, size_t size_hint) :
     NetworkBufferBase(socket, size_hint)
 {
 
@@ -137,22 +125,13 @@
 
 
 void NetworkBufferOutput::push_write (char *buf_ptr, size_t buf_size) {
-    int ret;
+    size_t ret;
 
     // try and short-circuit writes unless we have already buffered data
     if (offset == 0) {
-        try {
-            // attempt to send something
-            ret = socket.send(buf_ptr, buf_size);
+        // attempt to send something
+        ret = socket->send(buf_ptr, buf_size);
 
-        } catch (CL_Error &e) {
-            // ignore EAGAIN, detect this by setting ret to -1
-            if (errno != EAGAIN)
-                throw NetworkSocketOSError(socket, "send");
-
-            ret = -1;
-        }
-        
         // if we managed to send something, adjust buf/size and buffer
         if (ret > 0) {
             // sanity-check
@@ -175,24 +154,18 @@
 }
         
 void NetworkBufferOutput::flush_write (void) {
-    int ret;
+    size_t ret;
 
     // ignore if we don't have any data buffered
     if (offset == 0)
         return;
     
     // attempt to write as much as possible
-    try {
-        ret = socket.send(buf, offset);
+    ret = socket->send(buf, offset);
 
-    } catch (CL_Error &e) {
-        // ignore EAGAIN and just return
-        if (errno == EAGAIN)
-            return;
-
-        else
-            throw NetworkSocketOSError(socket, "send");
-    }
+    // busy?
+    if (ret == 0)
+        return;
 
     // trim the buffer
     trim(ret);
--- a/src/Network/Buffer.hh	Mon Dec 15 16:41:00 2008 +0000
+++ b/src/Network/Buffer.hh	Mon Dec 15 23:56:42 2008 +0000
@@ -25,7 +25,7 @@
 class NetworkBufferBase {
     protected:
         /** The socket that we use */
-        NetworkSocket socket;
+        NetworkSocket *socket;
         
         /** The buffer itself */
         char *buf;
@@ -37,7 +37,7 @@
         /**
          * Allocate buf using the given initial size, and set offset to zero
          */
-        NetworkBufferBase (NetworkSocket &socket, size_t size_hint);
+        NetworkBufferBase (NetworkSocket *socket, size_t size_hint);
 
         /**
          * Free()'s the buf
@@ -76,7 +76,7 @@
         /**
          * @see NetworkBufferBase
          */
-        NetworkBufferInput (NetworkSocket &socket, size_t size_hint);
+        NetworkBufferInput (NetworkSocket *socket, size_t size_hint);
 
     private:
         /**
@@ -187,7 +187,7 @@
         /**
          * @see NetworkBufferBase
          */
-        NetworkBufferOutput (NetworkSocket &socket, size_t size_hint);
+        NetworkBufferOutput (NetworkSocket *socket, size_t size_hint);
 
     private:
         /**
--- a/src/Network/Socket.cc	Mon Dec 15 16:41:00 2008 +0000
+++ b/src/Network/Socket.cc	Mon Dec 15 23:56:42 2008 +0000
@@ -1,8 +1,275 @@
 
 #include "Socket.hh"
+#include "../Engine.hh"
 
 #include <sstream>
 
+NetworkSocket::NetworkSocket (int family, int socktype, int protocol) :
+    fd(-1), family(family), socktype(socktype), protocol(protocol), bound(false)
+{
+
+}
+        
+NetworkSocket::NetworkSocket (int fd) :
+    fd(fd), family(0), socktype(0), protocol(0), bound(false)
+{
+    
+}
+        
+NetworkSocket::~NetworkSocket (void) {
+    // close any remaining socket
+    if (fd >= 0)
+        force_close();
+}
+        
+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
+    if (
+        (this->family && family != this->family) || 
+        (this->socktype && socktype != this->socktype) || 
+        (this->protocol && protocol != this->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");
+
+    // update our family/type/protocol
+    this->family = family;
+    this->socktype = socktype;
+    this->protocol = protocol;
+}
+        
+void NetworkSocket::force_close (void) {
+    // use closesocket
+    if (::closesocket(fd))
+        Engine::log(WARN, "socket.force_close") << "error closing socket: " /* XXX: errno */;
+    
+    // invalidate fd
+    fd = -1;
+}
+
+void NetworkSocket::bind (const NetworkAddress &addr) {
+    // get our addrinfo
+    addrinfo *r, *results = addr.get_addrinfo(family, socktype, protocol, AI_PASSIVE);
+    
+    // find the right address to bind to
+    for (r = results; r; r = r->ai_next) {
+        // create socket if needed, warn on errors
+        try {
+            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();
+            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 */ ;
+            
+            // close the bad socket
+            force_close();
+
+            continue;
+
+        } else {
+            // we have a bound socket, break
+            break;
+        }
+    }
+   
+    // release our addrinfo
+    freeaddrinfo(results);
+
+    // if we failed to bind, r is a NULL pointer
+    if (r == NULL)
+        throw NetworkSocketError(*this, "bind", "unable to bind on any addresses");
+
+    // mark ourselves as bound
+    bound = true;
+}
+        
+void NetworkSocket::listen (int backlog) {
+    // just call listen
+    if (::listen(fd, backlog))
+        throw NetworkSocketOSError(*this, "listen");
+}
+        
+NetworkAddress NetworkSocket::get_local_address (void) {
+    sockaddr_storage addr;
+    socklen_t addrlen = sizeof(addr);
+
+    // do getsockname()
+    if (::getsockname(fd, (sockaddr *) &addr, &addrlen))
+        throw NetworkSocketOSError(*this, "getsockname");
+
+    // return addr
+    return NetworkAddress((sockaddr *) &addr, addrlen);
+}
+
+NetworkAddress NetworkSocket::get_remote_address (void) {
+    sockaddr_storage addr;
+    socklen_t addrlen = sizeof(addr);
+
+    // do getpeername()
+    if (::getpeername(fd, (sockaddr *) &addr, &addrlen))
+        throw NetworkSocketOSError(*this, "getpeername");
+
+    // return addr
+    return NetworkAddress((sockaddr *) &addr, addrlen);
+}
+        
+void NetworkSocket::set_nonblocking (bool nonblocking) {
+    // XXX: linux-specific
+    if (fcntl(fd, F_SETFL, O_NONBLOCK, nonblocking ? 1 : 0) == -1)
+        throw NetworkSocketOSError(*this, "fcntl(F_SETFL, O_NONBLOCK)");
+}
+ 
+NetworkSocket* NetworkSocket::accept (NetworkAddress *src) {
+    int new_fd;
+    sockaddr_storage addr;
+    socklen_t addrlen = sizeof(addr);
+
+    // try and get the FD
+    if ((new_fd = ::accept(fd, (sockaddr *) &addr, &addrlen)))
+        throw NetworkSocketOSError(*this, "accept");
+    
+    // allocate new NetworkSocket for new_fd
+    NetworkSocket *socket = new NetworkSocket(new_fd);
+    
+    // update src
+    if (src)
+        src->set_sockaddr((sockaddr *) &addr, addrlen);    
+
+    // done
+    return socket;
+}
+        
+void NetworkSocket::connect (const NetworkAddress &addr) {
+    // get our addrinfo
+    addrinfo *r, *results = addr.get_addrinfo(family, socktype, protocol);
+    
+    // find the right address to bind to
+    for (r = results; r; r = r->ai_next) {
+        // create socket if needed, warn on errors
+        try {
+            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();
+            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 */ ;
+            
+            // close unless bound, to not keep invalid sockets hanging around
+            if (!bound)
+                force_close();
+
+            continue;
+
+        } else {
+            // we have a connected socket, break
+            break;
+        }
+    }
+   
+    // release our addrinfo
+    freeaddrinfo(results);
+
+    // if we failed to connect, r is a NULL pointer
+    if (r == NULL)
+        throw NetworkSocketError(*this, "connect", "unable to connect to any addresses");
+}
+        
+size_t NetworkSocket::send (const char *buf, size_t size, const NetworkAddress *dest) {
+    ssize_t ret;
+    
+    // use send or sendto?
+    if (dest) {
+        const sockaddr *addr;
+        socklen_t addr_len;
+        
+        // get destination address
+        addr = dest->get_sockaddr(addr_len);
+        
+        // sendto()
+        if ((ret = ::sendto(fd, buf, size, 0, addr, addr_len)) < 0 && errno != EAGAIN)
+            throw NetworkSocketOSError(*this, "sendto");
+
+    } else {
+        // send()
+        if ((ret = ::send(fd, buf, size, 0)) < 0 && errno != EAGAIN)
+            throw NetworkSocketOSError(*this, "send");
+
+    }
+    
+    // sanity-check
+    if (ret == 0) {
+        // XXX: not sure what this means...
+        Engine::log(ERROR, "socket.send") << "send[to] returned zero, trying again...";
+        return 0;
+    }
+    
+    // EAGAIN?
+    if (ret < 0)
+        return 0;
+    
+    // return number of bytes sent
+    return ret;
+}
+
+size_t NetworkSocket::recv (char *buf, size_t size, NetworkAddress *src) {
+    ssize_t ret;
+
+    // use recv or recvfrom?
+    if (src) {
+        sockaddr_storage addr;
+        socklen_t addr_len = sizeof(addr);
+
+        // recvfrom()
+        if ((ret = ::recvfrom(fd, buf, size, 0, (sockaddr *) &addr, &addr_len)) < 0 && errno != EAGAIN)
+            throw NetworkSocketOSError(*this, "recvfrom");
+
+        // 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");
+
+    }
+
+    // EOF?
+    if (ret == 0)
+        throw NetworkSocketEOFError(*this, "recv");
+
+    // EAGAIN?
+    if (ret < 0)
+        return 0;
+    
+    // return number of bytes received
+    return ret;
+}
+        
+void NetworkSocket::close (void) {
+    // use closesocket
+    if (::closesocket(fd))
+        throw NetworkSocketOSError(*this, "close");
+    
+    // invalidate fd
+    fd = -1;
+}
+
 std::string NetworkSocketError::build_str (const NetworkSocket &socket, const char *op, const char *err) {
     std::stringstream ss;
 
--- a/src/Network/Socket.hh	Mon Dec 15 16:41:00 2008 +0000
+++ b/src/Network/Socket.hh	Mon Dec 15 23:56:42 2008 +0000
@@ -2,23 +2,165 @@
 #define NETWORK_SOCKET_HH
 
 #include "../Error.hh"
+#include "Address.hh"
 
-#include <ClanLib/Network/Socket/socket.h>
+/*
+ * 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
+
 #include <cerrno>
 #include <cstring>
 
 /**
- * We use ClanLib's Socket API, but with our own name
+ * We use ClanLib's Socket API, but with our own extensions...
  */
-typedef CL_Socket NetworkSocket;
+class NetworkSocket {
+    private:
+        /** The file descriptor */
+        int fd;
+
+        /** Socket domain */
+        int family;
+
+        /** Socket type */
+        int socktype;
+
+        /** Socket protocol */
+        int protocol;
+
+        /** 
+         * Has the socket been explicitly bind()'d? If so, force ourselves to use this socket in connect().
+         */
+        bool bound;
+
+        /**
+         * 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.
+         */
+        NetworkSocket (int family, int socktype, int protocol = 0);
+        
+        /**
+         * Create a socket from the given pre-existing fd
+         */
+        NetworkSocket (int fd);
+
+        /**
+         * Force-close the socket if it's still open
+         */
+        ~NetworkSocket (void);
+
+    private:
+        // XXX: nocopy
+        
+        /**
+         * 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
+         */
+        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 NetworkAddress &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 NetworkAddress &addr);
+
+        /**
+         * Send, optionally using the specific destination
+         *
+         * @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
+         *
+         * @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 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; }
+};
 
 /**
  * Base class for expcetions thrown by socket methods
  */
 class NetworkSocketError : public Error {
     protected:
-        std::string build_str (const NetworkSocket &socket, const char *op, const char *err);
-
+        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);
 };
 
--- a/src/Network/TCP.cc	Mon Dec 15 16:41:00 2008 +0000
+++ b/src/Network/TCP.cc	Mon Dec 15 23:56:42 2008 +0000
@@ -5,15 +5,18 @@
 /*
  * NetworkTCPTransport
  */
-NetworkTCPTransport::NetworkTCPTransport (NetworkSocket socket) :
+NetworkTCPTransport::NetworkTCPTransport (NetworkSocket *socket) :
     socket(socket), in(socket, NETWORK_TCP_INITIAL_IN_BUF), out(socket, NETWORK_TCP_INITIAL_OUT_BUF) {
     
     // connect signals
-    slots.connect(socket.sig_read_triggered(), this, &NetworkTCPTransport::on_read);
-    slots.connect(socket.sig_write_triggered(), this, &NetworkTCPTransport::on_write);
-    slots.connect(socket.sig_disconnected(), this, &NetworkTCPTransport::on_disconnected);
+    slots.connect(socket->sig_read(), this, &NetworkTCPTransport::on_read);
+    slots.connect(socket->sig_write(), this, &NetworkTCPTransport::on_write);
 }
-
+        
+NetworkTCPTransport::~NetworkTCPTransport (void) {
+    // release socket
+    delete socket;
+}
 
 void NetworkTCPTransport::on_read (void) {
     uint32_t length;
@@ -56,11 +59,6 @@
         _sig_disconnect();
     }
 }
-
-void NetworkTCPTransport::on_disconnected (void) {
-    // pass right through
-    _sig_disconnect();
-}
         
 void NetworkTCPTransport::write_packet (const NetworkPacketBuffer &packet) {
     uint32_t prefix = packet.get_data_size();
@@ -82,17 +80,17 @@
     }
 }
 
+/*
+ * NetworkTCPServer
+ */
 NetworkTCPServer::NetworkTCPServer (const NetworkAddress &listen_addr) :
-    socket(CL_Socket::tcp, CL_Socket::ipv4) {
-    
-    // wow... I didn't know ClanLib was *this* crap
-    socket.proto = CL_Socket::tcp;
+    socket(AF_UNSPEC, SOCK_STREAM) {
     
     // bind
     socket.bind(listen_addr);
 
     // assign slots
-    slots.connect(socket.sig_read_triggered(), this, &NetworkTCPServer::on_accept);
+    slots.connect(socket.sig_read(), this, &NetworkTCPServer::on_accept);
 
     // listen
     socket.listen(NETWORK_LISTEN_BACKLOG);
@@ -104,7 +102,7 @@
 
 void NetworkTCPServer::on_accept (void) {
     // accept a new socket
-    NetworkSocket client_sock = socket.accept();
+    NetworkSocket *client_sock = socket.accept(NULL);
 
     // create a new NetworkTCPTransport
     NetworkTCPTransport *client = buildTransport(client_sock);
@@ -113,19 +111,19 @@
     _sig_client(client);
 }
         
-NetworkTCPTransport* NetworkTCPServer::buildTransport (CL_Socket &socket) {
+NetworkTCPTransport* NetworkTCPServer::buildTransport (NetworkSocket *socket) {
     return new NetworkTCPTransport(socket);
 }
         
+/*
+ * NetworkTCPClient
+ */
 NetworkTCPClient::NetworkTCPClient (const NetworkAddress &connect_addr) :
-    NetworkTCPTransport(NetworkSocket(CL_Socket::tcp, CL_Socket::ipv4)) {
+    NetworkTCPTransport(new NetworkSocket(AF_UNSPEC, SOCK_STREAM)) {
     
-    // wow... I didn't know ClanLib was *this* crap
-    socket.proto = CL_Socket::tcp;
-
     // connect
-    socket.connect(connect_addr);
+    socket->connect(connect_addr);
     
     // use nonblocking sockets
-    socket.set_nonblocking(true);
+    socket->set_nonblocking(true);
 }
--- a/src/Network/TCP.hh	Mon Dec 15 16:41:00 2008 +0000
+++ b/src/Network/TCP.hh	Mon Dec 15 23:56:42 2008 +0000
@@ -26,7 +26,7 @@
         /**
          * The SOCK_STREAM socket
          */
-        NetworkSocket socket;
+        NetworkSocket *socket;
         
         /**
          * Our input buffer associated with socket
@@ -44,7 +44,12 @@
         /**
          * Construct this using the given socket
          */
-        NetworkTCPTransport (NetworkSocket socket);
+        NetworkTCPTransport (NetworkSocket *socket);
+        
+        /**
+         * Deletes the socket
+         */
+        ~NetworkTCPTransport (void);
 
     private:
         /**
@@ -56,11 +61,6 @@
          * Triggered when the socket is read for send
          */
         void on_write (void);
-
-        /**
-         * Triggered when the socket is disconnected (?)
-         */
-        void on_disconnected (void);
         
         /**
          * The packet-read signal
@@ -74,14 +74,14 @@
 
     public:
         /**
-         * Get this TCP socket's local ddress
+         * Get this TCP socket's local address
          */
-        NetworkAddress getLocalAddress (void) { return socket.get_source_address(); }
+        NetworkAddress getLocalAddress (void) { return socket->get_local_address(); }
 
         /**
          * Get this TCP socket's remote address
          */
-        NetworkAddress getRemoteAddress (void) { return socket.get_dest_address(); }
+        NetworkAddress getRemoteAddress (void) { return socket->get_remote_address(); }
         
         /**
          * Write the given packet to this socket output, buffering the data if need be
@@ -138,7 +138,7 @@
          *
          * @param socket the socket returned by accept()
          */
-        virtual NetworkTCPTransport* buildTransport (CL_Socket &socket);
+        virtual NetworkTCPTransport* buildTransport (NetworkSocket *socket);
 
     public:
         /**
--- a/src/Network/UDP.cc	Mon Dec 15 16:41:00 2008 +0000
+++ b/src/Network/UDP.cc	Mon Dec 15 23:56:42 2008 +0000
@@ -1,52 +1,49 @@
 
 #include "UDP.hh"
+#include "../Engine.hh"
 
 #include <ClanLib/core.h>
 #include <cassert>
 
 NetworkUDP::NetworkUDP (void) : 
-    socket(CL_Socket::udp, CL_Socket::ipv4) {
-    
+    socket(AF_UNSPEC, SOCK_DGRAM) 
+{
     // do not bind
 
     // connect signal
-    slots.connect(socket.sig_read_triggered(), this, &NetworkUDP::on_recv);
+    slots.connect(socket.sig_read(), this, &NetworkUDP::on_recv);
 
     // nonblocking
     socket.set_nonblocking(true);
 }
 
 NetworkUDP::NetworkUDP (const NetworkAddress &bind_addr) :
-    socket(CL_Socket::udp, CL_Socket::ipv4) {
+    socket(AF_UNSPEC, SOCK_DGRAM) {
     
     // bind socket
     socket.bind(bind_addr);
 
     // connect signal
-    slots.connect(socket.sig_read_triggered(), this, &NetworkUDP::on_recv);
+    slots.connect(socket.sig_read(), this, &NetworkUDP::on_recv);
 
     // nonblocking
     socket.set_nonblocking(true);
 }
         
 void NetworkUDP::on_recv (void) {
-    int ret;
+    size_t ret;
     NetworkPacket pkt;
     NetworkAddress src;
     
     // receieve as many packets as possible
     do {    
         // attempt to recv a packet
-        try {
-            ret = socket.recv(pkt.get_buf(), pkt.get_buf_size(), src);
+        ret = socket.recv(pkt.get_buf(), pkt.get_buf_size(), &src);
+        
+        // no more packets?
+        if (ret == 0)
+            return;
 
-        } catch (CL_Error &e) {
-            if (errno == EAGAIN)
-                return;
-            else
-                throw;
-        }
-        
         // set packet data size
         pkt.set_data_size(ret);
 
@@ -57,23 +54,26 @@
 }
         
 bool NetworkUDP::sendto (const NetworkPacketBuffer &packet, const NetworkAddress &dst) {
-    int ret;
+    size_t ret;
 
-    // XXX: shouldn't get trimmed
     try {
-        ret = socket.send(packet.get_buf(), packet.get_data_size(), dst);
+        // try and send
+        ret = socket.send(packet.get_buf(), packet.get_data_size(), &dst);
 
-    } catch (CL_Error &e) {
-        // XXX: catch some errors, but not others?
+    } catch (NetworkSocketError &e) {
+        // catch and log errors
+        Engine::log(WARN, "udp.sendto") << "socket->send raised error: " << e.what();
         return false;
     }
-
-    assert(ret > 0);
     
-    // UDP shouldn't trim packets
-    assert((unsigned int) ret == packet.get_data_size());
-    
-    // good
-    return true;
+    // weird packet size?
+    if (ret != packet.get_data_size()) {
+        Engine::log(ERROR, "udp.sendto") << "socket->send returned weird length: " << ret << ", packet was " << packet.get_data_size();
+        return false;
+
+    } else {
+        // sent
+        return true;
+    }
 }