/** * Similar to write(2) with the exception that this function will block until * <em>all</em> data has been written or an error occurs. * * @return @c size when succesful or @c SOCKET_ERROR if an error occurred. */ ssize_t writeAll(Socket *sock, const void *buf, size_t size, size_t *rawByteCount) { size_t ignored; size_t &rawBytes = rawByteCount != NULL ? *rawByteCount : ignored; rawBytes = 0; if (!sock || sock->fd[SOCK_CONNECTION] == INVALID_SOCKET) { debug(LOG_ERROR, "Invalid socket (EBADF)"); setSockErr(EBADF); return SOCKET_ERROR; } if (sock->writeError) { return SOCKET_ERROR; } if (size > 0) { if (!sock->isCompressed) { wzMutexLock(socketThreadMutex); if (socketThreadWrites.empty()) { wzSemaphorePost(socketThreadSemaphore); } std::vector<uint8_t> &writeQueue = socketThreadWrites[sock]; writeQueue.insert(writeQueue.end(), static_cast<char const *>(buf), static_cast<char const *>(buf) + size); wzMutexUnlock(socketThreadMutex); rawBytes = size; } else { sock->zDeflate.next_in = (Bytef *)buf; sock->zDeflate.avail_in = size; sock->zDeflateInSize += sock->zDeflate.avail_in; do { size_t alreadyHave = sock->zDeflateOutBuf.size(); sock->zDeflateOutBuf.resize(alreadyHave + size + 20); // A bit more than size should be enough to always do everything in one go. sock->zDeflate.next_out = (Bytef *)&sock->zDeflateOutBuf[alreadyHave]; sock->zDeflate.avail_out = sock->zDeflateOutBuf.size() - alreadyHave; int ret = deflate(&sock->zDeflate, Z_NO_FLUSH); ASSERT(ret != Z_STREAM_ERROR, "zlib compression failed!"); // Remove unused part of buffer. sock->zDeflateOutBuf.resize(sock->zDeflateOutBuf.size() - sock->zDeflate.avail_out); } while (sock->zDeflate.avail_out == 0); ASSERT(sock->zDeflate.avail_in == 0, "zlib didn't compress everything!"); } } return size; }
void socketFlush(Socket *sock, size_t *rawByteCount) { size_t ignored; size_t &rawBytes = rawByteCount != NULL ? *rawByteCount : ignored; rawBytes = 0; if (!sock->isCompressed) { return; // Not compressed, so don't mess with zlib. } // Flush data out of zlib compression state. do { sock->zDeflate.next_in = (Bytef *)NULL; sock->zDeflate.avail_in = 0; size_t alreadyHave = sock->zDeflateOutBuf.size(); sock->zDeflateOutBuf.resize(alreadyHave + 1000); // 100 bytes would probably be enough to flush the rest in one go. sock->zDeflate.next_out = (Bytef *)&sock->zDeflateOutBuf[alreadyHave]; sock->zDeflate.avail_out = sock->zDeflateOutBuf.size() - alreadyHave; int ret = deflate(&sock->zDeflate, Z_PARTIAL_FLUSH); ASSERT(ret != Z_STREAM_ERROR, "zlib compression failed!"); // Remove unused part of buffer. sock->zDeflateOutBuf.resize(sock->zDeflateOutBuf.size() - sock->zDeflate.avail_out); } while (sock->zDeflate.avail_out == 0); if (sock->zDeflateOutBuf.empty()) { return; // No data to flush out. } wzMutexLock(socketThreadMutex); if (socketThreadWrites.empty()) { wzSemaphorePost(socketThreadSemaphore); } std::vector<uint8_t> &writeQueue = socketThreadWrites[sock]; writeQueue.insert(writeQueue.end(), sock->zDeflateOutBuf.begin(), sock->zDeflateOutBuf.end()); wzMutexUnlock(socketThreadMutex); // Primitive network logging, uncomment to use. //printf("Size %3u ->%3zu, buf =", sock->zDeflateInSize, sock->zDeflateOutBuf.size()); //for (unsigned n = 0; n < std::min<unsigned>(sock->zDeflateOutBuf.size(), 40); ++n) printf(" %02X", sock->zDeflateOutBuf[n]); //printf("\n"); // Data sent, don't send again. rawBytes = sock->zDeflateOutBuf.size(); sock->zDeflateInSize = 0; sock->zDeflateOutBuf.clear(); }
static int socketThreadFunction(void *) { wzMutexLock(socketThreadMutex); while (!socketThreadQuit) { #if defined(WZ_OS_UNIX) SOCKET maxfd = INT_MIN; #elif defined(WZ_OS_WIN) SOCKET maxfd = 0; #endif fd_set fds; FD_ZERO(&fds); for (SocketThreadWriteMap::iterator i = socketThreadWrites.begin(); i != socketThreadWrites.end(); ++i) { if (!i->second.empty()) { SOCKET fd = i->first->fd[SOCK_CONNECTION]; maxfd = std::max(maxfd, fd); ASSERT(!FD_ISSET(fd, &fds), "Duplicate file descriptor!"); // Shouldn't be possible, but blocking in send, after select says it won't block, shouldn't be possible either. FD_SET(fd, &fds); } } struct timeval tv = {0, 50 * 1000}; // Check if we can write to any sockets. wzMutexUnlock(socketThreadMutex); int ret = select(maxfd + 1, NULL, &fds, NULL, &tv); wzMutexLock(socketThreadMutex); // We can write to some sockets. (Ignore errors from select, we may have deleted the socket after unlocking the mutex, and before calling select.) if (ret > 0) { for (SocketThreadWriteMap::iterator i = socketThreadWrites.begin(); i != socketThreadWrites.end(); ) { SocketThreadWriteMap::iterator w = i; ++i; Socket *sock = w->first; std::vector<uint8_t> &writeQueue = w->second; ASSERT(!writeQueue.empty(), "writeQueue[sock] must not be empty."); if (!FD_ISSET(sock->fd[SOCK_CONNECTION], &fds)) { continue; // This socket is not ready for writing, or we don't have anything to write. } // Write data. // FIXME SOMEHOW AAARGH This send() call can't block, but unless the socket is not set to blocking (setting the socket to nonblocking had better work, or else), does anyway (at least sometimes, when someone quits). Not reproducible except in public releases. ssize_t ret = send(sock->fd[SOCK_CONNECTION], reinterpret_cast<char *>(&writeQueue[0]), writeQueue.size(), MSG_NOSIGNAL); if (ret != SOCKET_ERROR) { // Erase as much data as written. writeQueue.erase(writeQueue.begin(), writeQueue.begin() + ret); if (writeQueue.empty()) { socketThreadWrites.erase(w); // Nothing left to write, delete from pending list. if (sock->deleteLater) { socketCloseNow(sock); } } } else { switch (getSockErr()) { case EAGAIN: #if defined(EWOULDBLOCK) && EAGAIN != EWOULDBLOCK case EWOULDBLOCK: #endif if (!connectionIsOpen(sock)) { debug(LOG_NET, "Socket error"); sock->writeError = true; socketThreadWrites.erase(w); // Socket broken, don't try writing to it again. if (sock->deleteLater) { socketCloseNow(sock); } break; } case EINTR: break; #if defined(EPIPE) case EPIPE: debug(LOG_NET, "EPIPE generated"); // fall through #endif default: sock->writeError = true; socketThreadWrites.erase(w); // Socket broken, don't try writing to it again. if (sock->deleteLater) { socketCloseNow(sock); } break; } } } } if (socketThreadWrites.empty()) { // Nothing to do, expect to wait. wzMutexUnlock(socketThreadMutex); wzSemaphoreWait(socketThreadSemaphore); wzMutexLock(socketThreadMutex); } } wzMutexUnlock(socketThreadMutex); return 42; // Return value arbitrary and unused. }