org.jgroups.blocks.cs.TcpServer Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
package org.jgroups.blocks.cs;
import org.jgroups.Address;
import org.jgroups.util.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
/**
* Class that manages allows to send and receive messages via TCP sockets. Uses 1 thread/connection to read messages.
* @author Vladimir Blagojevic
* @author Bela Ban
*/
public class TcpServer extends TcpBaseServer {
protected ServerSocket srv_sock;
protected Thread acceptor;
protected boolean log_accept_error=true;
public boolean getLogAcceptError() {return log_accept_error;}
public TcpServer setLogAcceptError(boolean l) {log_accept_error=l; return this;}
/**
* Creates an instance of {@link TcpServer} that creates a server socket and listens for connections.
* The end port defaults to (port + 50). Needs to be started next.
* @param bind_addr The local address to bind to. If null, the address will be picked by the OS
* @param port The local port to bind to. If 0, the port will be picked by the OS.
* @throws Exception Thrown if the creation failed
*/
public TcpServer(InetAddress bind_addr, int port) throws Exception {
this(new DefaultThreadFactory("tcp", false),
new DefaultSocketFactory(), bind_addr, port, port+50, null, 0, 0);
}
public TcpServer(ThreadFactory thread_factory, SocketFactory socket_factory,
InetAddress bind_addr, int srv_port, int end_port,
InetAddress external_addr, int external_port, int recv_buf_size) throws Exception {
this(thread_factory, socket_factory, bind_addr, srv_port, end_port, external_addr, external_port,
recv_buf_size, "jgroups.tcp.server");
}
/**
* Creates an instance of {@link TcpServer} that creates a server socket and listens for connections
* Needs to be started next.
* @param thread_factory The thread factory used to create new threads
* @param socket_factory The socket factory used to create sockets
* @param bind_addr The local address to bind to. If null, the address will be picked by the OS
* @param srv_port The local port to bind to. If 0, the port will be picked by the OS.
* @param end_port If srv_port is taken, the next port is tried, until end_port has been reached, in which case an
* exception will be thrown. If srv_port == end_port, only 1 port will be tried.
* @param external_addr The external address in case of NAT. Ignored if null.
* @param external_port The external port on the NA. If 0, srv_port is used.
* @param recv_buf_size The size of the initial TCP receive window (in bytes)
* @param service_name The name of the service
* @throws Exception Thrown if the creation failed
*/
public TcpServer(ThreadFactory thread_factory, SocketFactory socket_factory,
InetAddress bind_addr, int srv_port, int end_port,
InetAddress external_addr, int external_port, int recv_buf_size,
String service_name) throws Exception {
super(thread_factory, socket_factory, recv_buf_size);
this.srv_sock=Util.createServerSocket(this.socket_factory, service_name, bind_addr,
srv_port, end_port, recv_buf_size);
acceptor=factory.newThread(new Acceptor(),"TcpServer.Acceptor[" + srv_sock.getLocalPort() + "]");
local_addr=localAddress(bind_addr, srv_sock.getLocalPort(), external_addr, external_port);
addConnectionListener(this);
}
@Override
public void start() throws Exception {
if(running.compareAndSet(false, true)) {
acceptor.start();
super.start();
}
}
@Override
public void stop() {
if(running.compareAndSet(true, false)) {
Util.close(srv_sock);
Util.interruptAndWaitToDie(acceptor);
super.stop();
}
}
/** Acceptor thread. Continuously accepts new connections, creating a new thread for each new connection and
* putting it in conns. When the thread should stop, it is interrupted by the thread creator */
protected class Acceptor implements Runnable {
public void run() {
while(!srv_sock.isClosed() && !Thread.currentThread().isInterrupted()) {
Socket client_sock=null;
try {
client_sock=srv_sock.accept();
handleAccept(client_sock);
}
catch(Exception ex) {
if(ex instanceof SocketException && srv_sock.isClosed() || Thread.currentThread().isInterrupted())
break;
if(log_accept_error)
log.warn(Util.getMessage("AcceptError"), local_addr, client_sock, ex);
Util.close(client_sock);
}
}
}
protected void handleAccept(final Socket client_sock) throws Exception {
TcpConnection conn=null;
try {
conn=non_blocking_sends? new TcpConnectionNonBlocking(client_sock, TcpServer.this).maxSize(max_send_queue)
: new TcpConnection(client_sock, TcpServer.this);
Address peer_addr=conn.peerAddress();
boolean conn_exists=hasConnection(peer_addr),
replace=conn_exists && use_peer_connections && local_addr.compareTo(peer_addr) < 0; // bigger conn wins
if(!conn_exists || replace) {
if(use_acks)
conn.send(OK, 0, OK.length); // do this *before* other threads can send messages!!
replaceConnection(peer_addr, conn); // closes old conn
conn.start();
log.trace("%s: accepted connection from %s", local_addr, peer_addr);
}
else {
log.trace("%s: rejected connection from %s %s", local_addr, peer_addr, explanation(conn_exists, replace));
if(use_acks)
conn.send(FAIL, 0, FAIL.length);
conn.flush();
Util.close(conn); // keep our existing conn, reject accept() and close client_sock
}
}
catch(Exception ex) {
Util.close(conn);
throw ex;
}
}
}
}