I have a client server code in python, wherein the client queries the server about a process running on the server and provides the memory threshold. If the process consumes more memory than the threshold,the server kills the process and restarts it. The problem is since I am creating a new thread for each client, the thread after servicing the client should terminate itself, releasing the connections and restart the killed process. When I am running only one thread(i.e. ONLY ONE CLIENT),everything works fine. But when I am running multiple clients,only one of them releases the connection and restarts the process while the other threads only restart the process but do not connections. I am calling the Popen.subprocess() module inside the threads. Is that a reason or there is some other reason for that?
Here is my code:
from thread import *
import threading
import time
import psutil
import itertools
import ctypes
import string
import os
import sys
import socket
import subprocess
from datetime import datetime
def drives():
drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
return list(itertools.compress(string.ascii_uppercase,map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
t=0
c=drives()
o=[]
while(t<len(c)):
o.append(str(c[t]+':\\'))
t=t+1
class procThread(threading.Thread):
def __init__(self,conn,addr):
threading.Thread.__init__(self)
self.conn=conn
self.addr=addr
def run(self): # prints the process's info which match the keyword....... SERVER SIDE
self.conn.send('Welcome to the server\n')
name=[]
global o
m=0
k=0
self.conn.send("Enter the key...\n") # for authentication purposes...e.g.here the key is 1
t=self.conn.recv(8)
if(t!='1'): #WRONG KEY....INVALID USER
self.conn.send("\nInvalid key..teminating the connection.....ABORT\n")
self.conn.close()
else:
fp=open("processlogs.txt","a")
r=""
self.conn.send("\nEnter the process keyword ..Press # @ the end ") # e.g. 'Sk' for Skype
d=self.conn.recv(65536)
while(d!='#'):
r=r+str(d)
d=self.conn.recv(65536)
for p in psutil.pids(): # iterates through all the pids of the processes obtained
try:
p1=psutil.Process(p)
if(r in p1.name()):
p2=p1.get_memory_info()
t=p1.name()+' '
d=str(p)+' '
self.conn.send(d)# prints the pid of the process
self.conn.send(t),# prints the name of the process
d=str(p2[0]/(1024*1024))+' '
self.conn.send(d) # print memory in MB
self.conn.send('MB\t')
for connect in p1.connections(kind='tcp'):
d=str(connect.laddr[0])+' '
self.conn.send(d) # prints ip
d=str(connect.laddr[1])+' '
self.conn.send(d) # prints tcp ports
except psutil.AccessDenied: # won't show all the processes
pass
except psutil.NoSuchProcess:
pass
else:
continue
self.conn.send(" Enter the threshold...(in MB)(Press # at the end) ")
d=""
t=self.conn.recv(65536).decode('utf-8')
while(t!='#'):
d=d+str(t)
t=self.conn.recv(65536).decode('utf-8')
names=[] # LIST OF PROCESSES TO BE KILLED...#A RECORD IS KEPT SO THAT THEY CAN BE RESTARTED......
for p in psutil.pids():
try:
p1=psutil.Process(p)
if(r in p1.name()):
if((p2[0]/(1024*1024))>=int(d)):
fp.write(str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
fp.write("|")
fp.write(str(self.addr))
m=p1.name()
m=m.encode('ascii','ignore') # converting unicode object into string object
for l in o:
f=0
for root, dirs, files in os.walk(l): # walks through the entire file system of the Windows OS
for name in files:
if name==m:
p1.kill()
self.conn.send(" Finally the process is killed...... ")
f=1
self.conn.send(str(os.path.abspath(os.path.join(root,name))))
fp.write("|")
fp.write(str(os.path.abspath(os.path.join(root,name)))+'\n')
names.append(os.path.abspath(os.path.join(root,name)))
break
if(f==1):
break
if(f==1):
break
except psutil.AccessDenied:
pass
except psutil.NoSuchProcess:
pass
else:
continue
self.conn.send(" Now the processes will be restarted after the connection is terminated......" )
fp.close()
self.conn.close() # closes the connection...
if(names):
for l in names:
subprocess.Popen(str(l))
class serverThread(threading.Thread): # Thread class for Server(FOR LISTENING)
def __init__(self, threadID, name,):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
def run(self):
threadLock.acquire()
host=raw_input("Enter the hostname.....")
HOST=socket.gethostbyname(host)
PORT=input("Enter the port no......") # specific port available
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket created'
#Bind socket to local host and port
try:
s.bind((HOST, PORT))
except socket.error as msg:
print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
sys.exit()
print 'Socket bind complete'
s.listen(10) # no of connections @ one time
print self.name+' now listening'
threadLock.release()
while 1:
conn, addr = s.accept() # connection gets established here..
print 'Connected with ' + addr[0] + ':' + str(addr[1])
conn.send('Thank you for connecting ')
procs=procThread(conn,addr)
procs.start()
#start_new_thread(proc_info,(conn,addr))
s.close()
print("WELCOME")
print(" server-client")
threadLock=threading.Lock()
thread1 =serverThread(1,"Server 1")
thread1.start() # starting the server thread...
The issue could be that some exception is getting thrown before the process can close the connection. You should try the below suggestion and check if that is the case.
You should make it a habit to write the code to close connections in a finally block, something like -
This would make sure that even if there are some errors/exceptions thrown from the code before conn.close() is called, it is still executed. Also, a good practice to keep these kinds of sanitary code (cleanup code) at the end of the function/script , so that they are executed at end , though that may not always be applicable based on the requirement.