void Gosu::MessageSocket::update() { std::vector<char> buffer(maxMessageSize()); sockaddr_in addr; socklen_t size = sizeof addr; for (;;) { int received = ::recvfrom(pimpl->socket.handle(), &buffer.front(), buffer.size(), 0, reinterpret_cast<sockaddr*>(&addr), &size); if (received != SOCKET_ERROR && onReceive) { onReceive(ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port), &buffer.front(), received); } else switch (lastSocketError()) { // Ignore some of the errors. case GOSU_SOCK_ERR(EWOULDBLOCK): case GOSU_SOCK_ERR(ENETDOWN): case GOSU_SOCK_ERR(ENETRESET): case GOSU_SOCK_ERR(ETIMEDOUT): case GOSU_SOCK_ERR(ECONNRESET): return; // Everything else is unexpected. default: throwLastSocketError(); } } }
void Gosu::CommSocket::update() { sendPendingData(); if (!connected()) return; for (;;) { char buffer[1024]; int received = ::recv(pimpl->socket.handle(), buffer, sizeof buffer, 0); if (received > 0 && received <= static_cast<int>(sizeof buffer)) { // Data arrived and fit into the buffer. pimpl->appendBuffer(buffer, received, onReceive); } else if (received == 0) { // The other side has gracefully closed the connection. disconnect(); return; } else if (received == SOCKET_ERROR) { switch (lastSocketError()) { // Arriving data didn't fit into the buffer. case GOSU_SOCK_ERR(EMSGSIZE): pimpl->appendBuffer(buffer, sizeof buffer, onReceive); break; // There simply was no data. case GOSU_SOCK_ERR(EWOULDBLOCK): return; // Connection was reset or is invalid. case GOSU_SOCK_ERR(ENETDOWN): case GOSU_SOCK_ERR(ENOTCONN): case GOSU_SOCK_ERR(ENETRESET): case GOSU_SOCK_ERR(ECONNABORTED): case GOSU_SOCK_ERR(ETIMEDOUT): case GOSU_SOCK_ERR(ECONNRESET): #ifndef GOSU_IS_WIN // UNIX specific, rare error case GOSU_SOCK_ERR(EPIPE): #endif disconnect(); return; // Everything else is unexpected. default: throwLastSocketError(); } } else assert(false); } }
void Gosu::CommSocket::sendPendingData() { if (pendingBytes() == 0 || !connected()) return; int sent = ::send(pimpl->socket.handle(), &pimpl->outbox.front(), pendingBytes(), 0); if (sent >= 0) { // Remove sent data from the outbox. if (sent >= static_cast<int>(pendingBytes())) pimpl->outbox.clear(); else pimpl->outbox.erase(pimpl->outbox.begin(), pimpl->outbox.begin() + sent); } else { switch (lastSocketError()) { // These error codes basically mean "try again later". case GOSU_SOCK_ERR(ENOBUFS): case GOSU_SOCK_ERR(EWOULDBLOCK): case GOSU_SOCK_ERR(EHOSTUNREACH): break; // And these tell us we're disconnected. case GOSU_SOCK_ERR(ENETDOWN): case GOSU_SOCK_ERR(ENETRESET): case GOSU_SOCK_ERR(ENOTCONN): case GOSU_SOCK_ERR(ECONNABORTED): case GOSU_SOCK_ERR(ECONNRESET): case GOSU_SOCK_ERR(ETIMEDOUT): #ifndef GOSU_IS_WIN // UNIX-specific, rare error case GOSU_SOCK_ERR(EPIPE): #endif disconnect(); break; // Everything else is unexpected. default: throwLastSocketError(); } } }
T socketCheck(T retVal) { if (retVal == SOCKET_ERROR && lastSocketError() != GOSU_SOCK_ERR(EWOULDBLOCK)) { throwLastSocketError(); } return retVal; }
void Gosu::MessageSocket::send(SocketAddress address, SocketPort port, const void* buffer, std::size_t size) { sockaddr_in addr; std::memset(&addr, 0, sizeof addr); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(address); addr.sin_port = htons(port); int sent = ::sendto(pimpl->socket.handle(), reinterpret_cast<const char*>(buffer), size, 0, reinterpret_cast<sockaddr*>(&addr), sizeof addr); if (sent == static_cast<int>(size)) return; // Yay, did it! assert(sent == SOCKET_ERROR); // Don't expect partial sends. switch (lastSocketError()) { // Just ignore a lot of errors... this is UDP, right? case GOSU_SOCK_ERR(ENETDOWN): case GOSU_SOCK_ERR(ENETRESET): case GOSU_SOCK_ERR(ENOBUFS): case GOSU_SOCK_ERR(EWOULDBLOCK): case GOSU_SOCK_ERR(EHOSTUNREACH): case GOSU_SOCK_ERR(ECONNABORTED): case GOSU_SOCK_ERR(ECONNRESET): case GOSU_SOCK_ERR(ENETUNREACH): case GOSU_SOCK_ERR(ETIMEDOUT): break; // Everything else means more than just another lost packet, though. default: throwLastSocketError(); } }