# HG changeset patch # User terom # Date 1229469686 0 # Node ID d193dd1d8a7eb555c847ccd8b3fd7e2c361e2e7a # Parent 2a8e780844d2c60c877b5ef3d97af87cdb77d646 new NetworkReactor, fix everything to actually work diff -r 2a8e780844d2 -r d193dd1d8a7e src/Config.hh --- 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; diff -r 2a8e780844d2 -r d193dd1d8a7e src/Engine.cc --- 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 @@ -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); } } diff -r 2a8e780844d2 -r d193dd1d8a7e src/Network/Address.cc --- 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; diff -r 2a8e780844d2 -r d193dd1d8a7e src/Network/Address.hh --- 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: /** diff -r 2a8e780844d2 -r d193dd1d8a7e src/Network/Buffer.cc --- 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); } - diff -r 2a8e780844d2 -r d193dd1d8a7e src/Network/Buffer.hh --- 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); // @} }; diff -r 2a8e780844d2 -r d193dd1d8a7e src/Network/Error.cc --- /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 +#include + +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) +{ + +} + diff -r 2a8e780844d2 -r d193dd1d8a7e src/Network/Error.hh --- /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 + * + * : + */ +class NetworkErrno : public Error { + protected: + static std::string build_str (std::string op, int err); + + public: + int err; + + NetworkErrno (std::string op); +}; + +#endif diff -r 2a8e780844d2 -r d193dd1d8a7e src/Network/Reactor.cc --- /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::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 socket-events + for (std::list::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; + diff -r 2a8e780844d2 -r d193dd1d8a7e src/Network/Reactor.hh --- /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 +#else + #error "This network code won't compile on win32 :)" +#endif + +#include + +/** + * A Reactor manages a set of NetworkSockets, providing readyness notification and a poll method + */ +class NetworkReactor { + protected: + std::list 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 diff -r 2a8e780844d2 -r d193dd1d8a7e src/Network/Session.cc --- 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); diff -r 2a8e780844d2 -r d193dd1d8a7e src/Network/Socket.cc --- 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 +#include #include -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)) +{ + +} + diff -r 2a8e780844d2 -r d193dd1d8a7e src/Network/Socket.hh --- 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 -#include - /** * 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); }; /** diff -r 2a8e780844d2 -r d193dd1d8a7e src/Network/TCP.cc --- 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); diff -r 2a8e780844d2 -r d193dd1d8a7e src/Network/TCP.hh --- 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 diff -r 2a8e780844d2 -r d193dd1d8a7e src/Network/UDP.cc --- 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) {