Я пытаюсь создать приложение для чата, используя библиотеку сокетов. У меня есть три файла server.py
, client.py
и gui.py
. Процесс прослушивания для клиента и сервера обеспечивается бесконечными циклами. из-за этого server.py
запускается в другом окне терминала. но клиент и gui работают в одном окне терминала. проблема в том, что когда я вызываю функции, содержащие бесконечное число l oop, оно застревает там, а остальная часть кода не запускается. Я даже пытался использовать multiprocessing
, threading.Thread
, threading.Timer
, QThread
и Queue
, но все равно безуспешно. Я подумал, что, возможно, я не правильно использую эти библиотеки, поэтому решил обратиться за помощью. проблема threading.Timer
заключалась в том, что new parent is running in another thread, and I can not change gui objects outside main thread
. приятно упомянуть, что я как-то решил проблему, и все входящие сообщения, отправленные сервером, в итоге появляются на GUI. снова проблема все еще существовала, и я получил результаты, когда сломал бесконечную функцию l oop из incoming message
, нажав ctrl+c
. это занимает слишком много времени у меня и вызывает болезненную головную боль, я ценю, если кто-нибудь может помочь мне справиться с этой проблемой. спасибо, вот минимальный код: (для тестирования этого кода я объединил клиента и gui вместе)
здесь server.py:
import socket, json, select
class Server():
def __init__(self):
self.connected_sockets = []# for saving sockets
self.connected_clients = {}# for saving sockets and related usernames
self.password = '21709'
self.server_name = 'SERVER1'
def start(self, host, port):
# create the socket, AF_INET == ipv4, SOCK_STREAM == TCP
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind( (host, port) )
self.connected_sockets.append( self.server_socket )
self.connected_clients[self.server_name] = self.server_socket
self.server_socket.listen()
self.connect_clients()
def connect_clients(self):
next_msg = b''
while True:
read_sockets, _, exception_sockets = select.select(self.connected_sockets, [], self.connected_sockets)
for socket in read_sockets:
if socket == self.server_socket:
#new connecttion
client_socket, address = self.server_socket.accept()
from_client = client_socket
msg, next_msg = self.receive_data( from_client )
data = json.loads(msg)['data']
username = data['username']
password = data['password']
from_user = self.server_name
to_user = username
BCC = self.server_socket
msg_type = "string"
if username in self.connected_clients:
self.transfer_data( f"username {username} is not valid, try again", msg_type, from_user, to_user, BCC )
client_socket.close()
else:
if ( password == self.password ):
self.connected_sockets.append( client_socket )
self.connected_clients[username] = client_socket
self.transfer_data( 'password was correct, wellcome', msg_type, from_user, to_user, BCC )
print(f"Connection from {address} has been established.")
#send welcome phrase to client just joined from the server
self.transfer_data( "Hey there!!! it's a json", msg_type, from_user, to_user, BCC )
self.transfer_data( "Wellcome to this server", msg_type, from_user, to_user, BCC )
self.transfer_data( "here you can", msg_type, from_user, to_user, BCC )
self.transfer_data( "connect to others", msg_type, from_user, to_user, BCC )
else:
self.transfer_data( "password was incorrect, sorry", msg_type, from_user, client_socket, BCC )
client_socket.close()
else:
#old connection and receive_message from them
for user,user_socket in self.connected_clients.items():
if user_socket == socket:
username_related_to_socket = user
break
try:
msg,next_msg = self.receive_data(socket,next_msg)
msg = json.loads(msg)
from_user = msg['from_user']
to_user = msg['to_user']
if to_user != self.server_name:
self.transfer_data(msg['data'], msg['type'], msg['from_user'], msg['to_user'], msg['BCC'] )
else:
print( f"{msg['from_user']}: \x1b[6;30;42m {msg['data']} \x1b[0m" )
except:
print(f'\n client \x1b[6;30;42m {username_related_to_socket} \x1b[0m disconnected \n')
self.connected_sockets.remove( socket )
del self.connected_clients[username_related_to_socket]
def receive_data(self, from_user, next_msg=b""):
from_client = from_user
full_msg = next_msg
while True:
msg = from_client.recv(7)
try:# because the Ӛ has length of 2, so it may happen that, only one of them exist in the msg received
index = msg.decode("utf-8").find('Ӛ')
except:
msg += from_client.recv(1)
index = msg.decode("utf-8").find('Ӛ')
if ( (index != -1 ) or (len(msg) <= 0) ):
full_msg += msg[:index]
next_msg = msg[index+2:]
break
else:
full_msg += msg
full_msg = full_msg.decode("utf-8")
return(full_msg, next_msg)
def transfer_data(self, msg, msg_type, from_user, to_user, BCC):
from_client = self.connected_clients[from_user]
if type(to_user) is str:
try:
to_client = self.connected_clients[to_user]
result = True
except:
msg = f"{to_user} is offline. try later"
to_client = self.connected_clients[from_user]
to_user = from_user
from_user = self.server_name
result = False
else:
to_client = to_user
to_user = from_user
from_user = self.server_name
result = False
if msg_type == 'string':
msg = msg.strip()
msg = {'type':msg_type, 'size':len(msg), 'data':msg, 'from_user':from_user, 'to_user':to_user, 'BCC':str(BCC), 'result':result}
# turn a dictionary into a string to transfere with socket
data_string = json.dumps(msg)
to_client.send( bytes(data_string,"utf-8") )
to_client.send( bytes('Ӛ',"utf-8") )
server = Server()
server.start(host='127.0.0.1', port=1234)
клиента и gui вместе. py:
import os, socket, json
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class ConnectPage(QWidget):
def __init__(self):
QWidget.__init__(self)
prev_info = ['','','','']
if os.path.isfile("previous_login.txt"):
saved_login_file = open("previous_login.txt",'r')
lines = saved_login_file.readlines()
if len(lines) > 0:
for i in range(0,len(lines)):
prev_info[i] = lines[i]
saved_login_file.close()
self.username = QLineEdit(prev_info[0])
self.password = QLineEdit(prev_info[1])
self.host = QLineEdit(prev_info[2])
self.port = QLineEdit(prev_info[3])
self.port.setValidator( QIntValidator() )#takes only numbers
self.login_button = QPushButton('Login')
self.login_button.clicked.connect(self.login_button_clicked)
self.clear_form = QPushButton('Clear Form')
self.clear_form.clicked.connect(self.clear_form_clicked)
self.status_bar = QLabel()
self.main_layout = QGridLayout()
self.main_layout.addWidget( QLabel('Username:'),0,0 )
self.main_layout.addWidget( self.username,0,1 )
self.main_layout.addWidget( QLabel('Password:'),1,0 )
self.main_layout.addWidget( self.password,1,1 )
self.main_layout.addWidget( QLabel('Host:'),2,0 )
self.main_layout.addWidget( self.host,2,1 )
self.main_layout.addWidget( QLabel('Port:'),3,0 )
self.main_layout.addWidget( self.port,3,1 )
self.main_layout.addWidget( self.clear_form,4,0 )
self.main_layout.addWidget( self.login_button,4,1 )
self.main_layout.addWidget( self.status_bar,5,0,2,1 )
self.setLayout(self.main_layout)
def clear_form_clicked(self):
username = self.username.setText('')
password = self.password.setText('')
host = self.host.setText('')
port = self.port.setText('')
def login_button_clicked(self):
username = self.username.text().strip()
password = self.password.text().strip()
host = self.host.text().strip()
port = self.port.text().strip()
saved_login_file = open("previous_login.txt",'w')
saved_login_file.write( username + '\n' + password + '\n' + host + '\n' + port )
saved_login_file.close()
result,msg = controller.client_connect(host, int(port), username, password)
if result:
controller.connect_page.close()
controller.chat_page.show()
controller.chat_page.start_listening()
else:
controller.connect_page.status_bar.setText(msg['data'])
class ChatPage(QWidget):
def __init__(self):
QWidget.__init__(self)
self.next_msg = b''
#create a scrolled window to put info_grid in it
self.msg_layout = QVBoxLayout()
msg_layout_widget = QWidget()
msg_layout_widget.setLayout(self.msg_layout)
self.scrolled = QScrollArea()
self.scrolled.setWidget(msg_layout_widget)
self.scrolled.setWidgetResizable(True)
self.scrolled.setFixedHeight(400)
scroll_layout = QHBoxLayout()
scroll_layout.addWidget(self.scrolled)
msg_layout_groupbox = QGroupBox()
msg_layout_groupbox.setLayout(scroll_layout)
self.new_msg = QTextEdit()
send_button = QPushButton('Send icon')
#send_button.clicked.connect( self.send_button_clicked )
self.send_layout = QHBoxLayout()
self.send_layout.addWidget(self.new_msg)
self.send_layout.addWidget(send_button)
self.right_layout = QVBoxLayout()
self.right_layout.addWidget(msg_layout_groupbox)
self.right_layout.addLayout(self.send_layout)
self.main_layout = QHBoxLayout()
self.main_layout.addLayout(self.right_layout)
self.setLayout(self.main_layout)
def start_listening(self):
controller.client.listen_to_incoming_messages()
def insert_into_chat_area(self,data):
from_user = data['from_user']
to_user = data['to_user']
msg = data['data']
if msg != '':
if data['type'] == 'string':
self.msg_layout.addWidget( QLabel(msg) )
else:
print( "unknown data recieved (not a dictionary)" )
class Client(QObject):
def __init__(self,host,port,username,password):
QObject.__init__(self)
self.BCC = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.host = host
self.port = port
self.username = username
self.password = password
self.next_msg = b''
def connect(self):
audience = "SERVER1"
self.BCC.connect( (self.host, self.port) )
msg = {'username':self.username,'password':self.password}
msg_type = 'login'
to_user = audience
from_user = self.username
self.transfer_data(msg, msg_type, from_user, to_user, self.BCC)
msg,self.next_msg = self.recieve_data(self.BCC)
msg = json.loads(msg)
print( f"{msg['from_user']}: \x1b[6;30;42m {msg['data']} \x1b[0m" ) #syntax => print(f'\x1b[6;30;42m {colored text} \x1b[0m')
result = msg['result']
return( result,msg )
def listen_to_incoming_messages(self):
while True:
msg,self.next_msg = self.recieve_data(self.BCC,self.next_msg)
msg = json.loads(msg)
print( f"{msg['from_user']}: \x1b[6;30;42m {msg['data']} \x1b[0m" )
controller.chat_page.insert_into_chat_area(msg)
def outgoing_messages(self,from_user,to_user,BCC):
while True:
msg = input(f'{self.username}> ')
self.transfer_data(msg,'string',from_user,to_user,BCC)
def transfer_data(self, msg, msg_type, from_user, to_user, BCC):
if msg_type == 'string':
msg = msg.strip()
result = True
msg = {'type':msg_type, 'size':len(msg), 'data':msg, 'from_user':from_user, 'to_user':to_user, 'BCC':str(BCC), 'result':result}
data_string = json.dumps(msg)
BCC.send( bytes(data_string,"utf-8") )
BCC.send( bytes('Ӛ',"utf-8") )
def recieve_data(self, from_server, next_msg=b""):
full_msg = next_msg
while True:
msg = from_server.recv(7)
try:
index = msg.decode("utf-8").find('Ӛ')
except:
msg += from_server.recv(1)
index = msg.decode("utf-8").find('Ӛ')
if ( (index != -1 ) or (len(msg) <= 0) ):
full_msg += msg[:index]
next_msg = msg[index+2:]
break
else:
full_msg += msg
full_msg = full_msg.decode("utf-8")
return(full_msg, next_msg)
# this class controls moving between all screens
class Controller:
def __init__(self):
self.connect_page = ConnectPage()
self.connect_page.show()
self.chat_page = ChatPage()
def client_connect( self, host, port, username, password ):
self.client = Client(host, port, username, password)
return( self.client.connect() )
app = QApplication([])
controller = Controller()
app.exec_()