Reputation: 3981
Let's say you had a fairly basic client/server code, where each client creates three threads and multiple clients can connect at once. I want the server to wait for an incoming connection, and once it starts getting connections, to run until there are no more threads running, then exit. Code is similar to below. (ie, rather than the server "serve forever," I want it to exit once all the threads finish).
edit: I want the server to wait for incoming connections. Once the connections begin, it should keep accepting connections until there are no threads left running, then exit. These connections will be somewhat sporadic.
import socket
import threading
# Our thread class:
class ClientThread ( threading.Thread ):
# Override Thread's __init__ method to accept the parameters needed:
def __init__ ( self, channel, details ):
self.channel = channel
self.details = details
threading.Thread.__init__ ( self )
def run ( self ):
print 'Received connection:', self.details [ 0 ]
self.channel.send ( 'hello from server' )
for x in xrange ( 10 ):
print self.channel.recv ( 1024 )
self.channel.close()
print 'Closed connection:', self.details [ 0 ]
# Set up the server:
server = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
server.bind ( ( '', 2727 ) )
server.listen ( 5 )
# Have the server serve "forever":
while True:
channel, details = server.accept()
ClientThread ( channel, details ).start()
Upvotes: 0
Views: 1784
Reputation: 92569
As per your comments, what you are looking for is to starting counting connections after the first one is made to the server, and to kill the server once there are no more existing connections.
An immediate problem with your current infinite while loop is that is blocks on each accept()
. So no matter what, it will always be waiting for another connection. You would have to interrupt it from some other thread to get it out of that loop. But another solution would be to make your event loop larger, and the act of accepting a new connection is only one part of it. The loop should also be checking for a condition to exit.
This example is only one possible way. It makes use of a Queue.Queue to coordinate the work counter.
import socket
import threading
import select
from Queue import Queue
class ClientThread ( threading.Thread ):
def __init__ ( self, channel, details, queue=None ):
self.channel = channel
self.details = details
self.queue = queue
threading.Thread.__init__ ( self )
def run ( self ):
if self.queue:
self.queue.put(1)
print 'Received connection:', self.details [ 0 ]
self.channel.send ( 'hello from server' )
for x in xrange ( 10 ):
print self.channel.recv ( 1024 )
self.channel.close()
print 'Closed connection:', self.details [ 0 ]
if self.queue:
self.queue.get_nowait()
# Set up the server:
server = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
server.bind ( ( '', 2727 ) )
server.listen ( 5 )
rlist = [server]
work_queue = Queue()
def accept_client():
channel, details = server.accept()
ClientThread ( channel, details, work_queue ).start()
accept_client()
while not work_queue.empty():
server_ready, _, _ = select.select(rlist,[],[], .25)
if server in server_ready:
accept_client()
print "Shutting down"
server.close()
print "Exiting"
We use select.select as a simple way to detect activity on your server socket, but also with a timeout. If the server is ready, then we accept the new connection. If it reaches the .25
second timeout, we just loop again and wait.
You will see that we create a queue and constantly check if its empty. The queue is passed into each thread. When a thread is started, it logs some work into the queue. The data is arbitrary. Just a flag. When the thread has completed, it will clear that item from the queue. The result is that after the first connection is received, the queue is no longer empty, and the loop will keep running. If at any point the queue becomes empty (because all current threads have finished), the loop will break and the server will shut down.
Upvotes: 1
Reputation: 500357
If you break
from the while
loop, the process will wait until all the ClientThreads
exit, and will then terminate.
This will work because the client threads are non-daemon.
See threading.Thread.daemon
for further details on this.
Upvotes: 0