On Windows in particular, when a client is opening and closing many sockets very quickly (on the order of one opened per 8ms) the server quickly runs out of ports, and connections start to fail.
One solution could be to have a single connection per-client, and it is kept alive on the server by a thread that is always servicing that client. This would mean removing the threadpool the server uses to handle client messages, and instead each accept() call spawns a thread that is kept alive as long as the client is still sending data. But, in a large deployment, this would mean thousands of threads (and sockets) always open on the server, which might cause other forms of exhaustion.
This might not be such an issue, however, since Linux doesn't have this problem, and the most-likely deployment of this software is on a Linux system. It can also be somewhat avoided by using Async mode (-a) on the client, since it slows down the rate at which it reports work, by giving it bigger work chunks.
On Windows in particular, when a client is opening and closing many sockets very quickly (on the order of one opened per 8ms) the server quickly runs out of ports, and connections start to fail.
One solution could be to have a single connection per-client, and it is kept alive on the server by a thread that is always servicing that client. This would mean removing the threadpool the server uses to handle client messages, and instead each accept() call spawns a thread that is kept alive as long as the client is still sending data. But, in a large deployment, this would mean thousands of threads (and sockets) always open on the server, which might cause other forms of exhaustion.
This might not be such an issue, however, since Linux doesn't have this problem, and the most-likely deployment of this software is on a Linux system. It can also be somewhat avoided by using Async mode (-a) on the client, since it slows down the rate at which it reports work, by giving it bigger work chunks.