I would better start the question from the code.
from multiprocessing import Process, Event, Queue
from threading import Timer
from Queue import Empty
class MyServer(Process):
def __init__(self, port, queue):
Process.__init__(self)
self.port = port
self.queue = queue
self.sd = None
def run(self):
try:
self.start_serving()
except KeyboardInterrupt:
print("Shutting down..")
finally:
if self.sd is not None:
self.sd.close()
def start_serving(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sd = s
try:
s.bind(('', self.port))
s.listen(1)
while True:
conn, addr = s.accept()
while True:
# I dont want to bore you with excess code
# just recv data from clients
try:
msg = self.queue.get_nowait()
# here i start Timer with delay from message (I'll describe Message class below)
Timer(msg.delay, self.response_handler, args=(conn, msg)).start()
except Empty:
pass
conn.close()
finally:
s.close()
def response_handler(self, sd, msg):
# doesn't matter
# and now I want to terminate the MyServer instance
if msg.terminate:
# the problem is here. Lets call it 'problem line'
sys.exit()
msg is instance of Message class which is:
class Message(object):
def __init__(self, port, delay, values, terminate=False):
self.port = port
self.delay = delay
self.values = values
self.terminate = terminate
The logic is I get data from clients via TCP connection and check Queue for message. Messages are things to control the server. Sometimes I get a message like "wait 3 seconds and terminate the server".
What I have done so far.
Call self.terminate() at the problem line. I get
AttributeError: 'NoneType' object has no attribute 'terminate'
Raise an exception at the problem line. I assumed the exception was caught in run() function. I was
wrong
Call sys.exit(). It doesn't work too.
Perhaps my question can be shorter. How to terminate the process from its thread in Python?
Why don't you use multiprocessing.Event (you are already importing it) and exit the process gracefully if you get an terminate message.
To do this add this to __init__:
self.exit = Event()
And change the two while loops:
while True:
conn, addr = s.accept()
while True:
#...
to
while not self.exit.is_set():
conn, addr = s.accept()
while not self.exit.is_set()
#...
then in your response handler:
if msg.terminate:
self.exit.set()
this will allow the code to naturally exit the loops, ensuring that conn.close() is called.
Related
I am trying to close the socket and terminate the thread when shutdown handler callback is executed (I send SIGINT signal by CTRL+C)
main
from example import Example
if __name__ == '__main__':
exmpl = Example()
success = exmpl.connect('', 2000)
if success:
rospy.on_shutdown(exmpl.shutdown_handler)
rospy.spin()
imported class
import socket
import threading
class Example(object):
def connect(self, host='', port=2000):
try:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.settimeout(10)
self.sock.bind((host, port))
self.sock.listen(1)
self.conn, self.addr = self.sock.accept()
self.sock.settimeout(None)
self.t = threading.Thread(target=self.recv_msg)
self.t.start()
return True
except Exception as e:
return False
except Exception as timeout:
return False
def recv_msg(self):
while True:
recv_msg = self.conn.recv(1024)
print(recv_msg)
def shutdown_handler(self):
try:
self.sock.close()
self.t.join()
except Exception as e:
print(e)
In this application recv_msg is always executing because data are coming all the time. So, when I send SIGINT, shutdown handler starts and only executes the statement sock.close() but not t.join() and thread never ends
I want to implement a streaming server which sends and endless stream of data to all connected clients. Multiple clients should be able to connect and disconnect from the server in order to process the data in different ways.
Each client is served by a dedicated ClientThread, which sub-classes Thread and contains a queue of the data to be sent to the client (necessary, since clients might process data at different speeds and because bursts of data can occur which the clients might be unable to handle).
The program listens to incoming client connections via a seperate ClientHandlerThread. Whenever a client connects, the ClientHandlerThread spawns a ClientThread and adds it to a list.
As a dummy example, the main Thread increments an integer each second and pushes it to all ClientThread queues through ClientHandlerThread.push_item().
Every 10 increments, the number of items in the client queues is printed.
Now to my questions:
When a client disconnects, the Thread terminates and no more data is send, however, the ClientThread object remains in the ClientHandlerThreads list of clients and items are continuously pushed to its queue.
I'm therefore looking for either (1) a way to delete the ClientThread object from the list whenever a client disconnects, (2) a better way to monitor the ClientThreads than a list or (3) a different (better) architecture to archive my goal.
Many thanks!
Server
import socket
import time
from threading import Thread
from queue import Queue
class ClientThread(Thread):
def __init__(self, conn, client_addr):
Thread.__init__(self)
self.queue = Queue()
self.conn = conn
self.client_addr = client_addr
def run(self):
print('Client connected')
while True:
try:
self.conn.sendall(self.queue.get().encode('utf-8'))
time.sleep(1)
except BrokenPipeError:
print('Client disconnected')
break
class ClientHandlerThread(Thread):
def __init__(self):
Thread.__init__(self, daemon = True)
self.clients = list()
def push_item(self, item):
for client in self.clients:
client.queue.put(str(i))
def run(self):
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
s.bind('./socket')
s.listen()
i = 1
while True:
conn, client_addr = s.accept()
client = ClientThread(conn, client_addr)
client.start()
self.clients.append(client)
i += 1
if __name__ == '__main__':
client_handler = ClientHandlerThread()
client_handler.start()
i = 1
while True:
client_handler.push_item(str(i))
if i % 10 == 0:
print('[' + ', '.join([str(client.queue.qsize()) for client in client_handler.clients]) + ']')
i += 1
time.sleep(1)
Client:
import socket
if __name__ == '__main__':
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
s.connect('./socket')
print('Connected to server')
while True:
data = s.recv(1024)
if not data:
print('Disconnected from server')
break
print(data.decode('utf-8'))
Note You should probably read up on things like aiohttp for much more scalable versions to your answer.
For your question, you can make a few changes to achieve this:
First, change ClientThread's constructor:
class ClientThread(Thread):
def __init__(self, client_handler, conn, client_addr):
self.client_handler = client_handler
self.running = True
...
When the handler creates the object, it should pass self to it as client_handler.
In the run method, use
def run(self):
while True:
...
self.running = False
self.client_handler.purge()
That is, it marks itself as not running, and calls the purge method of handler. This can be written as
class ClientHandlerThread(Thread):
...
def purge(self):
self.clients = [c for c in self.clients if c.running]
I have a server with main process acepting socket connections and put them in Queue stack and another process monitoring this stack and applying it to pool processes handling connections. All works fine except for one thing:
last connection allways at stuck until another connection appears, it's look like last connection can't be closed, but why?
from multiprocessing import Queue, Process, Pool, Manager
import datetime
import socket
def get_date():
return datetime.datetime.now().strftime('%H:%M:%S')
class Server:
def __init__(self, host, port):
self.server_address = host, port
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def run(self):
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind(self.server_address)
self.server_socket.listen(1)
print('listen at: %s:%s' % self.server_address)
q = Manager().Queue()
Process(target=self.handle_request, args=(q,)).start()
while True:
client_socket, adress = self.server_socket.accept()
print('\n[%s] request from: %s:%s' % (get_date(), *adress))
q.put(client_socket)
client_socket.close()
del client_socket # client_socket.close() not working
def help(self, client_socket):
data = client_socket.recv(512)
client_socket.send(data)
client_socket.close()
print(data[:50])
def handle_request(self, q):
with Pool(processes=2) as pool:
while True:
pool.apply_async(self.help, (q.get(),))
Server('localhost', 8000).run()
close doesn't realy close connection unless no other process holding a reference, but shutdown will affect all processes. you could call client_socket.shutdown(socket.SHUT_WR) before client_socket.close().
Update:
the reason close doesn't fully close connection is there is a process started by Manager() is holding a reference. Use Queue instead would make close works as you expected.
I made a Client socket object, which I instantiate and it keeps alive a connection with the server, which is working fine, but I'm wondering if there is a way to call the socket.send event from outside the instance. I was about to make a stack for the messages and check the stack in the while loop and if it's not empty then send the oldest data to the server, which would be just fine for me, but my problem is that the stack only updates after the while loop(I tried breaking out, then it updated).
So my question would be, is there a way to update the global stack simultaneously with the while loop running? Or is there any other way to call the socket.send event outside the object?
import socket
import sys
import select
import threading
SERVER_IP = '192.168.1.4'
PORT = 8686
TIMEOUT = 5
BUF_SIZE = 1024
MESSAGES = ['testdata1', 'testdata2']
class Client(threading.Thread):
def __init__(self, host=SERVER_IP, port=PORT):
threading.Thread.__init__(self)
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock = socket.create_connection((host, port), 1)
self.sock.setblocking(0)
while 1:
try:
global MESSAGES
ready = select.select([self.sock], [], [], TIMEOUT*1000)
if ready[0]:
buf = self.sock.recv(BUF_SIZE)
print buf
#TODO:do stuff with buf
print 'messages left:'+str(len(MESSAGES))
if len(MESSAGES)>0:
self.sock.send(MESSAGES.pop())
except KeyboardInterrupt:
self.sock.close()
sys.exit(1)
except Exception, e:
print '\n[ERR] %s' % e
self.sock.close()
sys.exit(1)
def run(self):
pass
def sendData(self, data):
global MESSAGES
print 'appending data:%s' % data
MESSAGES.append(data)
def main():
client = Client()
client.start()
client.sendData("test1")
client.sendData("test2")
client.sendData("test3")
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
sys.exit(1)
Client.__init__() does not return because it enters an infinite while loop. Hence control is never returned to the main thread, and the Client thread is not actually started.
Instead you should move the while loop into the run() method. Then the __init__() method will return control to the main thread, which can then start the thread, and request that the client send messages via sendData().
class Client(threading.Thread):
def __init__(self, host=SERVER_IP, port=PORT):
threading.Thread.__init__(self)
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock = socket.create_connection((host, port), 1)
self.sock.setblocking(0)
def run(self):
while 1:
try:
global MESSAGES
ready = select.select([self.sock], [], [], TIMEOUT*1000)
if ready[0]:
buf = self.sock.recv(BUF_SIZE)
print buf
#TODO:do stuff with buf
print 'messages left:'+str(len(MESSAGES))
if len(MESSAGES)>0:
self.sock.send(MESSAGES.pop())
except KeyboardInterrupt:
self.sock.close()
sys.exit(1)
except Exception, e:
print '\n[ERR] %s' % e
self.sock.close()
sys.exit(1)
def sendData(self, data):
global MESSAGES
print 'appending data:%s' % data
MESSAGES.append(data)
Instead of using the global MESSAGES list you should probably create a Queue for communicating between the main thread and the worker thread(s), particularly if more than one worker thread is running. Something like this (untested!):
import Queue
class Client(threading.Thread):
def __init__(self, msg_queue, host=SERVER_IP, port=PORT):
threading.Thread.__init__(self)
self.msg_queue = msg_queue
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock = socket.create_connection((host, port), 1)
self.sock.setblocking(0)
def run(self):
while 1:
try:
ready = select.select([self.sock], [], [], TIMEOUT*1000)
if ready[0]:
buf = self.sock.recv(BUF_SIZE)
print buf
#TODO:do stuff with buf
print 'messages left:'+ str(self.msg_queue.qsize())
try:
msg = self.msg_queue.get_nowait()
self.sock.send(msg)
except Queue.Empty:
pass
except KeyboardInterrupt:
self.sock.close()
sys.exit(1)
except Exception, e:
print '\n[ERR] %s' % e
self.sock.close()
sys.exit(1)
def main():
# create a queue and pass it to the client
msg_queue = Queue.Queue()
client = Client(msg_queue)
client.start()
msg_queue.put("test1")
msg_queue.put("test2")
msg_queue.put("test3")
The thing should work if you move your loop from
__init__() into run()
method instead.
Your thread is not a thread this way, process blocks at client = Client(...).
Why do you mix select and threads? Is this really necessary? If you want asynchronous sending and receiving without threads use asyncore module.
Or remove select from your code. The socket.recv() will block until it receives data in blocking mode, but as this is a thread, I don't see anything wrong about that. If in nonblocking mode, recv() will just return None if there is no data to receive if I remember correctly. So you don't really need select. Just check if recv() returned None. If it does, sleep some time before trying again.
The way you did it troubles your OS twice. Once for reading a socket, and second time to get the status of a socket where timeout is used to simulate sleep() more than anything else. Then the loop checks again making select() system call right after timeout confirmed that there is nothing to do for that socket.
I wrote this script to test socket behavior in Windows, and I'm not sure why it hangs in Windows and not in Ubuntu. The script makes three listening sockets bound to '127.0.0.1', and it makes 60 threads which each connect to the listening sockets 10 times, 20 threads per listening socket.
import threading
import socket
import logging
import os
ports = [60003, 60004, 60005]
class ServerTest(threading.Thread):
log_lock = threading.Lock()
def __init__(self, port):
super(ServerTest, self).__init__(name=('socktest_%d'%port))
self.port = port
self._init_logger()
def _init_logger(self):
self.logger = logging.getLogger(self.name)
handler = logging.FileHandler('socktest.log')
formatter = logging.Formatter(
'%(levelname)s -- %(asctime)s:\n%(message)s',
datefmt='%m/%d/%Y %I:%M:%S %a')
handler.setFormatter(formatter)
handler.setLevel(logging.INFO)
self.logger.addHandler(handler)
self.logger.setLevel(logging.INFO)
def log(self, junk):
self.log_lock.acquire()
if isinstance(junk, Exception):
self.logger.exception(junk)
else:
self.logger.info(str(junk))
self.log_lock.release()
def run(self):
try:
listener = socket.socket()
listener.bind(('127.0.0.1', self.port))
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listener.listen(100)
except Exeption as exc:
self.log(exc)
return
while True:
try:
c, a = listener.accept()
self.log('accepted connection from '+str(a)+' to '+self.name)
data = c.recv(4096)
if data == 'stop':
break
self.log('data:\n'+data)
c.sendall(data)
c.close()
except Exception as exc:
self.log(exc)
listener.close()
class ClientTest(threading.Thread):
def __init__(self, port):
super(ClientTest, self).__init__()
self.port = port
def run(self):
try:
for i in range(10):
c = socket.create_connection(('127.0.0.1', self.port))
data = os.urandom(256)
c.sendall(data)
c.recv(256)
c.close()
except Exception as exc:
return
def main():
print 'Starting test'
server_threads = [ServerTest(p) for p in ports]
for thread in server_threads:
thread.start()
print 'started thread', thread
client_threads = []
for p in ports:
for i in range(20):
client_threads.append(ClientTest(p))
for thread in client_threads:
thread.start()
print 'started thread', thread
for thread in client_threads:
thread.join()
print 'joined thread', thread
for p in ports:
c = socket.create_connection(('127.0.0.1', p))
c.sendall('stop')
c.close()
for thread in server_threads:
thread.join()
print 'joined thread', thread
print 'Finished'
if __name__ == '__main__':
main()
I've tried it with and without the line listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) and the behavior is the same. I also tried it with an without the lock and the behavior was still the same.
EDIT
I forgot to mention that it does print out all the the threads, it seems to be stuck in the thread.join() for the client_threads, but I don't know why.
My psychic powers tell me os.urandom() can block on Windows if there is insufficient entropy available (it should never block on Unix). I haven't been able to confirm this one way or the other via MSDN, but given the reliance on hardware entropy (unlike, say, FreeBSD, which uses a pure software implementation), I don't think blocking is entirely inconceivable. Try replacing the os.urandom(256) call with (say) b'\x00' * 256.