All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bboss.org.jgroups.blocks.ConnectionTable Maven / Gradle / Ivy

The newest version!
// $Id: ConnectionTable.java,v 1.72 2010/06/15 10:10:41 belaban Exp $

package bboss.org.jgroups.blocks;

import java.io.IOException;
import java.net.BindException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;

import bboss.org.jgroups.Address;
import bboss.org.jgroups.stack.IpAddress;
import bboss.org.jgroups.util.Util;


/**
 * Manages incoming and outgoing TCP connections. For each outgoing message to destination P, if there
 * is not yet a connection for P, one will be created. Subsequent outgoing messages will use this
 * connection.  For incoming messages, one server socket is created at startup. For each new incoming
 * client connecting, a new thread from a thread pool is allocated and listens for incoming messages
 * until the socket is closed by the peer.
Sockets/threads with no activity will be killed * after some time. *

* Incoming messages from any of the sockets can be received by setting the message listener. * @author Bela Ban */ public class ConnectionTable extends BasicConnectionTable implements Runnable { /** * Regular ConnectionTable without expiration of idle connections * @param srv_port The port on which the server will listen. If this port is reserved, the next * free port will be taken (incrementing srv_port). */ public ConnectionTable(int srv_port) throws Exception { this.srv_port=srv_port; init(); } public ConnectionTable(InetAddress bind_addr, int srv_port) throws Exception { this.srv_port=srv_port; this.bind_addr=bind_addr; init(); } /** * ConnectionTable including a connection reaper. Connections that have been idle for more than conn_expire_time * milliseconds will be closed and removed from the connection table. On next access they will be re-created. * @param srv_port The port on which the server will listen * @param reaper_interval Number of milliseconds to wait for reaper between attepts to reap idle connections * @param conn_expire_time Number of milliseconds a connection can be idle (no traffic sent or received until * it will be reaped */ public ConnectionTable(int srv_port, long reaper_interval, long conn_expire_time) throws Exception { this.srv_port=srv_port; this.reaper_interval=reaper_interval; this.conn_expire_time=conn_expire_time; use_reaper=true; init(); } /** * Create a ConnectionTable * @param r A reference to a receiver of all messages received by this class. Method receive() * will be called. * @param bind_addr The host name or IP address of the interface to which the server socket will bind. * This is interesting only in multi-homed systems. If bind_addr is null, the * server socket will bind to the first available interface (e.g. /dev/hme0 on * Solaris or /dev/eth0 on Linux systems). * @param external_addr The address which will be broadcast to the group (the externally visible address * which this host should be contacted on). If external_addr is null, it will default to * the same address that the server socket is bound to. * @param srv_port The port to which the server socket will bind to. If this port is reserved, the next * free port will be taken (incrementing srv_port). * @param max_port The largest port number that the server socket will be bound to. If max_port < srv_port * then there is no limit. */ public ConnectionTable(Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port) throws Exception { setReceiver(r); this.bind_addr=bind_addr; this.external_addr=external_addr; this.srv_port=srv_port; this.max_port=max_port; init(); } /** * ConnectionTable including a connection reaper. Connections that have been idle for more than conn_expire_time * milliseconds will be closed and removed from the connection table. On next access they will be re-created. * * @param r The Receiver * @param bind_addr The host name or IP address of the interface to which the server socket will bind. * This is interesting only in multi-homed systems. If bind_addr is null, the * server socket will bind to the first available interface (e.g. /dev/hme0 on * Solaris or /dev/eth0 on Linux systems). * @param external_addr The address which will be broadcast to the group (the externally visible address * which this host should be contacted on). If external_addr is null, it will default to * the same address that the server socket is bound to. * @param srv_port The port to which the server socket will bind to. If this port is reserved, the next * free port will be taken (incrementing srv_port). * @param max_port The largest port number that the server socket will be bound to. If max_port < srv_port * then there is no limit. * @param reaper_interval Number of milliseconds to wait for reaper between attepts to reap idle connections * @param conn_expire_time Number of milliseconds a connection can be idle (no traffic sent or received until * it will be reaped */ public ConnectionTable(Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port, long reaper_interval, long conn_expire_time) throws Exception { setReceiver(r); this.bind_addr=bind_addr; this.external_addr=external_addr; this.srv_port=srv_port; this.max_port=max_port; this.reaper_interval=reaper_interval; this.conn_expire_time=conn_expire_time; use_reaper=true; init(); } /** Try to obtain correct Connection (or create one if not yet existent) */ Connection getConnection(Address dest) throws Exception { Connection conn=null; Socket sock; synchronized(conns) { conn=conns.get(dest); if(conn == null) { // changed by bela Jan 18 2004: use the bind address for the client sockets as well SocketAddress tmpBindAddr=new InetSocketAddress(bind_addr, 0); InetAddress tmpDest=((IpAddress)dest).getIpAddress(); SocketAddress destAddr=new InetSocketAddress(tmpDest, ((IpAddress)dest).getPort()); sock=new Socket(); sock.bind(tmpBindAddr); sock.setKeepAlive(true); sock.setTcpNoDelay(tcp_nodelay); if(linger > 0) sock.setSoLinger(true, linger); else sock.setSoLinger(false, -1); Util.connect(sock, destAddr, sock_conn_timeout); try { sock.setSendBufferSize(send_buf_size); } catch(IllegalArgumentException ex) { if(log.isErrorEnabled()) log.error("exception setting send buffer size to " + send_buf_size + " bytes", ex); } try { sock.setReceiveBufferSize(recv_buf_size); } catch(IllegalArgumentException ex) { if(log.isErrorEnabled()) log.error("exception setting receive buffer size to " + send_buf_size + " bytes", ex); } conn=new Connection(sock, dest); conn.sendLocalAddress(local_addr); notifyConnectionOpened(dest); addConnection(dest, conn); conn.init(); if(log.isTraceEnabled()) log.trace("created socket to " + dest); } return conn; } } public final void start() throws Exception { acceptor=getThreadFactory().newThread(thread_group,this, "ConnectionTable.AcceptorThread"); acceptor.start(); // start the connection reaper - will periodically remove unused connections if(use_reaper && reaper == null) { reaper=new Reaper(); reaper.start(); } super.start(); } protected void init() throws Exception { srv_sock=createServerSocket(srv_port, max_port); if (external_addr!=null) local_addr=new IpAddress(external_addr, srv_sock.getLocalPort()); else if (bind_addr != null) local_addr=new IpAddress(bind_addr, srv_sock.getLocalPort()); else local_addr=new IpAddress(srv_sock.getLocalPort()); if(log.isDebugEnabled()) log.debug("server socket listening on " + local_addr); } /** * Acceptor thread. Continuously accept new connections. Create a new thread for each new * connection and put it in conns. When the thread should stop, it is * interrupted by the thread creator. */ public void run() { Socket client_sock=null; Connection conn=null; Address peer_addr; while(srv_sock != null) { try { conn=null; client_sock=srv_sock.accept(); if(!running) { if(log.isWarnEnabled()) log.warn("cannot accept connection from " + client_sock.getRemoteSocketAddress() + " as I'm closed"); break; } if(log.isTraceEnabled()) log.trace("[" +local_addr + "] accepted connection from " + client_sock.getInetAddress() + ":" + client_sock.getPort()); try { client_sock.setSendBufferSize(send_buf_size); } catch(IllegalArgumentException ex) { if(log.isErrorEnabled()) log.error("exception setting send buffer size to " + send_buf_size + " bytes", ex); } try { client_sock.setReceiveBufferSize(recv_buf_size); } catch(IllegalArgumentException ex) { if(log.isErrorEnabled()) log.error("exception setting receive buffer size to " + send_buf_size + " bytes", ex); } client_sock.setKeepAlive(true); client_sock.setTcpNoDelay(tcp_nodelay); if(linger > 0) client_sock.setSoLinger(true, linger); else client_sock.setSoLinger(false, -1); // create new thread and add to conn table conn=new Connection(client_sock, null); // will call receive(msg) // get peer's address peer_addr=conn.readPeerAddress(client_sock); // client_addr=new IpAddress(client_sock.getInetAddress(), client_port); conn.setPeerAddress(peer_addr); synchronized(conns) { Connection tmp=conns.get(peer_addr); //Vladimir Nov, 5th, 2007 //we might have a connection to peer but is that //connection still open? boolean connectionOpen = tmp != null && !tmp.isSocketClosed(); if(connectionOpen) { if(peer_addr.compareTo(local_addr) > 0) { if(log.isTraceEnabled()) log.trace("peer's address (" + peer_addr + ") is greater than our local address (" + local_addr + "), replacing our existing connection"); // peer's address is greater, add peer's connection to ConnectionTable, destroy existing connection removeConnection(peer_addr); addConnection(peer_addr, conn); notifyConnectionOpened(peer_addr); } else { if(log.isTraceEnabled()) log.trace("peer's address (" + peer_addr + ") is smaller than our local address (" + local_addr + "), rejecting peer connection request"); conn.destroy(); continue; } } else { addConnection(peer_addr, conn); notifyConnectionOpened(peer_addr); } } conn.init(); // starts handler thread on this socket } catch(SocketTimeoutException timeout_ex) { if(log.isWarnEnabled()) log.warn("timed out waiting for peer address, closing connection " + conn + ": " + timeout_ex); if(conn != null) conn.destroy(); if(srv_sock == null) break; // socket was closed, therefore stop } catch(SocketException sock_ex) { if(log.isWarnEnabled() && srv_sock != null) log.warn("Could not read accept connection from peer " + sock_ex); if(conn != null) conn.destroy(); if(srv_sock == null) break; // socket was closed, therefore stop } catch(Throwable ex) { if(log.isWarnEnabled()) log.warn("Could not read accept connection from peer " + ex); if(srv_sock == null) break; // socket was closed, therefore stop } } if(client_sock != null) try {client_sock.close();} catch(IOException e) {} if(log.isTraceEnabled()) log.trace(Thread.currentThread().getName() + " terminated"); } /** Finds first available port starting at start_port and returns server socket. * Will not bind to port >end_port. Sets srv_port */ protected ServerSocket createServerSocket(int start_port, int end_port) throws Exception { ServerSocket ret=null; while(true) { try { if(bind_addr == null) ret=socket_factory.createServerSocket("jgroups.connection_table.srv_sock", start_port); else { // changed (bela Sept 7 2007): we accept connections on all NICs ret=socket_factory.createServerSocket("jgroups.connection_table.srv_sock", start_port, backlog, bind_addr); } } catch(BindException bind_ex) { if (start_port==end_port) throw new BindException("No available port to bind to"); if(bind_addr != null && !bind_addr.isLoopbackAddress()) { NetworkInterface nic=NetworkInterface.getByInetAddress(bind_addr); if(nic == null) throw new BindException("bind_addr " + bind_addr + " is not a valid interface"); } start_port++; continue; } catch(IOException io_ex) { if(log.isErrorEnabled()) log.error("exception is " + io_ex); } srv_port=start_port; break; } return ret; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy