Connecting a PyQt5 application to another python program - python

I have separately developed programs for a pyqt5 app and a client for communicate with a server.
GUI:
from PyQt5 import QtCore, QtWidgets, QtGui
from screen_stack import Ui_MainWindow_screenstack
from screen1 import Ui_Form_screen1
from screen2 import Ui_Form_screen2
from screen3 import Ui_Form_screen3
from client_cli_oop_with_exception_printing import mainProg
import socket
import threading #maybe this won't useful
import sys
import os
class MainApp(QtWidgets.QMainWindow):
def __init__(self):
super(MainApp, self).__init__() #if init was provided with paren=None, then inside this init parent argument can be passed
self.screenstack = Ui_MainWindow_screenstack()
self.screenstack.setupUi(self)
self.screen1 = scr1()
self.screen2 = scr2()
self.screen3 = scr3()
self.screenstack.stackedWidget.addWidget(self.screen1)
self.screenstack.stackedWidget.addWidget(self.screen2)
self.screenstack.stackedWidget.addWidget(self.screen3)
self.screenstack.stackedWidget.setCurrentWidget(self.screen1)
# self.screen1.s1.connect_tcp.clicked.connect(
# lambda: self.screenstack.stackedWidget.setCurrentWidget(self.screen2)
# )
self.screen1.s1.connect_tcp.clicked.connect(self.init_connect_server)
self.screen2.s2.register_name.clicked.connect(
lambda: self.screenstack.stackedWidget.setCurrentWidget(self.screen3)
)
self.sock = None
self.errScr1 = QtWidgets.QMessageBox()
self.errScr1.setWindowTitle('Error')
def init_connect_server(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.sock.connect((self.screen1.s1.tcp_link.text(), int(self.screen1.s1.tcp_port.text())))
self.screenstack.stackedWidget.setCurrentWidget(self.screen2)
except Exception as e:
self.errScr1.setText(str(e))
self.errScr1.exec_()
def start_client_threads(self):
#all the communication threads should start here..
pass
class scr1(QtWidgets.QWidget):
def __init__(self):
super(scr1,self).__init__()
self.s1=Ui_Form_screen1()
self.s1.setupUi(self)
class scr2(QtWidgets.QWidget):
def __init__(self):
super(scr2,self).__init__()
self.s2=Ui_Form_screen2()
self.s2.setupUi(self)
class scr3(QtWidgets.QWidget):
def __init__(self):
super(scr3,self).__init__()
self.s3=Ui_Form_screen3()
self.s3.setupUi(self)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
sw = MainApp()
sw.show()
sys.exit(app.exec_())
Client:
import socket
import threading
import time
import os
class mainProg:
def __init__(self, host, port):
self.is_connection_lost = threading.Event()
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.client.connect((host, port))
except Exception as e:
print(e)
print('Server is not valid...quitting...')
time.sleep(5)
os._exit(0)
self.start_threads()
def recieve(self):
while True:
try:
message = self.client.recv(1024).decode('utf-8')
if message == 'Nickname':
self.client.send(nickname.encode('utf-8'))
elif message == '': #when server gone, we will recieve a lot of empty strings continuously.. we can make the clients shut the python if server gone, but client waiting server to init again will be cooler
self.is_connection_lost.set()
self.client.close()
print('Connection Lost: waiting for the connection,')
while True:
print('waiting...')
time.sleep(3)
if not self.is_connection_lost.is_set():
print('Connection restored!!!')
break
else:
print(message)
#print(type(message))
except Exception as e:
print(e)
print('An error occured!, Not yet connected!')
#client.close()
#is_connection_lost = True
break
def send_messages(self):
while True:
try:
message = f"{nickname}: {input('')}"
self.client.send(message.encode('utf-8'))
except Exception:
pass
def reconnect_server(self):
time.sleep(3)
while True:
if self.is_connection_lost.is_set():
print('Trying to connect!!!!')
time.sleep(3)
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.client.connect((srv,port))
except Exception as e:
print(f'Reconnect failed due to {e}')
continue
if self.client.recv(1024).decode('utf-8') == '':
self.client.close()
pass
else:
self.client.send(nickname.encode('utf-8'))
self.is_connection_lost.clear() #no problem here, it connects to server when server is up again...
def start_threads(self):
t1 = threading.Thread(target=self.recieve)
t2 = threading.Thread(target=self.send_messages)
t3 = threading.Thread(target=self.reconnect_server)
t1.start()
t2.start()
t3.start()
if __name__ == '__main__':
nickname = input('Choose a nickname: ')
srv = '0.tcp.in.ngrok.io'
port = 14112
mainProg(srv, port)
I know that using qthreads I can rewrite the code for client in GUI and make a functional app. But is there a way to connect both classes, so that I can use self.labelMessage.setText(message) instead of print(message) in client. In another way I am asking is there a way to inherit from multiple classes (MainApp and mainProg) so that I can easily connect both codes?

Related

Adapt thread to select in Python

While studying about networks, i found a very common exercise that i thought was pretty insteresting, that is an application to manage simple chatrooms using sockets in Python
The thing is that i found a solution that uses thread, and was wondering how to adapt the solution i found from thread use to select.
The server.py :
from http import client
import os
import socket
import threading
import time
class Server:
def __init__(self, host, port):
self.HOST = host
self.PORT = port
self.rooms_list = []
def get_network(self):
return (self.HOST,self.PORT)
def run(self):
try:
self.create_connection_TCP()
self.accept_connection_rooms()
except:
print("Ocorreu um erro com o servidor principal")
os._exit(1)
def getList(self):
...
def create_connection_TCP(self):
server = (self.HOST, self.PORT)
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
self.socket.bind(server)
except:
print("Bind failed")
os._exit(1)
self.socket.listen(100)
def accept_connection_rooms(self):
while True:
try:
client, client_address = self.socket.accept()
thread = threading.Thread(target = self.control_connection, args = (client, ))
thread.start()
except:
print("Failing while creating conection")
os._exit(1)
def check_comand(self, client_socket):
message = client_socket.recv(1024).decode('utf-8')
command = message.split(':')
if command[0] == '/shutdown':
self.socket.close()
if command[0] == '/add_room':
room = ':'.join(command[1:4])
print(room)
if not room in self.rooms_list:
qtd_clients = len(self.rooms_list)
print(f"servidor: {room} | max clients: {command[4]}")
room = ':'.join(command[1:5])
self.rooms_list.append(room)
if command[0] == '/get_room':
index = int(command[1])
try:
room = self.rooms_list[index].split(':')
room = ':'.join(room[1:3])
client_socket.send(f"{room}".encode('utf-8'))
except IndexError:
client_socket.send("error: invald option".encode('utf-8'))
if command[0] == '/get_room_id':
message = len(self.rooms_list)
client_socket.send(message.encode('utf-8'))
if command[0] == '/list_rooms':
rooms = []
for index in range(len(self.rooms_list)):
room_name = self.rooms_list[index].split(':')[0]
rooms.append(f"{index} - {room_name}")
rooms = '\n'.join(rooms)
client_socket.send(f"{rooms}".encode('utf-8'))
# print(f"{rooms}")
if command[0] == '/close_room':
room = ':'.join(command[1:4])
self.rooms_list.remove(room)
print(f"closed_room: {room}")
def control_connection(self, client):
self.check_comand(client)
def close_server(self):
self.socket.close()
server = Server('127.0.0.1', 5000)
server.run()
Probably a good solution is to use select to listen all sockets connections and implement on accept_connection_rooms to manage new sockets

Why is my connection being refused half of the time

I'm trying to find a way to forward stdin input from my main process to a child process, and what I've found that works is basically to open a socket on the main process and then send text via the socket to the children processes. But what I'm finding is that half of the time my socket gets refused, and I have no idea what's going on.
I've followed the instructions on this question 16130786 but to no avail, I can connect via telnet, but the software still fails.
Here is the minimally reproducable example I've made
from multiprocessing import Process, Queue
from queue import Full, Empty
from io import TextIOBase
import socket
import selectors
class SocketConsoleClient(TextIOBase):
def __init__(self, port: int):
self.port = port
self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.conn.connect(('', self.port))
self.selector = selectors.DefaultSelector()
self.conn.setblocking(False)
self.selector.register(self.conn, selectors.EVENT_WRITE, data='hello')
def readline(self, size: int = ...) -> str:
while True:
for k, _ in self.selector.select(timeout=None):
if k.data == 'hello':
try:
return str(self.conn.recv(1024).decode('latin1'))
except Exception as e:
# print(e)
continue
class SocketConsoleWriter(Process):
def __init__(self):
super().__init__()
self.writes = Queue()
self.connections = []
self.listener = None
self.selector = None
self.port = 10000
def run(self) -> None:
while True:
try:
self.listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.listener.bind(('', self.port))
self.listener.listen()
print('listening on', ('', self.port))
self.listener.setblocking(False)
break
except Exception as _:
self.port += 1 # if errno is 98, then port is not available.
self.selector = selectors.DefaultSelector()
self.selector.register(self.listener, selectors.EVENT_READ, data='test')
while True:
try:
w = self.writes.get_nowait()
if w == '$$$EXIT!!!':
break
else:
for c in self.connections:
c.send(w.encode('latin1'))
except Empty:
pass
try:
d = self.selector.select(1)
for k, _ in d:
if k.data == 'test':
conn, addr = self.listener.accept()
print('{} connected'.format(addr))
self.connections.append(conn)
except Exception as e:
# print(e)
pass
class SocketConsoleServer:
server = None
def __init__(self):
if SocketConsoleServer.server is None:
SocketConsoleServer.server = SocketConsoleWriter()
SocketConsoleServer.server.start()
#staticmethod
def port() -> int:
if SocketConsoleServer.server is None:
SocketConsoleServer.server = SocketConsoleWriter()
SocketConsoleServer.server.start()
return SocketConsoleServer.server.port
#staticmethod
def write(msg: str):
if SocketConsoleServer.server is None:
SocketConsoleServer.server = SocketConsoleWriter()
SocketConsoleServer.server.start()
SocketConsoleServer.server.writes.put(msg)
if __name__ == '__main__':
import sys, time
serv = SocketConsoleServer()
time.sleep(1)
class TestProcessSocket(Process):
def run(self):
sys.stdin = SocketConsoleClient(serv.port())
time.sleep(1)
print(input())
client = TestProcessSocket()
client.start()
serv.write(input('Type something: '))
client.join()
Why is my socket connection getting refused, I'm using ubuntu?

Python Multiprocessing Manager - Client unable to reconnect

I am running an application which cannot sit and wait the successful/unsuccessful connection to a Python Manager. The client application should try to send some info to the supposedly running server, and in case it fails, another measure is taken. The problem is that whenever the server is down the connection takes a lot of time to return the control to the client application, and it cannot waste time waiting for it because there is other stuff to do.
I came up with a scheme where an intermediary object is in charge of the connection but it only works once. Let's say that for the first time, when there is still no connection to the server, this intermediary object handles the connecting part without blocking the client application. If, for some reason, the server goes down and comes back again, I can't get it to work anymore.
Suppose I have the following server:
# server.py
from multiprocessing import Queue, managers
from multiprocessing.queues import Empty
import select
import threading
class RServer(object):
def __init__(self, items_buffer):
self.items_buffer = items_buffer
def receive_items(self):
while True:
(_, [], []) = select.select([self.items_buffer._reader], [], [])
while True:
try:
item = self.items_buffer.get(block=False)
# do something with item
print('item received')
except Empty:
break
class SharedObjectsManager(managers.BaseManager):
pass
if __name__ == '__main__':
items_buffer = Queue()
remote_server = RServer(items_buffer)
remote_server_th = threading.Thread(target=remote_server.receive_items)
remote_server_th.start()
SharedObjectsManager.register('items_buffer', callable=lambda: items_buffer)
shared_objects_manager = SharedObjectsManager(address=('localhost', 5001),
authkey=str.encode('my_server'),
serializer='xmlrpclib')
s = shared_objects_manager.get_server()
s.serve_forever()
And here is the intermediary object to handle the connection:
# bridge.py
from multiprocessing.managers import BaseManager
import threading
import socket
class ConnectionManager():
def __init__(self):
self.remote_manager = BaseManager(address=('localhost', 5001),
authkey=b'my_server',
serializer='xmlrpclib')
self.remote_manager.register('items_buffer')
self.items_buffer = None
self.items_buffer_lock = threading.Lock()
self.connecting = False
self.connecting_lock = threading.Lock()
self.connection_started_condition = threading.Condition()
def transmit_item(self, item):
try:
with self.items_buffer_lock:
self.items_buffer.put(item)
except (AttributeError, EOFError, IOError):
with self.connection_started_condition:
with self.connecting_lock:
if not self.connecting:
self.connecting = True
connect_th = threading.Thread(target=self.connect_to_server,
name='Client Connect')
connect_th.start()
self.connection_started_condition.notify()
raise ConnectionError('Connection Error')
def connect_to_server(self):
with self.connection_started_condition:
self.connection_started_condition.wait()
try:
self.remote_manager.connect()
except socket.error:
pass
else:
try:
with self.items_buffer_lock:
self.items_buffer = self.remote_manager.items_buffer()
except (AssertionError, socket.error):
pass
with self.connecting_lock:
self.connecting = False
class ConnectionError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
And finally the client application:
# client.py
import time
from bridge import ConnectionManager, ConnectionError
remote_buffer = ConnectionManager()
while True:
try:
remote_buffer.transmit_item({'rubish': None})
print('item sent')
except ConnectionError:
# do something else
print('item not sent')
# do other stuff
print('doing other stuff')
time.sleep(15)
I am for sure doing something wrong with the thread but I can't figure it out. Any idea?

Python Socket - Can't connect a second time

I have set up a socket server with a client and a host.
It works fine until the client has disconnected, with both .shutdown() and .close().
When I then launch the client again, it can't connect.
I presume this is not because of how I've written my code but rather what I haven't written.
How do I make the server truly disconnect the client's connection so that it can connect again?
Server:
import socket, threading, time, json
ONLINE_USERS = []
SESSION = None
class User():
def __init__(user, connection, address):
print('for', address, '{Connection established}')
user.connection = connection
user.address = address
user.character = None
threading.Thread(target=user.process, args=(), daemon=True).start()
def process(user):
time.sleep(1)
user.send("&pLogin\n^^^^^\n")
username = user.send("&iUser>")
password = user.send("&iPass>")
print(user.ping())
print(user.logout())
def send(user, *x):
user.connection.sendall(str.encode(str(x)))
data = user.connection.recv(1024)
return data if data!=b'\x01' else True
def recv(user, x):
user.connection.recv(x)
def ping(user):
start = time.time()
user.connection.sendall(b'\x02')
end = float(user.connection.recv(1024))
return round((end - start) * 1000)
def logout(user):
user.connection.sendall(b'\x04')
return user.connection.recv(4)
class Session():
def __init__(session, host='', port=12345):
session.host = host
session.port = port
session.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
session.socket.bind((host, port))
session.socket.listen(10)
def accept():
conn = User(*session.socket.accept())
session.thread_accept = threading.Thread(target=accept, args=(), daemon=True).start()
def shutdown():
for user in ONLINE_USERS.keys():
ONLINE_USERS[user].connection.sendall(bytes([0xF]))
if __name__ == '__main__':
SESSION = Session()
input('Press heart to continue!\n')
Client:
import socket, sys, threading, time, os
def clear(t=0.5):
time.sleep(t)
os.system('cls')
def tryeval(x, default):
try:
return eval(x)
except:
return default
class Client():
def __init__(client):
try:
server_info = input('IP_ADDRESS:PORT>').split(':')
client.host = server_info[0]
client.port = int(server_info[1])
except:
client.host = 'localhost'
client.port = 12345
client.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.socket.settimeout(10)
try:
client.socket.connect((client.host, client.port))
clear()
client.data_exchange()
finally:
client.shutdown()
def data_exchange(client):
while True:
data = client.socket.recv(1024)
if data:
if data==b'\x02':
client.socket.sendall(str.encode(str(time.time())))
elif data==b'\x04':
client.shutdown()
else:
data = tryeval(data, ())
response = []
for item in data:
try:
prefix, content = item[:2], item[2:]
if prefix=='&p':
print(content, end='')
elif prefix=='&i':
response.append(input(content))
if prefix=='&c':
time.sleep(float(content))
clear()
except:
pass
if len(response)>0:
client.socket.sendall(str.encode(str(tuple(response))))
else:
client.socket.sendall(b'\x01')
time.sleep(0.001)
def shutdown(client):
try:
client.socket.sendall(b'\x04')
except:
pass
print('Shutting down program.')
client.socket.shutdown(socket.SHUT_RDWR)
print('Socket has been shutdown.')
client.socket.close()
print('Socket has been closed.')
print('Exiting program')
time.sleep(1)
sys.exit()
if __name__ == '__main__':
client = Client()
"The server repeatedly calls accept waiting for new incoming connections." No it doesn't. It calls accept once in a thread...which exits. – Mark Tolonen

Python socket programming with multiple threads

I'm trying to use sockets in python. For now, I'm trying to get it such that if any client sends any message it is received at all clients. However I'm getting very weird results. I think it's because I'm using multiple threads. The output of the program changes every time I run it. Is this a threading problem or is it something else?
import socket
import sys
from thread import *
from server import Server
from client import Client
s = Server()
start_new_thread(s.acceptConnection,())
m = Client("m")
k = Client("k")
start_new_thread(m.recieveData,())
start_new_thread(k.recieveData,())
k.sendData("Hey!")
print "*"*100
print repr(k.data()), repr(m.data())
print "*"*100
m.sendData("okay okay")
print "*"*100
print repr(k.data()), repr(m.data())
print "*"*100
m.client.close()
k.client.close()
s.s.close()
Server Class:
import socket
import sys
from thread import *
class Server(object):
def __init__(self,port = 5555):
self.host = 'localhost' # '' means connect to all hosts
self.port = port
self.text = ""
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
self.s.bind((self.host, self.port))
except socket.error as e:
print(str(e))
self.s.listen(2)
print "Waiting for a connection.\n"
self.connections = []
def threaded_client(self,conn):
# conn.send("Connected to server\n")
while True:
try:
data = conn.recv(2048)
except:
data = ""
if(not data):
break
# conn.sendall(reply)
for c,a in self.connections:
try:
c.sendall(data + "\n")
except:
print "connection lost\n"
self.connections.remove((c,a))
conn.close()
def acceptConnection(self):
while True:
conn, addr = self.s.accept()
self.connections += [(conn,addr)]
start_new_thread(self.threaded_client,(conn,))
Client class:
import socket
import sys
from thread import *
class Client(object):
def __init__(self,name):
self.host = 'localhost'
self.port = 5555
self.name = name
self.client= socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.client.connect((self.host,self.port))
self.text = ""
def sendData(self,data):
self.client.send(data)
def recieveData(self):
while True:
try:
data = self.client.recv(2048)
except:
break
if data:
self.text = data
self.client.close()
def data(self):
return self.text
def closeClient(self):
self.client.close()
Anyway you have no warrants about the data was already come back to the clients when you try to print it. You should introduce some Conditions and use wait() and notifyAll() to make sure the data was arrive.... To check if my guess is correct put some sleep() in your test:
import time
k.sendData("Hey!")
print "*"*100
time.sleep(200)
print repr(k.data()), repr(m.data())
print "*"*100
m.sendData("okay okay")
print "*"*100
time.sleep(200)
print repr(k.data()), repr(m.data())
print "*"*100
If it works you should use conditions and notify to do your tests.
Moreover you must protect data access by a Lock().
def recieveData(self):
while True:
try:
data = self.client.recv(2048)
except:
break
if data:
self.l.acquire()
self.text = data
self.l.release()
self.client.close()
def data(self):
self.l.acquire()
ret = self.text
self.l.release()
return ret
Where attribute l of clients are defined in __init__ by
self.l=threading.Lock()

Categories