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

org.jgroups.protocols.SimpleTCP Maven / Gradle / Ivy

package org.jgroups.protocols;

import org.jgroups.*;
import org.jgroups.annotations.Experimental;
import org.jgroups.annotations.MBean;
import org.jgroups.annotations.ManagedOperation;
import org.jgroups.annotations.Property;
import org.jgroups.conf.AttributeType;
import org.jgroups.stack.IpAddress;
import org.jgroups.util.*;

import java.io.*;
import java.net.*;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Bare-bones thread-per-connection TCP-based transport. Only used to compare with {@link TCP} or {@link TCP_NIO2},
 * don't use in production!
 * @author Bela Ban
 * @since  4.0
 */
@Experimental
@MBean(description="Simple TCP based transport")
public class SimpleTCP extends TP {

    @Property(description="size in bytes of TCP receiver window",type=AttributeType.BYTES)
    protected int recv_buf_size=500000;

    @Property(description="size in bytes of TCP send window",type=AttributeType.BYTES)
    protected int send_buf_size=500000;

    @Property(description="Size of the buffer of the BufferedInputStream in TcpConnection. A read always tries to read " +
      "ahead as much data as possible into the buffer. 0: default size",type=AttributeType.BYTES)
    protected int buffered_input_stream_size=8192;

    @Property(description="Size of the buffer of the BufferedOutputStream in TcpConnection. Smaller messages are " +
      " buffered until this size is exceeded or flush() is called. Bigger messages are sent immediately. 0: default size",
      type=AttributeType.BYTES)
    protected int buffered_output_stream_size=8192;


    protected ServerSocket                        srv_sock;
    protected Acceptor                            acceptor;
    protected final Map connections=new ConcurrentHashMap<>();
    protected final Map    addr_table=new ConcurrentHashMap<>();

    public boolean supportsMulticasting() {return false;}

    @ManagedOperation(description="dumps the address table")
    public String printAddressTable() {
        return addr_table.entrySet().stream()
          .collect(StringBuilder::new,
                   (sb, e) -> sb.append(e.getKey()).append(": ").append(e.getValue()).append("\n"),
                   (l, r) ->{}).toString();
    }

    public void sendUnicast(PhysicalAddress dest, byte[] data, int offset, int length) throws Exception {
        // not needed, implemented in down()
    }

    public String getInfo() {
        return "SimpleTCP";
    }

    public void init() throws Exception {
        super.init();
        srv_sock=Util.createServerSocket(new DefaultSocketFactory(), "srv-sock", bind_addr,
                                         bind_port, bind_port+50, recv_buf_size);
        acceptor=new Acceptor(bind_addr, bind_port);
    }

    public void start() throws Exception {
        super.start();
        acceptor.start();
    }

    public void stop() {
        super.stop();
        Util.close(srv_sock);
        acceptor.stop();
    }

    public void destroy() {
        super.destroy();
        acceptor.stop();
        connections.values().forEach(Util::close);
        connections.clear();
    }

    public Object down(Event evt) {
        Object retval=super.down(evt);
        switch(evt.type()) {
            case Event.ADD_PHYSICAL_ADDRESS:
                Tuple tuple=evt.arg();
                IpAddress val=(IpAddress)tuple.getVal2();
                addr_table.put(tuple.getVal1(), new InetSocketAddress(val.getIpAddress(), val.getPort()));
                break;
            case Event.VIEW_CHANGE:
                for(Iterator> it=addr_table.entrySet().iterator(); it.hasNext();) {
                    Map.Entry entry=it.next();
                    if(!view.containsMember(entry.getKey())) {
                        SocketAddress sock_addr=entry.getValue();
                        it.remove();
                        Connection conn=connections.remove(sock_addr);
                        Util.close(conn);
                    }
                }
                break;
        }
        return retval;
    }

    public Object down(Message msg) {
        try {
            return _down(msg);
        }
        catch(Exception e) {
            log.error("failure passing message down", e);
            return null;
        }
    }

    protected Object _down(Message msg) throws Exception {
        Address dest=msg.getDest();
        setSourceAddress(msg); // very important !! listToBuffer() will fail with a null src address !!
        int size=msg.size();
        ByteArrayDataOutputStream out=new ByteArrayDataOutputStream(size + Global.INT_SIZE);
        out.writeInt(size);
        msg.writeTo(out);

        if(dest != null) // unicast
            sendTo(dest, out.buffer(), 0, out.position());
        else { // multicast
            Collection
dests=view != null? view.getMembers(): addr_table.keySet(); for(Address dst: dests) { try { sendTo(dst, out.buffer(), 0, out.position()); } catch(Throwable t) { log.error("failed sending multicast message to " + dst, t); } } } return null; } protected void sendTo(Address dest, byte[] buffer, int offset, int length) throws Exception { SocketAddress physical_dest=null; if(dest instanceof IpAddress) { IpAddress ip_addr=(IpAddress)dest; physical_dest=new InetSocketAddress(ip_addr.getIpAddress(), ip_addr.getPort()); } else physical_dest=addr_table.get(dest); if(physical_dest == null) throw new Exception(String.format("physical address for %s not found", dest)); Connection conn=getConnection(physical_dest); conn.send(buffer, offset, length); } protected Connection getConnection(SocketAddress dest) throws Exception { Connection conn=connections.get(dest); if(conn != null) return conn; Socket dest_sock=new Socket(); if(send_buf_size > 0) dest_sock.setSendBufferSize(send_buf_size); if(recv_buf_size > 0) dest_sock.setReceiveBufferSize(recv_buf_size); dest_sock.connect(dest); Connection c=connections.putIfAbsent(dest, conn=new Connection(dest_sock).start()); if(c != null) { Util.close(conn); return c; } return conn; } public boolean addPhysicalAddressToCache(Address logical_addr, PhysicalAddress physical_addr) { IpAddress tmp=(IpAddress)physical_addr; addr_table.put(logical_addr, new InetSocketAddress(tmp.getIpAddress(), tmp.getPort())); return super.addPhysicalAddressToCache(logical_addr, physical_addr); } protected PhysicalAddress getPhysicalAddress() { return new IpAddress((InetSocketAddress)srv_sock.getLocalSocketAddress()); } /** Calls ServerSocket.accept() and creates new Connection objects */ protected class Acceptor implements Runnable { protected final InetAddress bind; protected final int port; protected Runner runner; public Acceptor(InetAddress bind, int port) throws Exception { this.bind=bind; this.port=port; runner=new Runner(new DefaultThreadFactory("tcp", true, true), "acceptor", this, null); } protected void start() throws Exception { runner.start(); } protected void stop() { runner.stop(); } public void run() { try { Socket client_sock=srv_sock.accept(); if(send_buf_size > 0) client_sock.setSendBufferSize(send_buf_size); if(recv_buf_size > 0) client_sock.setReceiveBufferSize(recv_buf_size); Connection c; Connection existing=connections.putIfAbsent(client_sock.getRemoteSocketAddress(), c=new Connection(client_sock).start()); if(existing != null) Util.close(c); } catch(Exception e) { throw new RuntimeException(e); } } } /** 1 connection per peer to send and receive messages */ protected class Connection implements Runnable, Closeable { protected final Socket sock; protected final IpAddress peer_addr; protected final DataInputStream in; protected final DataOutputStream out; protected final Runner runner; protected byte[] buffer=new byte[1024]; protected final AtomicInteger writers=new AtomicInteger(0); // to determine if a flush() is needed public Connection(Socket sock) throws Exception { this.sock=sock; peer_addr=new IpAddress((InetSocketAddress)sock.getRemoteSocketAddress()); in=new DataInputStream(createBufferedInputStream(sock.getInputStream())); out=new DataOutputStream(createBufferedOutputStream(sock.getOutputStream())); runner=new Runner(new DefaultThreadFactory("tcp", false, true), "conn-" + sock.getLocalPort(), this, null); } protected Connection start() { runner.start(); return this; } public void close() { runner.stop(); Util.close(in, out, sock); } protected void send(byte[] buffer, int offset, int length) throws Exception { writers.incrementAndGet(); out.write(buffer, offset, length); // a write is synchronized (on the file descriptor) // if another writer is active, we don't need to flush as the other writer will (or somebody else) if(writers.decrementAndGet() == 0) out.flush(); } public void run() { // System.out.printf("[%s] reading from sock, conn: %s\n", Thread.currentThread().getName(), this); try { int len=in.readInt(); if(buffer == null || buffer.length < len) buffer=new byte[len]; in.readFully(buffer, 0, len); ByteArrayDataInputStream input=new ByteArrayDataInputStream(buffer, 0, len); Message msg=new BytesMessage(); msg.readFrom(input); thread_pool.execute(() -> up_prot.up(msg)); } catch(IOException io_ex) { runner.stop(); throw new RuntimeException(io_ex); } catch(Exception ex) { if(sock.isClosed()) runner.stop(); throw new RuntimeException(ex); } } public String toString() { return String.format("%s -> %s", sock.getLocalSocketAddress(), peer_addr); } protected BufferedOutputStream createBufferedOutputStream(OutputStream out) { int size=buffered_output_stream_size; return size == 0? new BufferedOutputStream(out) : new BufferedOutputStream(out, size); } protected BufferedInputStream createBufferedInputStream(InputStream in) { int size=buffered_input_stream_size; return size == 0? new BufferedInputStream(in) : new BufferedInputStream(in, size); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy