public StreamEngine(AsyncSocket handle, Options options, string endpoint) { this.m_handle = handle; this.m_insize = 0; this.m_ioEnabled = false; this.m_sendingState = SendState.Idle; this.m_receivingState = ReceiveState.Idle; this.m_outsize = 0; this.m_session = null; this.m_options = options; this.m_plugged = false; this.m_endpoint = endpoint; this.m_socket = null; this.m_encoder = null; this.m_decoder = null; this.m_actionsQueue = new Queue <StateMachineAction>(); // Set the socket buffer limits for the underlying socket. if (this.m_options.SendBuffer != 0) { this.m_handle.SendBufferSize = this.m_options.SendBuffer; } if (this.m_options.ReceiveBuffer != 0) { this.m_handle.ReceiveBufferSize = this.m_options.ReceiveBuffer; } }
private void HandleHandshake(Action action, SocketError socketError, int bytesTransferred) { int bytesSent; int bytesReceived; switch (this.m_handshakeState) { case HandshakeState.Closed: switch (action) { case Action.Start: // Send the 'length' and 'flags' fields of the identity message. // The 'length' field is encoded in the long format. this.m_greetingOutputBuffer[this.m_outsize++] = 0xff; this.m_greetingOutputBuffer.PutLong(this.m_options.Endian, (long)this.m_options.IdentitySize + 1, 1); this.m_outsize += 8; this.m_greetingOutputBuffer[this.m_outsize++] = 0x7f; this.m_outpos = new ByteArraySegment(this.m_greetingOutputBuffer); this.m_handshakeState = HandshakeState.SendingGreeting; this.BeginWrite(this.m_outpos, this.m_outsize); break; default: Debug.Assert(false); break; } break; case HandshakeState.SendingGreeting: switch (action) { case Action.OutCompleted: bytesSent = StreamEngine.EndWrite(socketError, bytesTransferred); if (bytesSent == -1) { this.Error(); } else { this.m_outpos.AdvanceOffset(bytesSent); this.m_outsize -= bytesSent; if (this.m_outsize > 0) { this.BeginWrite(this.m_outpos, this.m_outsize); } else { this.m_greetingBytesRead = 0; ByteArraySegment greetingSegment = new ByteArraySegment(this.m_greeting, this.m_greetingBytesRead); this.m_handshakeState = HandshakeState.ReceivingGreeting; this.BeginRead(greetingSegment, StreamEngine.PreambleSize); } } break; case Action.ActivateIn: case Action.ActivateOut: // nothing to do break; default: Debug.Assert(false); break; } break; case HandshakeState.ReceivingGreeting: switch (action) { case Action.InCompleted: bytesReceived = StreamEngine.EndRead(socketError, bytesTransferred); if (bytesReceived == -1) { this.Error(); } else { this.m_greetingBytesRead += bytesReceived; // check if it is an unversioned protocol if (this.m_greeting[0] != 0xff || this.m_greetingBytesRead == 10 && (this.m_greeting[9] & 0x01) == 0) { this.m_encoder = new V1Encoder(Config.OutBatchSize, this.m_options.Endian); this.m_encoder.SetMsgSource(this.m_session); this.m_decoder = new V1Decoder(Config.InBatchSize, this.m_options.MaxMessageSize, this.m_options.Endian); this.m_decoder.SetMsgSink(this.m_session); // We have already sent the message header. // Since there is no way to tell the encoder to // skip the message header, we simply throw that // header data away. int headerSize = this.m_options.IdentitySize + 1 >= 255 ? 10 : 2; byte[] tmp = new byte[10]; ByteArraySegment bufferp = new ByteArraySegment(tmp); int bufferSize = headerSize; this.m_encoder.GetData(ref bufferp, ref bufferSize); Debug.Assert(bufferSize == headerSize); // Make sure the decoder sees the data we have already received. this.m_inpos = new ByteArraySegment(this.m_greeting); this.m_insize = this.m_greetingBytesRead; // To allow for interoperability with peers that do not forward // their subscriptions, we inject a phony subscription // message into the incoming message stream. To put this // message right after the identity message, we temporarily // divert the message stream from session to ourselves. if (this.m_options.SocketType == ZmqSocketType.Pub || this.m_options.SocketType == ZmqSocketType.Xpub) { this.m_decoder.SetMsgSink(this); } // handshake is done this.Activate(); } else if (this.m_greetingBytesRead < 10) { ByteArraySegment greetingSegment = new ByteArraySegment(this.m_greeting, this.m_greetingBytesRead); this.BeginRead(greetingSegment, StreamEngine.PreambleSize - this.m_greetingBytesRead); } else { // The peer is using versioned protocol. // Send the rest of the greeting. this.m_outpos[this.m_outsize++] = 1; // Protocol version this.m_outpos[this.m_outsize++] = (byte)this.m_options.SocketType; this.m_handshakeState = HandshakeState.SendingRestOfGreeting; this.BeginWrite(this.m_outpos, this.m_outsize); } } break; case Action.ActivateIn: case Action.ActivateOut: // nothing to do break; default: Debug.Assert(false); break; } break; case HandshakeState.SendingRestOfGreeting: switch (action) { case Action.OutCompleted: bytesSent = StreamEngine.EndWrite(socketError, bytesTransferred); if (bytesSent == -1) { this.Error(); } else { this.m_outpos.AdvanceOffset(bytesSent); this.m_outsize -= bytesSent; if (this.m_outsize > 0) { this.BeginWrite(this.m_outpos, this.m_outsize); } else { ByteArraySegment greetingSegment = new ByteArraySegment(this.m_greeting, this.m_greetingBytesRead); this.m_handshakeState = HandshakeState.ReceivingRestOfGreeting; this.BeginRead(greetingSegment, StreamEngine.GreetingSize - this.m_greetingBytesRead); } } break; case Action.ActivateIn: case Action.ActivateOut: // nothing to do break; default: Debug.Assert(false); break; } break; case HandshakeState.ReceivingRestOfGreeting: switch (action) { case Action.InCompleted: bytesReceived = StreamEngine.EndRead(socketError, bytesTransferred); if (bytesReceived == -1) { this.Error(); } else { this.m_greetingBytesRead += bytesReceived; if (this.m_greetingBytesRead < StreamEngine.GreetingSize) { ByteArraySegment greetingSegment = new ByteArraySegment(this.m_greeting, this.m_greetingBytesRead); this.BeginRead(greetingSegment, StreamEngine.GreetingSize - this.m_greetingBytesRead); } else { if (this.m_greeting[StreamEngine.VersionPos] == 0) { // ZMTP/1.0 framing. this.m_encoder = new V1Encoder(Config.OutBatchSize, this.m_options.Endian); this.m_encoder.SetMsgSource(this.m_session); this.m_decoder = new V1Decoder(Config.InBatchSize, this.m_options.MaxMessageSize, this.m_options.Endian); this.m_decoder.SetMsgSink(this.m_session); } else { // v1 framing protocol. this.m_encoder = new V2Encoder(Config.OutBatchSize, this.m_session, this.m_options.Endian); this.m_decoder = new V2Decoder(Config.InBatchSize, this.m_options.MaxMessageSize, this.m_session, this.m_options.Endian); } // handshake is done this.Activate(); } } break; case Action.ActivateIn: case Action.ActivateOut: // nothing to do break; default: Debug.Assert(false); break; } break; default: Debug.Assert(false); break; } }
private void Handle(Action action, SocketError socketError, int bytesTransferred) { switch (this.m_state) { case State.Closed: switch (action) { case Action.Start: if (this.m_options.RawSocket) { this.m_encoder = new RawEncoder(Config.OutBatchSize, this.m_session, this.m_options.Endian); this.m_decoder = new RawDecoder(Config.InBatchSize, this.m_options.MaxMessageSize, this.m_session, this.m_options.Endian); this.Activate(); } else { this.m_state = State.Handshaking; this.m_handshakeState = HandshakeState.Closed; this.HandleHandshake(action, socketError, bytesTransferred); } break; } break; case State.Handshaking: this.HandleHandshake(action, socketError, bytesTransferred); break; case State.Active: switch (action) { case Action.InCompleted: this.m_insize = StreamEngine.EndRead(socketError, bytesTransferred); this.ProcessInput(); break; case Action.ActivateIn: // if we stuck let's continue, other than that nothing to do if (this.m_receivingState == ReceiveState.Stuck) { this.m_receivingState = ReceiveState.Active; this.ProcessInput(); } break; case Action.OutCompleted: int bytesSent = StreamEngine.EndWrite(socketError, bytesTransferred); // 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 incoming messages. if (bytesSent == -1) { this.m_sendingState = SendState.Error; } else { this.m_outpos.AdvanceOffset(bytesSent); this.m_outsize -= bytesSent; this.BeginSending(); } break; case Action.ActivateOut: // if we idle we start sending, other than do nothing if (this.m_sendingState == SendState.Idle) { this.m_sendingState = SendState.Active; this.BeginSending(); } break; default: Debug.Assert(false); break; } break; case State.Stalled: switch (action) { case Action.ActivateIn: // There was an input error but the engine could not // be terminated (due to the stalled decoder). // Flush the pending message and terminate the engine now. this.m_decoder.ProcessBuffer(this.m_inpos, 0); Debug.Assert(!this.m_decoder.Stalled()); this.m_session.Flush(); this.Error(); break; case Action.ActivateOut: break; } break; } }