views:

92

answers:

4

I'm trying to limit the number of connections my server will accept using semaphores, but when running, my code doesn't seem to make this restriction - am I using the semaphore correctly? eg. I have hardcoded the number of permit as 2, but I can connect an unlimited number of clients...

public class EServer implements Runnable {
    private ServerSocket serverSocket;
    private int numberofConnections = 0;
    private Semaphore sem = new Semaphore(2);
    private volatile boolean keepProcessing = true;

    public EServer(int port)
            throws IOException {
        serverSocket = new ServerSocket(port);
    }

    @Override
    public void run() {
        while (keepProcessing) {
            try {
                sem.acquire();
                Socket socket = serverSocket.accept();
                process(socket, getNextConnectionNumber());
            } catch (Exception e) {
            } finally {
                sem.release();
            }
        }
        closeIgnoringException(serverSocket);
    }

    private synchronized int getNextConnectionNumber() {
        return ++numberofConnections;
    }

    // processing related methods
}
A: 

You should not be calling release in your finally block. Finally blocks are always executed. What you want to do is release the lock in the exception, or when your client disconnects:

    while (keepProcessing) {
        try {
            sem.acquire();
            Socket socket = serverSocket.accept();
            process(socket, getNextConnectionNumber());
        } catch (Exception e) {
            //Here the client hasn't been connected, so release the lock.
            sem.release();
        } finally {
           //code here always executes, exception or not
        }
    }

Then in an appropriate place in your client code release the lock:

...
//The client is shutting down / disconnecting
serverInstance.sem.release(); //not good OOP

OR 

serverInstance.clientFinished(); //callback, where clientFinished in your server class will release the lock
...

Of course there are various ways to release the lock depending on how your code is orgnized, but the key is that when you call release in the finally block it will always execute (making the permit available for the next incoming connection). So only release it a) in the server when there is an exception (you know a connection hasn't been made) or b) in the client (possibly via some kind of callback/listener on the server) when you know the connection is finished / disconnected / otherwise terminated.

M. Jessup
I don't believe this is either the cause or correct - I want a new connection to be available if process finishes or if an exception is thrown, so in the finally block is correct
Robert
If process is spawning another thread for the connection (as your comments to other answers seem to statethen it should return "immediately" (i.e. while the socket is still alive) so the finally block in your code is immediately releasing the semaphore for the next incoming connection while the previous socket is still connected.
M. Jessup
+1  A: 

What does your process method look like? Could it be returning before you want it to, hence releasing the semaphore before you want it to?

Cocowalla
process maintains a persistent connection with an infinite loop
Robert
process is not releasing the semaphore
Robert
@Robert: is it in a separate thread then? That would explain the behavior you see.
Nikolai N Fetissov
@Nikolai: Since it's marked as 'homework' I was trying just to point in the right direction (i.e. "`process` looks to be returning before you want it to - why would it do that?") :)
Cocowalla
@Cocowalla: me too, exactly, having an infinite loop in the processing function probably means it starts another thread, returns in the server thread, semaphore is released.
Nikolai N Fetissov
each connection is processed in a separate thread, yes
Robert
Then you have your answer!
Cocowalla
A: 

How many threads are you creating? Just 1 server instance in 1 thread? Or is 1 instance used in 2 threads?

1 instance/1 thread - your loop is processing serially, processing accepting 1 thread at a time. This isn't because of the semaphore, but rather because its just one thread. The other connections are being queued waiting for acceptance.

1 instance/2 threads - your code (the semaphore) is limiting your server up to processing accepting 2 connections simultaneously, but TCP/IP allows for connections to be queued to be accepted (I think the general default queue length is 5). The 3rd simultaneous request (a request that comes in before either of the 2 previous requests finish processing) would wait for a certain amount of time ( I don't recall how long clients wait by default). So would the 4th, 5th, etc.

Either way: So your code would have unlimited connections, because the code is just limiting how many are accepted simultaneously. Once accepted, the connections remain until the server or the client closes the connection.

Updated - in response to OP comments giving more information.

process maintains a persistent connection with an infinite loop – Robert 12 mins ago

Bert F
A: 

A semaphore will not limit number of connections to server.

A semaphore limits the number of threads accessing a particular section of code. Ergo, your server will accept as many incoming socket requests as are made, but each socket thread will block on the semaphore.

Furthermore, your run method contains a loop which is acquiring and releasing the semaphore, which results in all sockets round-robin-ing on the semaphore.

To see this in action, add a few log statements. One before acquiring the semaphore, one more immediately after acquiring it, another before releasing, and a final one immediately after releasing it.

So how does one limit connections to a server?

Check the backlog constructor parameter on ServerSocket

public EServer (int port) 
        throws IOException 
{
    // 'backlog' is in fact a listening queue length. 
    // if more than 2 socket requests are made at a time,
    // they are refused. probably want to parameterize 
    // this :)
    serverSocket = new ServerSocket (port, 2); 
}
johnny g
Backlog does not limit number of connections, it defines the depth of new connections queue. One has to manually count connections and *not* accept a new one until count drops bellow the limit.
Nikolai N Fetissov
are you certain ... [ http://docstore.mik.ua/orelly/java/fclass/ch15_15.htm ] ? "The backlog parameter specifies how many pending connections can be queued by the ServerSocket. Calling accept() removes a pending connection from the queue. *If the queue is full, additional connection requests are refused.*"
johnny g
Yes, I am. Note the *pending* word there. Do a `man listen` on any Unix box, or better pick up a copy of Stevens' UNP book. The backlog parameter is more or less useless and is mostly historical.
Nikolai N Fetissov
you mean like .. [ http://fuse4bsd.creo.hu/localcgi/man-cgi.cgi?listen+2 ] "If a connection request arrives with the queue full the client may receive an error with an indication of ECONNREFUSED, or, in the case of TCP, the connection will be silently dropped." i profess i am not as familiar as you seem to be, but at a certain point, i must accept the documentation of the APIs i consume.
johnny g
@johnny: these are *pending* connection requests that OS has queued and the app has not yet `accept`-ed, not already established connections. Each established connection is a separate socket from the OS point of view.
Nikolai N Fetissov
ding [light bulb] :)
johnny g
You're welcome :)
Nikolai N Fetissov