void CTCPSocket::shutdownInput() { bool useNewJob = false; { CLock lock(&m_mutex); // shutdown socket for reading try { ARCH->closeSocketForRead(m_socket); } catch (XArchNetwork&) { // ignore } // shutdown buffer for reading if (m_readable) { sendEvent(getInputShutdownEvent()); onInputShutdown(); useNewJob = true; } } if (useNewJob) { setJob(newJob()); } }
void CPacketStreamFilter::filterEvent(const CEvent& event) { if (event.getType() == getInputReadyEvent()) { CLock lock(&m_mutex); if (!readMore()) { return; } } else if (event.getType() == getInputShutdownEvent()) { // discard this if we have buffered data CLock lock(&m_mutex); m_inputShutdown = true; if (m_size != 0) { return; } } // pass event CStreamFilter::filterEvent(event); }
UInt32 CPacketStreamFilter::read(void* buffer, UInt32 n) { if (n == 0) { return 0; } CLock lock(&m_mutex); // if not enough data yet then give up if (!isReadyNoLock()) { return 0; } // read no more than what's left in the buffered packet if (n > m_size) { n = m_size; } // read it if (buffer != NULL) { memcpy(buffer, m_buffer.peek(n), n); } m_buffer.pop(n); m_size -= n; // get next packet's size if we've finished with this packet and // there's enough data to do so. readPacketSize(); if (m_inputShutdown && m_size == 0) { EVENTQUEUE->addEvent(CEvent(getInputShutdownEvent(), getEventTarget(), NULL)); } return n; }
ISocketMultiplexerJob* CTCPSocket::serviceConnected(ISocketMultiplexerJob* job, bool read, bool write, bool error) { CLock lock(&m_mutex); if (error) { sendEvent(getDisconnectedEvent()); onDisconnected(); return newJob(); } bool needNewJob = false; if (write) { try { // write data UInt32 n = m_outputBuffer.getSize(); const void* buffer = m_outputBuffer.peek(n); n = (UInt32)ARCH->writeSocket(m_socket, buffer, n); // discard written data if (n > 0) { m_outputBuffer.pop(n); if (m_outputBuffer.getSize() == 0) { sendEvent(getOutputFlushedEvent()); m_flushed = true; m_flushed.broadcast(); needNewJob = true; } } } catch (XArchNetworkShutdown&) { // remote read end of stream hungup. our output side // has therefore shutdown. onOutputShutdown(); sendEvent(getOutputShutdownEvent()); if (!m_readable && m_inputBuffer.getSize() == 0) { sendEvent(getDisconnectedEvent()); m_connected = false; } needNewJob = true; } catch (XArchNetworkDisconnected&) { // stream hungup onDisconnected(); sendEvent(getDisconnectedEvent()); needNewJob = true; } catch (XArchNetwork& e) { // other write error LOG((CLOG_WARN "error writing socket: %s", e.what().c_str())); onDisconnected(); sendEvent(getOutputErrorEvent()); sendEvent(getDisconnectedEvent()); needNewJob = true; } } if (read && m_readable) { try { UInt8 buffer[4096]; size_t n = ARCH->readSocket(m_socket, buffer, sizeof(buffer)); if (n > 0) { bool wasEmpty = (m_inputBuffer.getSize() == 0); // slurp up as much as possible do { m_inputBuffer.write(buffer, n); n = ARCH->readSocket(m_socket, buffer, sizeof(buffer)); } while (n > 0); // send input ready if input buffer was empty if (wasEmpty) { sendEvent(getInputReadyEvent()); } } else { // remote write end of stream hungup. our input side // has therefore shutdown but don't flush our buffer // since there's still data to be read. sendEvent(getInputShutdownEvent()); if (!m_writable && m_inputBuffer.getSize() == 0) { sendEvent(getDisconnectedEvent()); m_connected = false; } m_readable = false; needNewJob = true; } } catch (XArchNetworkDisconnected&) { // stream hungup sendEvent(getDisconnectedEvent()); onDisconnected(); needNewJob = true; } catch (XArchNetwork& e) { // ignore other read error LOG((CLOG_WARN "error reading socket: %s", e.what().c_str())); } } return needNewJob ? newJob() : job; }