public void OutEvent() { // If write buffer is empty, try to read new data from the encoder. if (m_outsize == 0) { m_outpos = null; m_encoder.GetData(ref m_outpos, ref m_outsize); // If there is no data to send, stop polling for output. if (m_outsize == 0) { m_ioObject.ResetPollout(m_handle); return; } } // If there are any data to write in write buffer, write as much as // possible to the socket. Note that amount of data to write can be // arbitratily large. However, we assume that underlying TCP layer has // limited transmission buffer and thus the actual number of bytes // written should be reasonably modest. int nbytes = Write(m_outpos, m_outsize); // IO error has occurred. We stop waiting for output events. // The engine is not terminated until we detect input error; // this is necessary to prevent losing incomming messages. if (nbytes == -1) { m_ioObject.ResetPollout(m_handle); return; } m_outpos.AdvanceOffset(nbytes); m_outsize -= nbytes; // If we are still handshaking and there are no data // to send, stop polling for output. if (m_handshaking) { if (m_outsize == 0) { m_ioObject.ResetPollout(m_handle); } } }
public void InEvent() { // If still handshaking, receive and process the greeting message. if (m_handshaking) { if (!Handshake()) { return; } } Debug.Assert(m_decoder != null); bool disconnection = false; int processed; // If there's no data to process in the buffer... if (m_insize == 0) { // Retrieve the buffer and read as much data as possible. // Note that buffer can be arbitrarily large. However, we assume // the underlying TCP layer has fixed buffer size and thus the // number of bytes read will be always limited. m_decoder.GetBuffer(ref m_inpos, ref m_insize); m_insize = Read(m_inpos, m_insize); // Check whether the peer has closed the connection. if (m_insize == -1) { m_insize = 0; disconnection = true; } } if (m_options.RawSocket) { if (m_insize == 0 || !m_decoder.MessageReadySize(m_insize)) { processed = 0; } else { processed = m_decoder.ProcessBuffer(m_inpos, m_insize); } } else { // Push the data to the decoder. processed = m_decoder.ProcessBuffer(m_inpos, m_insize); } if (processed == -1) { disconnection = true; } else { // Stop polling for input if we got stuck. if (processed < m_insize) { m_ioObject.ResetPollin(m_handle); } m_inpos.AdvanceOffset(processed); m_insize -= processed; } // Flush all messages the decoder may have produced. m_session.Flush(); // An input error has occurred. If the last decoded message // has already been accepted, we terminate the engine immediately. // Otherwise, we stop waiting for socket events and postpone // the termination until after the message is accepted. if (disconnection) { if (m_decoder.Stalled()) { m_ioObject.RmFd(m_handle); m_inputError = true; } else { Error(); } } }
public void GetData(ref ByteArraySegment data, ref int size, ref int offset) { ByteArraySegment buffer = data ?? new ByteArraySegment(m_buf); int bufferSize = data == null ? m_buffersize : size; int pos = 0; while (pos < bufferSize) { // If there are no more data to return, run the state machine. // If there are still no data, return what we already have // in the buffer. if (m_toWrite == 0) { // If we are to encode the beginning of a new message, // adjust the message offset. if (m_beginning) { if (offset == -1) { offset = pos; } } if (!Next()) { break; } } // If there are no data in the buffer yet and we are able to // fill whole buffer in a single go, let's use zero-copy. // There's no disadvantage to it as we cannot stuck multiple // messages into the buffer anyway. Note that subsequent // write(s) are non-blocking, thus each single write writes // at most SO_SNDBUF bytes at once not depending on how large // is the chunk returned from here. // As a consequence, large messages being sent won't block // other engines running in the same I/O thread for excessive // amounts of time. if (pos == 0 && data == null && m_toWrite >= bufferSize) { data = m_writePos; size = m_toWrite; m_writePos = null; m_toWrite = 0; return; } // Copy data to the buffer. If the buffer is full, return. int toCopy = Math.Min(m_toWrite, bufferSize - pos); if (toCopy != 0) { m_writePos.CopyTo(0, buffer, pos, toCopy); pos += toCopy; m_writePos.AdvanceOffset(toCopy); m_toWrite -= toCopy; } } data = buffer; size = pos; }
// Processes the data in the buffer previously allocated using // get_buffer function. size_ argument specifies nemuber of bytes // actually filled into the buffer. Function returns number of // bytes actually processed. public int ProcessBuffer(ByteArraySegment data, int size) { // Check if we had an error in previous attempt. if (State < 0) { return(-1); } // In case of zero-copy simply adjust the pointers, no copying // is required. Also, run the state machine in case all the data // were processed. if (data.Equals(m_readPos)) { m_readPos.AdvanceOffset(size); m_toRead -= size; while (m_toRead == 0) { if (!Next()) { if (State < 0) { return(-1); } return(size); } } return(size); } int pos = 0; while (true) { // Try to get more space in the message to fill in. // If none is available, return. while (m_toRead == 0) { if (!Next()) { if (State < 0) { return(-1); } return(pos); } } // If there are no more data in the buffer, return. if (pos == size) { return(pos); } // Copy the data from buffer to the message. int toCopy = Math.Min(m_toRead, size - pos); data.CopyTo(pos, m_readPos, 0, toCopy); m_readPos.AdvanceOffset(toCopy); pos += toCopy; m_toRead -= toCopy; } }
public void InEvent() { if (m_pendingBytes > 0) { return; } // Get new batch of data. // Note the workaround made not to break strict-aliasing rules. data.Reset(); int received = 0; try { received = m_handle.Receive((byte[])data); } catch (SocketException ex) { if (ex.SocketErrorCode == SocketError.WouldBlock) { return; //break; } else { m_joined = false; Error(); return; } } // No data to process. This may happen if the packet received is // neither ODATA nor ODATA. if (received == 0) { return; } // Read the offset of the fist message in the current packet. Debug.Assert(received >= sizeof(ushort)); ushort offset = data.GetUnsignedShort(m_options.Endian, 0); data.AdvanceOffset(sizeof(ushort)); received -= sizeof(ushort); // Join the stream if needed. if (!m_joined) { // There is no beginning of the message in current packet. // Ignore the data. if (offset == 0xffff) { return; } Debug.Assert(offset <= received); Debug.Assert(m_decoder == null); // We have to move data to the begining of the first message. data.AdvanceOffset(offset); received -= offset; // Mark the stream as joined. m_joined = true; // Create and connect decoder for the peer. m_decoder = new Decoder(0, m_options.Maxmsgsize, m_options.Endian); m_decoder.SetMsgSink(m_session); } // Push all the data to the decoder. int processed = m_decoder.ProcessBuffer(data, received); if (processed < received) { // Save some state so we can resume the decoding process later. m_pendingBytes = received - processed; m_pendingData = new ByteArraySegment(data, processed); // Stop polling. m_ioObject.ResetPollin(m_handle); return; } m_session.Flush(); }