zmq.TcpConnecter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jeromq Show documentation
Show all versions of jeromq Show documentation
Pure Java implementation of libzmq
/*
Copyright (c) 2009-2011 250bpm s.r.o.
Copyright (c) 2007-2009 iMatix Corporation
Copyright (c) 2007-2011 Other contributors as noted in the AUTHORS file
This file is part of 0MQ.
0MQ is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
0MQ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see .
*/
package zmq;
import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.nio.channels.SocketChannel;
// If 'delay' is true connecter first waits for a while, then starts
// connection process.
public class TcpConnecter extends Own implements IPollEvents {
// ID of the timer used to delay the reconnection.
private final static int reconnect_timer_id = 1;
private final IOObject io_object;
// Address to connect to. Owned by session_base_t.
private final Address addr;
// Underlying socket.
private SocketChannel handle;
// If true file descriptor is registered with the poller and 'handle'
// contains valid value.
private boolean handle_valid;
// If true, connecter is waiting a while before trying to connect.
private boolean delayed_start;
// True iff a timer has been started.
private boolean timer_started;
// Reference to the session we belong to.
private SessionBase session;
// Current reconnect ivl, updated for backoff strategy
private int current_reconnect_ivl;
// String representation of endpoint to connect to
private Address address;
// Socket
private SocketBase socket;
public TcpConnecter (IOThread io_thread_,
SessionBase session_, final Options options_,
final Address addr_, boolean delayed_start_) {
super (io_thread_, options_);
io_object = new IOObject(io_thread_);
addr = addr_;
handle = null;
handle_valid = false;
delayed_start = delayed_start_;
timer_started = false;
session = session_;
current_reconnect_ivl = options.reconnect_ivl;
assert (addr != null);
address = addr;
socket = session_.get_soket ();
}
public void destroy ()
{
assert (!timer_started);
assert (!handle_valid);
assert (handle == null);
}
@Override
protected void process_plug ()
{
io_object.set_handler(this);
if (delayed_start)
add_reconnect_timer();
else {
start_connecting ();
}
}
@Override
public void process_term (int linger_)
{
if (timer_started) {
io_object.cancel_timer (reconnect_timer_id);
timer_started = false;
}
if (handle_valid) {
io_object.rm_fd (handle);
handle_valid = false;
}
if (handle != null)
close ();
super.process_term (linger_);
}
@Override
public void in_event() {
// connected but attaching to stream engine is not completed. do nothing
return;
}
@Override
public void out_event() {
// connected but attaching to stream engine is not completed. do nothing
return;
}
@Override
public void accept_event() {
throw new UnsupportedOperationException();
}
@Override
public void connect_event ()
{
boolean err = false;
SocketChannel fd = null;
try {
fd = connect ();
} catch (ConnectException e) {
err = true;
} catch (SocketException e) {
err = true;
} catch (SocketTimeoutException e) {
err = true;
} catch (IOException e) {
throw new ZError.IOException(e);
}
io_object.rm_fd (handle);
handle_valid = false;
if (err) {
// Handle the error condition by attempt to reconnect.
close ();
add_reconnect_timer();
return;
}
handle = null;
try {
Utils.tune_tcp_socket (fd);
Utils.tune_tcp_keepalives (fd, options.tcp_keepalive, options.tcp_keepalive_cnt, options.tcp_keepalive_idle, options.tcp_keepalive_intvl);
} catch (SocketException e) {
throw new RuntimeException(e);
}
// Create the engine object for this connection.
StreamEngine engine = null;
try {
engine = new StreamEngine (fd, options, address.toString());
} catch (ZError.InstantiationException e) {
socket.event_connect_delayed (address.toString(), -1);
return;
}
// Attach the engine to the corresponding session object.
send_attach (session, engine);
// Shut the connecter down.
terminate ();
socket.event_connected (address.toString(), fd);
}
@Override
public void timer_event(int id_) {
timer_started = false;
start_connecting ();
}
// Internal function to start the actual connection establishment.
private void start_connecting ()
{
// Open the connecting socket.
try {
boolean rc = open ();
// Connect may succeed in synchronous manner.
if (rc) {
io_object.add_fd (handle);
handle_valid = true;
io_object.connect_event();
}
// Connection establishment may be delayed. Poll for its completion.
else {
io_object.add_fd (handle);
handle_valid = true;
io_object.set_pollconnect (handle);
socket.event_connect_delayed (address.toString(), -1);
}
} catch (IOException e) {
// Handle any other error condition by eventual reconnect.
if (handle != null)
close ();
add_reconnect_timer();
}
}
// Internal function to add a reconnect timer
private void add_reconnect_timer()
{
int rc_ivl = get_new_reconnect_ivl();
io_object.add_timer(rc_ivl, reconnect_timer_id);
// resolve address again to take into account other addresses
// besides the failing one (e.g. multiple dns entries).
address.resolve();
socket.event_connect_retried(address.toString(), rc_ivl);
timer_started = true;
}
// Internal function to return a reconnect backoff delay.
// Will modify the current_reconnect_ivl used for next call
// Returns the currently used interval
private int get_new_reconnect_ivl ()
{
// The new interval is the current interval + random value.
int this_interval = current_reconnect_ivl +
(Utils.generate_random () % options.reconnect_ivl);
// Only change the current reconnect interval if the maximum reconnect
// interval was set and if it's larger than the reconnect interval.
if (options.reconnect_ivl_max > 0 &&
options.reconnect_ivl_max > options.reconnect_ivl) {
// Calculate the next interval
current_reconnect_ivl = current_reconnect_ivl * 2;
if(current_reconnect_ivl >= options.reconnect_ivl_max) {
current_reconnect_ivl = options.reconnect_ivl_max;
}
}
return this_interval;
}
// Open TCP connecting socket. Returns -1 in case of error,
// true if connect was successfull immediately. Returns false with
// if async connect was launched.
private boolean open () throws IOException
{
assert (handle == null);
// Create the socket.
handle = SocketChannel.open();
// Set the socket to non-blocking mode so that we get async connect().
Utils.unblock_socket(handle);
// Connect to the remote peer.
boolean rc = handle.connect(addr.resolved().address());
return rc;
}
// Get the file descriptor of newly created connection. Returns
// retired_fd if the connection was unsuccessfull.
private SocketChannel connect () throws IOException
{
boolean finished = handle.finishConnect();
assert finished;
SocketChannel ret = handle;
return ret;
}
// Close the connecting socket.
private void close() {
assert (handle != null);
try {
handle.close();
socket.event_closed (address.toString(), handle);
} catch (IOException e) {
socket.event_close_failed (address.toString(), ZError.exccode(e));
}
handle = null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy