I'm using Python 2.7.
class Client():
def __init__(self, host, server_port):
"""
This method is run when creating a new Client object
"""
self.host = 'localhost'
self.server_Port = 1337
# Set up the socket connection to the server
self.connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.receiver = None
self.myParser = MessageParser()
# TODO: Finish init process with necessary code
self.run()
def run(self):
self.connection.connect((self.host, self.server_Port))
self.receiver = MessageReceiver(self, self.connection) #On this line, a MessageReceiver object is instantiated.
self.take_input()
class MessageReceiver(Thread):
def __init__(self, client, connection):
super(MessageReceiver, self).__init__()
self.myClient = client
self.connection = connection
self.daemon = True
self.run()
def run(self):
self.myClient.receive_message(self.connection.recv(1024)) #This line blocks further progress in the code.
When the run-method in the Client object instantiates a MessageReceiver object, I want the next line of code in Client to be executed immediately, without waiting for an exit code from MessageReceiver. Is there a way to do this?
self.run()
Call start() instead. run() executes the run method in the current thread. start() spins up another thread and calls it there.
self.start()
Related
My process is using asyncio and socket to communicate with other processes. It handles one client process connection perfectly, but when a second client tries to connect, it waits forever at the client's connect_ex method and the server's sock_accept method.
The flow is this: Process #1 spawns a worker process which creates a socket server. Process #1 connects and communicates with worker process. When/if process #1 dies, process #2 tries to connect with worker process. Process #2 can't connect.
# process #1
class Communicator:
def __init__(self, ..., port):
...
self.port = port
self.loop = asyncio.get_event_loop()
async def connect(self):
self.psocket = socket.socket(socket.AF_INET, (socket.SOCK_STREAM | socket.SOCK_NONBLOCK))
ex = 1
while ex:
ex = self.psocket.connect_ex(('localhost', self.port))
self.psocket.setblocking(False)
self.listen_task = self.loop.create_task(self.listen())
self.emit_task = self.loop.create_task(self.emit())
print('Connected on port', self.port)
async def listen(self):
...
async def emit(self):
...
# worker process
class Communicator(threading.Thread):
def __init__(self, ..., port):
...
self.port = port
super().__init__()
def set_event_loop(self):
try:
self.loop = asyncio.get_event_loop()
except RuntimeError:
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
def run(self):
self.set_event_loop()
self.psocket = socket.socket(socket.AF_INET, (socket.SOCK_STREAM | socket.SOCK_NONBLOCK))
self.psocket.bind(('localhost', self.port))
self.psocket.listen()
self.psocket.setblocking(False)
self.accept_task = self.loop.create_task(self.accept())
pending = asyncio.all_tasks(loop=self.loop)
self.loop.run_until_complete(asyncio.gather(*pending))
async def accept(self):
while True:
connection, addr = await self.loop.sock_accept(self.psocket)
self.tasks.append({
'listener': self.loop.create_task(self.listen(connection)),
'emitter': self.loop.create_task(self.emit(connection)),
})
async def listen(self, connection):
...
async def emit(self, connection):
...
I know how to do this with threading, I only want to use asynchronous methods for handling multiple client connections.
Also connect_ex blocks when the second process tries to connects. The first process runs through the connect_ex loop many times before connecting.
What's causing connect_ex to block and sock_accept to wait forever?
I have a listbox that, when server accepts a connection, should display the names of the clients. The server code is as follows:
class GUI2(GUI): #server GUI
def __init__(self):
self.clientlist = tk.Listbox(self.clientframe) #listbox that should display client names
self.clientlist.pack(expand = 1)
self.s = INITSERVER()
self.process = Process(target = self.s.startChat) #prevents the GUI from freezing due to server running
self.process.start()
class INITSERVER(GUI2):
def startChat(self): #starts the server
print("server is working on " + self.SERVER)
self.server.listen(30) #sets max number to only 30 clients
while True:
self.conn, self.addr = self.server.accept()
self.name = self.conn.recv(1024).decode(self.FORMAT)
self.clientlist.insert("end", self.name) #append client names to listbox supposedly
print(f"Name is :{self.name}")
The client code is as follows:
class INITCLIENT(GUI3): #GUI3 is client GUI; calls INITCLIENT when done packing
def __init__(self):
self.PORT = 5000
self.SERVER = "" #left blank for this post; contains the server's exact address
self.ADDRESS = (self.SERVER, self.PORT)
self.FORMAT = "utf-8"
self.client = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
self.client.connect(self.ADDRESS)
self.name = g.entergname.get() # g = GUI() i.e. root window; entergname is Entry widget where client inputs their names
self.client.send(self.name.encode(self.FORMAT)) #sends inputted names to INITSERVER to display in listbox.... supposedly
Through VS Code, I run the server first, then join the server using another terminal; the problem happens next.
Process Process-1:
Traceback (most recent call last):
File "F:\Program Files (x86)\Python\lib\multiprocessing\process.py", line 314, in _bootstrap
self.run()
File "F:\Program Files (x86)\Python\lib\multiprocessing\process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File "f:\project\mainmenu.py", line 341, in startChat
self.clientlist.insert("end", self.name) #append client names to listbox
AttributeError: 'INITSERVER' object has no attribute 'clientlist'`
I tried replacing self.clientlist.insert to super().clientlist.insert but the same error pops up with `AttributeError: 'super' object has no attribute 'clientlist'
Any help in fixing the error, or in pointing me to the right direction is greatly appreciated.
EDIT: So after countless trial and error, I think the error is caused by the duplicate/child processes not knowing what is self.clientlist because they don't know that self (i.e. INITSERVER) is a child of GUI2; Process doesn't duplicate the parent attributes, only the ones within the function of Startchat().
Is there a way to restructure the code so that the clients' names can be displayed through listbox? Or is what I'm doing not compatible with Python and I have to display it in some other way?
Thanks to #acw1668 I was guided to the answer: I just needed to remove the INITSERVER class and move all of its functions and attributes to GUI2 class, then use Threading instead of Process to target startChat to bypass Tkinter pickle errors. The new code is as follows:
class GUI2(GUI): #server GUI
def __init__(self):
self.clientlist = tk.Listbox(self.clientframe) #listbox that should display client names
self.clientlist.pack(expand = 1)
self.thread = Thread(target = self.startChat) #prevents the GUI from freezing due to server running
self.thread.start()
def startChat(self):
if (self.checksignal == 0): #custom-made stop signal for stopping the thread
print("server is working on " + self.SERVER)
self.server.listen(30)
while True:
self.conn, self.addr = self.server.accept()
self.name = self.conn.recv(1024).decode(self.FORMAT)
self.clientlist.insert("end", self.name) #append client names to listbox
else:
return
I'm wondering how to correctly create background thread that would be listenning some random port and pushing received object to Queue?
I want my socket wrapper to launch new thread, select some random port and start listenning on in. I have to be able to get this port number from socket wrapper.
I've come up with simple class:
class SocketWrapper(Thread):
def __init__(self, socket_type, *args, **kwargs):
super(Thread, self).__init__(*args, **kwargs)
self._ctx = zmq.Context()
self._socket = self._ctx._socket(socket_type)
self.port = self._socket.bind_to_random_port('tcp://*')
self._queue = Queue()
def run(self):
while not self.stop_requested:
try:
item = socket.recv_pyobj(flags=zmq.NOBLOCK)
self._queue.put(item)
except ZMQError:
time.sleep(0.01) # Wait a little for next item to arrive
However, zmq sockets can't be shared between threads, they are not thread-safe (http://api.zeromq.org/2-1:zmq). So socket creation and binding should be moved to run() method:
class SocketWrapper2(Thread):
def __init__(self, socket_type, *args, **kwargs):
super(Thread, self).__init__(*args, **kwargs)
self._socket_type = socket_type
self._ctx = zmq.Context()
self._queue = Queue()
self._event = Event()
def run(self):
socket = self._ctx._socket(self._socket_type)
self.port = self._socket.bind_to_random_port('tcp://*')
self._event.set()
while not self.stop_requested:
try:
item = socket.recv_pyobj(flags=zmq.NOBLOCK)
self._queue.put(item)
except ZMQError:
time.sleep(0.01) # Wait a little for next item to arrive
def get_port(self):
self._event.wait()
return self.port
I had to add event to be sure that port is already binded before I can read it but it introduces risk of deadlock, when SocketWrapper2.get_port() is called before start(). This can be avoided by using Thread's _started Event:
def get_port(self):
if not self._started.is_set():
raise RuntimeError("You can't call run_port before thread start.")
self._event.wait()
return self.port
Is this is at last thread-safe? Is there anything else to take care of?
Problem I still see here is that I want to get port right after SocketWrapper is created. Can I safely call Thread's start() in __init__?
I ended up modifying this solution a little to avoid deadlocking main thread:
def get_port(self):
if not self._started.is_set():
raise RuntimeError("You can't call run_port before thread start.")
if not self._event.wait(1):
raise RuntimeError("Couldn't get port after a while.")
return self.port
This is not perfect. Since we delay get_port but it's simple and do the job. Any suggestions how to improve it?
I have a problem concerning threading in python (2.7.8). The problem resides in a python "chat" code written (I'll include the code to my threading class and my client class (different python files), since I think the problem is in one of those two, and not in the server code). When I run the Client.py file, I am able to communicate with another client (running the same python code) through a server, but the problem is that I have to refresh the .send_payload(msg) command in order to receive the message that the other client has sent (or simply by pressing enter in the chat, and hence sending "" as message). I want to know if it is possible to receive messages without "refreshing" the chat, somehow through threading.
class MessageReceiver(Thread):
def __init(self,client,connection):
Thread.__init__(self)
self.daemon = True
self.client = client
self.connection = connection
self.stop = False
def run(self):
while not self.stop:
data = self.connection.recv(8192)
if not data:
break
else:
self.client.receive_message(data)
pass
class Client:
def __init__(self, host, server_port):
self.host = host
self.server_port = server_port
self.connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.run()
def run(self):
self.connection.connect((self.host, self.server_port))
self.thread = MessageReceiver(self, self.connection)
self.thread.start()
while True:
text = raw_input()
if(text.find("login") == 0):
data={"request":"login","content":text[text.find("login")+6:]}
self.send_payload(data)
if(text.find("names") == 0):
data={"request":"names","content":""}
self.send_payload(data)
else:
data={"request":"msg","content":text}
self.send_payload(data)
def disconnect(self):
self.thread.stop = True
self.connection.close()
print("Disconnected")
def receive_message(self, message):
print("Msg: " + message)
def send_payload(self, data):
self.connection.send(json.dumps(data))
print("Payload sent!")
Short Question
Using my examples below, is there a Pythonic way to share my_object's actual instance with with the BaseRequestHandler class?
Background
By definition, the BaseRequestHandler class creates a new instance for each request. Because of this, I am struggling to try find a solution on how to get data from the handle() function back to the ProtocolInterface instance. Note that this might be the wrong approach if I am needing to do something in handle() other than print to stdout.
At this point in time, I do not believe that global variables will work because my_object is passed in and is expected to change often (this is why handle() needs to see it. To see an example client (sending bogus data) see my other SO question. I think the biggest issue I am facing is the the socketservers are running in a background thread.
Example of what I would like to do
class ProtocolHandler(SocketServer.BaseRequestHandler):
def handle(self):
while(1):
self.data = self.request.recv(1024)
if self.data == '':
break
self.request.send("Success")
self.my_object.success = True# <--- How can I share my_object's instance?
class ProtocolInterface():
def __init__(self, obj, host='127.0.0.1', port=8000, single_connection=False):
self.my_object = obj # <--- This ideally is the same instance seen in ProtocolHandler
self.host = host
self.port = port
# Create the socket server to process in coming traffic
if(single_connection):
self.server = SocketServer.TCPServer((self.host, self.port), ProtocolHandler)
else:
self.server = SocketServer.ThreadingTCPServer((self.host, self.port), ProtocolHandler)
def start(self):
print "Server Starting on HOST: " + self.host
server_thread = threading.Thread(target=self.server.serve_forever)
server_thread.daemon = True
server_thread.start()
You could pass the object through the server instance:
self.server = SocketServer.TCPServer((self.host, self.port), ProtocolHandler)
self.server.my_object = self.my_object
The documentation indicates that you can have access to the server instance in handle() as self.server.