@Override public void in_event() { // If still handshaking, receive and process the greeting message. if (handshaking) if (!handshake()) return; assert (decoder != null); boolean disconnection = false; // If there's no data to process in the buffer... if (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. inbuf = decoder.get_buffer(); insize = read(inbuf); inbuf.flip(); // Check whether the peer has closed the connection. if (insize == -1) { insize = 0; disconnection = true; } } // Push the data to the decoder. int processed = decoder.process_buffer(inbuf, insize); if (processed == -1) { disconnection = true; } else { // Stop polling for input if we got stuck. if (processed < insize) io_object.reset_pollin(handle); // Adjust the buffer. insize -= processed; } // Flush all messages the decoder may have produced. 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 (decoder.stalled()) { io_object.rm_fd(handle); io_enabled = false; } else error(); } }
@Override public void activate_in() { if (!io_enabled) { // 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. decoder.process_buffer(inbuf, 0); assert (!decoder.stalled()); session.flush(); error(); return; } io_object.set_pollin(handle); // Speculative read. io_object.in_event(); }
private void unplug() { assert (plugged); plugged = false; // Cancel all fd subscriptions. if (io_enabled) { io_object.rm_fd(handle); io_enabled = false; } // Disconnect from I/O threads poller object. io_object.unplug(); // Disconnect from session object. if (encoder != null) encoder.set_msg_source(null); if (decoder != null) decoder.set_msg_sink(null); session = null; }
@Override public int push_msg(Msg msg_) { assert (options.type == ZMQ.ZMQ_PUB || options.type == ZMQ.ZMQ_XPUB); // The first message is identity. // Let the session process it. int rc = session.push_msg(msg_); assert (rc == 0); // Inject the subscription message so that the ZMQ 2.x peer // receives our messages. msg_ = new Msg(1); msg_.put((byte) 1); rc = session.push_msg(msg_); session.flush(); // Once we have injected the subscription message, we can // Divert the message flow back to the session. assert (decoder != null); decoder.set_msg_sink(session); return rc; }
private boolean handshake() { assert (handshaking); // Receive the greeting. while (greeting.position() < GREETING_SIZE) { final int n = read(greeting); if (n == -1) { error(); return false; } if (n == 0) return false; // We have received at least one byte from the peer. // If the first byte is not 0xff, we know that the // peer is using unversioned protocol. if (greeting.array()[0] != -1) break; if (greeting.position() < 10) continue; // Inspect the right-most bit of the 10th byte (which coincides // with the 'flags' field if a regular message was sent). // Zero indicates this is a header of identity message // (i.e. the peer is using the unversioned protocol). if ((greeting.array()[9] & 0x01) == 0) break; // The peer is using versioned protocol. // Send the rest of the greeting, if necessary. if (greeting_output_buffer.limit() < GREETING_SIZE) { if (outsize == 0) io_object.set_pollout(handle); int pos = greeting_output_buffer.position(); greeting_output_buffer.position(10).limit(GREETING_SIZE); greeting_output_buffer.put((byte) 1); // Protocol version greeting_output_buffer.put((byte) options.type); // Socket type greeting_output_buffer.position(pos); outsize += 2; } } // Position of the version field in the greeting. final int version_pos = 10; // Is the peer using the unversioned protocol? // If so, we send and receive rests of identity // messages. if (greeting.array()[0] != -1 || (greeting.array()[9] & 0x01) == 0) { encoder = new_encoder(Config.out_batch_size.getValue(), null, 0); encoder.set_msg_source(session); decoder = new_decoder(Config.in_batch_size.getValue(), options.maxmsgsize, null, 0); decoder.set_msg_sink(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. final int header_size = options.identity_size + 1 >= 255 ? 10 : 2; ByteBuffer tmp = ByteBuffer.allocate(header_size); encoder.get_data(tmp); assert (tmp.remaining() == header_size); // Make sure the decoder sees the data we have already received. inbuf = greeting; greeting.flip(); insize = greeting.remaining(); // To allow for interoperability with peers that do not forward // their subscriptions, we inject a phony subsription // message into the incomming message stream. To put this // message right after the identity message, we temporarily // divert the message stream from session to ourselves. if (options.type == ZMQ.ZMQ_PUB || options.type == ZMQ.ZMQ_XPUB) decoder.set_msg_sink(this); } else if (greeting.array()[version_pos] == 0) { // ZMTP/1.0 framing. encoder = new_encoder(Config.out_batch_size.getValue(), null, 0); encoder.set_msg_source(session); decoder = new_decoder(Config.in_batch_size.getValue(), options.maxmsgsize, null, 0); decoder.set_msg_sink(session); } else { // v1 framing protocol. encoder = new_encoder(Config.out_batch_size.getValue(), session, V1Protocol.VERSION); decoder = new_decoder( Config.in_batch_size.getValue(), options.maxmsgsize, session, V1Protocol.VERSION); } // Start polling for output if necessary. if (outsize == 0) io_object.set_pollout(handle); // Handshaking was successful. // Switch into the normal message flow. handshaking = false; return true; }