break the network code. Too late to set up a branch for this now
--- 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;
+ }
}