I have a python2 program which uses socket
and SocketServer
. it basically consists of some clients, that can connect with each other.Each Client also has their own Thread
which is implemented using threading.Thread
and run in daemon
mode. Now my problem is that when i call sys.exit()
the program doesn't exit.I also (try to) close all open sockets before sys.exit()
call.I also use socket.makefile()
in the program, however I call close
on all of the opened files right after I read from them.What could be the potential causes if sys.exit()
not exiting?
Here's the code, the application is supposed to represent a graph of nodes,
each node will contain an Edge to another node if we call initiateConnection
on the first node with the address of the second one.Since the Edges are undirected I want both sides of the socket to be handled by the same RequestHandler
.See notes above Client.initiateConnection
.
import socket
import SocketServer
import select
import sys
import threading
class Client(SocketServer.ThreadingTCPServer):
"""Attributes:
neighbours: connections to neighbours
n_addresses: addresses of Neighbours
requests: Number of Requests Sent
processed_requests:{}
id: this client's id
"""
def __init__(self, server_address, RequestHandlerClass, id):
self.neighbours = []
self.n_addresses = []
self.requests = 0
self.processed_requests = {}
self.id = id
SocketServer.ThreadingTCPServer.__init__(self, server_address, RequestHandlerClass)
def initiateConnection(self, address):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(address)
self.neighbours.append(s)
self.n_addresses.append(s.getpeername())
self.addConnection(s, address)
# check if the given port, and the connected one match
if address[1] != s.getpeername()[1]:
raise Exception(address, s.getpeername())
""" this method is based on the _handle_request_no_block method on the base server
we will add the connection created by this node i.e: this node is the client for that
particular connection, to the list of connections handled by this server, i.e: view it as a server connection
"""
#This method is supposed to add the request first instantiated by
# the Client instance itself, to the requests the ThreadingTCPServer manages (creates a seperate thread for it and etc.)
# Maybe there's something else I have to do, so that the ThreadingTCPServer closes this connection properly?
def addConnection(self, request, address):
if self.verify_request(request, address):
try:
self.process_request(request, address)
except:
self.handle_error(request, address)
self.shutdown_request(request)
def addNeighbour(self, s):
self.neighbours.append(s)
self.n_addresses.append(s.getpeername())
def server_close(self):
print("{}: closing neighbours".format(self.id))
for c in self.neighbours:
print("{}: closing {}".format(c.getpeername()))
c.close()
print("{}: closed all neighbours".format(self.id))
SocketServer.ThreadingTCPServer.server_close(self)
class RequestHandler(SocketServer.StreamRequestHandler):
GET_NEIGHBOURS="1"
EOR="EOR"
# the below function means someone is trying to initiate
# connection, since it will be handled in it's own thread
# we will initiate a loop and wait for a request.
def handle(self):
self.server.addNeighbour(self.request)
while True:
lines = []
select.select([self.rfile], [], [])
print("{}: got request from {}".format(self.server.id, self.request.getpeername()))
while True:
line = self.rfile.readline().strip()
print("line read:\n{}".format(line))
if line == RequestHandler.EOR:
break
elif not bool(line):
continue
lines.append(line)
print("{}: all request lines read:\n{}".format(self.server.id, lines))
# we have a complete request now process it
if lines[1] == self.GET_NEIGHBOURS:
print("{}: request was of type get_neighbours".format(self.server.id))
data = self.processNeighbours(lines)
self.request.sendall("{}\n".format(data))
print("{}: data sent to {}".format(self.server.id, self.request.getpeername()))
class UnknownRequestCode(Exception): pass
if __name__ == '__main__':
def output(s):
print(s)
def getLine():
return sys.stdin.readline()
address = ('', 0)
clients = {}
addresses = {}
while True:
print("commands:\nclose: exit\nnew: make a new client, will prompt for a client id\nshow: show a clients neighbours, will prompt for a client's id\nconnect: will connect a client to another one, will prompt for the ids")
i = getLine().strip()
if i == "close":
for c in clients.values():
c.shutdown()
print("everything shut down")
sys.exit()
if i == "new":
i = getLine(). strip()
client = Client(address, RequestHandler, i)
clients[i] = client
a = client.server_address
addresses[i] = a
output(a)
t = threading.Thread(target=client.serve_forever)
t.daemon = True
t.start()
elif i == "show":
i = getLine().strip()
c = clients[i]
o = c.startSearch()
#output(o)
elif i == "connect":
i = getLine().strip()
c = clients[i]
i = getLine().strip()
a = addresses[i]
print("{}".format(a))
c.initiateConnection(a)
UPDATE: I have identified the (rather obvious) problem, the handle method never exits the loop since the nodes are always supposed to get connected, and wait for each other's request.But I can not check program termination since it gets stuck before the select.select
call.How can I fix that?