// Helper method to send a number of bytes over fd. Returns the // number of bytes that failed to send. int stream_handler::send_data(const char * buf, size_t length) { int bytes_written = write(fd, buf, length); if(bytes_written == -1) { if(errno == EAGAIN) { tracer.trace5( "EAGAIN when trying to write %zu bytes to fd%d", length, fd); // The file descriptor is backed up! Try again when it // becomes writeable in the future. watch_writable(fd, true); return length; } if(errno == ECONNRESET) { // Connection closed by the other side while we were // busy getting through our buf_. tracer.trace0( "Connection closed when trying to write %zu bytes to fd%d", length, fd); on_connection_closed(); watch_stream(fd, false); return 0; } tracer.trace0( "Unexpected error code from send(): %d", errno); assert("send unexpectedly failed"); } else if((size_t) bytes_written < length) { watch_writable(fd, true); } else { // The fd is sending all data, no need to get alerts on when // it is writable. watch_writable(fd, false); } tracer.trace8("Sent %d of %zu bytes", bytes_written, length); return length - bytes_written; }
void SessionHandler::WatchStream(int socketDescriptor, bool doWatch) { assert((m_fd != 0 && m_fd != socketDescriptor) == false); // only one stream for a socket if (doWatch) { m_tracer.trace0("watching socket %d", socketDescriptor); m_fd = socketDescriptor; watch_readable(m_fd, true); m_readBuffer = new char[m_bufferSize]; m_writeBuffer = new char[m_bufferSize]; m_bytesRead = 0; m_bytesToWrite = 0; OnConnect(); } else if (m_fd != 0) { m_tracer.trace0("cleaning up"); watch_readable(m_fd, false); watch_writable(m_fd, false); delete [] m_readBuffer; delete [] m_writeBuffer; m_readBuffer = nullptr; m_writeBuffer = nullptr; m_fd = 0; m_nextMessageLength = 0; } }
void stream_handler::watch_stream(int file_descriptor, bool should_watch) { if(fd && fd != file_descriptor) { assert(!"Only allowed to handle one stream at a time"); } if(!should_watch) { if(fd) { tracer.trace0("Cleaning up"); watch_readable(fd, false); watch_writable(fd, false); delete[] read_buf; delete[] write_buf; read_buf = NULL; write_buf = NULL; fd = 0; next_message_length = 0; delimiter = NULL; delimiter_length = 0; } } else { tracer.trace0("Watching stream %d", file_descriptor); fd = file_descriptor; watch_readable(fd, true); read_buf = new char[buf_size]; write_buf = new char[buf_size]; bytes_read = 0; bytes_to_write = 0; } }
int SessionHandler::SendData(const char* buffer, size_t length) { auto bytesWritten = write(m_fd, buffer, length); if (bytesWritten == -1) { switch (errno) { case EAGAIN: m_tracer.trace5("EAGAIN when trying %d bytes to socket %d", length, m_fd); watch_writable(m_fd, true); return length; case ECONNRESET: m_tracer.trace0("connection closed while trying to write %d bytes to %d", length, m_fd); OnConnectionClosed(); WatchStream(m_fd, false); return 0; default: m_tracer.trace0("unexpected error code from send(): %d", errno); break; } } else if ((size_t)bytesWritten < length) { watch_writable(m_fd, true); } else { watch_writable(m_fd, false); } return length - bytesWritten; }
void stream_handler::on_writable(int notifying_fd) { if(notifying_fd != fd) { return; } if(!bytes_to_write) { // If we somehow get called but have nothing to write, // unregister our interest in this notification. watch_writable(fd, false); return; } tracer.trace7("Buffer is writable, attempting to send %zu bytes", bytes_to_write); int bytes_remaining = send_data(write_buf, bytes_to_write); if(bytes_remaining) { memmove(write_buf, write_buf + bytes_to_write - bytes_remaining, bytes_remaining); } bytes_to_write = bytes_remaining; }
void SessionHandler::on_writable(int notifyingSocket) { if (notifyingSocket != m_fd) { return; } if (m_bytesToWrite == 0) { watch_writable(m_fd, false); return; } m_tracer.trace7("Buffer is writable, attempting to send %d bytes", m_bytesToWrite); auto bytesRemaining = SendData(m_writeBuffer, m_bytesToWrite); if(bytesRemaining) { memmove(m_writeBuffer, m_writeBuffer + m_bytesToWrite - bytesRemaining, bytesRemaining); } m_bytesToWrite = bytesRemaining; }