Я вроде как новичок ie здесь, так что извините, если я не понимаю свою точку зрения так, как должно быть.
Я пытаюсь улучшить сервер, который несколько месяцев писал go в сокетах с Tkinter. Я использовал шаблон, который я нашел на inte rnet. Кажется, все работает гладко и нормально, пока я не дохожу до части server.listening (). В этот момент Tkinter всегда будет зависать. Я знаю, что это как-то связано с многопоточностью, многопроцессорностью и тем фактом, что этот поток находится вне основного l oop, но пока не могу найти никакого решения.
Сервер действительно работает, хотя Tkinter зависает каждый раз, когда я нажимаю кнопку «прослушивания». Итак, вот моя точка зрения. Не имеет значения, полностью ли он замораживает Tkinter на пару секунд, так как этот сервер не сможет многое сделать, пока к нему не подключится хотя бы один человек. Я только хочу, чтобы программа могла активировать «прослушивание», когда нажата соответствующая кнопка, и деактивировать ее, когда она мне нужна.
Не могли бы вы пролить свет на мои сомнения? Я как 2 дня постоянно боролся с этим и только смог добиться этого. Я оставлю полный код в конце этого поста, на всякий случай, если вы считаете необходимым запустить его в первую очередь. Итак, вот код, некоторые части которого удалены для простоты:
from socket import AF_INET, socket, SOCK_STREAM
from threading import Thread
from datetime import date
from tkinter import messagebox
from tkinter import *
from tkinter import ttk, font
from multiprocessing import Process
import getpass
import os
import time
global msgs
global opt
global ADDR
global MK1
def broadcast(msg, prefix=""): # prefix is for name identification.
"""Broadcasts a message to all the clients."""
for sock in clients:
sock.send(bytes(prefix, "utf8")+msg)
class threads():
def accept_incoming_connections():
"""Sets up handling for incoming clients."""
while True:
global client_address
client, client_address = SERVER.accept()
client.send(bytes("Introduce un nombre y pulsa enter ", "utf8"))
client.send(bytes("Asegurate de borrar 'Escribe...' antes de enviar tu nombre", "utf8"))
addresses[client] = client_address
Thread(target=threads.handle_client, args=(client,)).start()
def handle_client(client): # Takes client socket as argument.
"""Handles a single client connection."""
name = client.recv(BUFSIZ).decode("utf8")
welcome = 'Bienvenido %s! Pulsa el botón "Salir" para salir. No pulses X.' % name
client.send(bytes(welcome, "utf8"))
msg = "%s se ha unido al chat!" % name
broadcast(bytes(msg, "utf8"))
clients[client] = name
while True:
msg = client.recv(BUFSIZ)
if msg != bytes("{quit}", "utf8"):
broadcast(msg, name+": ")
else:
client.send(bytes("{quit}", "utf8"))
client.close()
del clients[client]
broadcast(bytes("%s se ha ido del chat." % name, "utf8"))
break
class IP():
def __init__(self):
# Tkinter part for defining the IP of the server. This works smoothly.
global ip1
ip1 = 1
def aceptar(self):
global HOST
global PORT
HOST = self.dip.get()
PORT = self.port.get()
if not PORT:
PORT = 33000
PORT = int(PORT)
self.raiz.destroy()
global host1
host1 = 1
global ADDR
ADDR = (HOST, PORT)
global SERVER
SERVER = socket(AF_INET, SOCK_STREAM)
SERVER.bind(ADDR)
def borrar_mensa(self, evento):
self.dip.set("")
self.port.set("")
class main ():
def __init__(self):
def send(event=None): # event is passed by binders.
"""Handles sending of messages."""
msg1 = my_msg.get()
my_msg.set("") # Clears input field.
msg_list.insert(tkinter.END, msg1)
if msg1 == "{quit}":
client_socket.close()
top.quit()
def on_closing(event=None):
"""This function is to be called when the window is closed."""
my_msg.set("{quit}")
send()
top = tkinter.Tk()
top.title("Servidor - Aristoi.................. V. 1.0")
top.geometry("650x450")
messages_frame = tkinter.Frame(top)
my_msg = tkinter.StringVar() # For the messages to be sent.
my_msg.set("Introduzca función...")
scrollbar = tkinter.Scrollbar(messages_frame) # To navigate through past messages.
# Following will contain the messages.
global msg_list
msg_list = tkinter.Listbox(messages_frame, height=23, width=95, yscrollcommand=scrollbar.set)
# Following will contain buttons functions.
def SERVERclose():
SERVER.close()
def EXIT():
top.destroy()
def def_IP():
if ip1 is None:
IP()
else:
tkinter.messagebox.showerror(title="Error", message="IP ya definida. Reinicie")
def listening():
if host1 is None:
tkinter.messagebox.showerror(title="Error", message="No ha definido una IP para el servidor")
else:
SERVER.listen(5)
INPUT = Thread(target=threads.accept_incoming_connections)
INPUT.start()
tkinter.messagebox.showinfo(title="Conectando", message="Esperando conexiones entrantes")
INPUT.join()
SERVER.close()
global host2
host2 = 1
# BUTTONS
separl = ttk.Separator(top, orient=HORIZONTAL)
entry_field = tkinter.Entry(top, textvariable=my_msg, width = 95)
entry_field.bind("<Return>", send)
send_button = tkinter.Button(top, text=" Enviar ", fg="black", command=send)
salir = tkinter.Button(top, text=" Salir ",bg="black", fg="white", command=EXIT)
host = tkinter.Button(top, text=" IP ",bg="black", fg="white", command=def_IP)
connect = tkinter.Button(top, text=" Conectar ",bg="black", fg="white", command=listening)
servcls = tkinter.Button(top, text=" ServerCLose ",bg="black", fg="white", command=SERVERclose)
#STETICS
scrollbar.pack(side=tkinter.RIGHT, fill=tkinter.Y)
msg_list.pack(side=tkinter.LEFT, fill=tkinter.BOTH)
messages_frame.pack()
entry_field.pack()
send_button.pack()
entry_field.place(x=29, y=380)
send_button.place(x=29, y=410)
salir.place(x=580, y=410)
host.place(x=480, y = 410)
connect.place(x=380, y = 410)
servcls.place(x=280, y = 410)
top.protocol("WM_DELETE_WINDOW", on_closing)
top.mainloop()
clients = {}
addresses = {}
PORT = 33000
BUFSIZ = 1024
opt = int(0)
MK1 = 1
global host1
global host2
global ip1
host1 = None
host2 = None
ip1 = None
if __name__ == "__main__":
main()
Вот полная версия кода:
from socket import AF_INET, socket, SOCK_STREAM
from threading import Thread
from datetime import date
from tkinter import messagebox
from tkinter import *
from tkinter import ttk, font
from multiprocessing import Process
import tkinter
import getpass
import os
import time
global msgs
global opt
global ADDR
global MK1
def broadcast(msg, prefix=""): # prefix is for name identification.
"""Broadcasts a message to all the clients."""
for sock in clients:
sock.send(bytes(prefix, "utf8")+msg)
class threads():
def accept_incoming_connections():
"""Sets up handling for incoming clients."""
while True:
global client_address
client, client_address = SERVER.accept()
client.send(bytes("Introduce un nombre y pulsa enter ", "utf8"))
client.send(bytes("Asegurate de borrar 'Escribe...' antes de enviar tu nombre", "utf8"))
addresses[client] = client_address
Thread(target=threads.handle_client, args=(client,)).start()
def handle_client(client): # Takes client socket as argument.
"""Handles a single client connection."""
name = client.recv(BUFSIZ).decode("utf8")
welcome = 'Bienvenido %s! Pulsa el botón "Salir" para salir. No pulses X.' % name
client.send(bytes(welcome, "utf8"))
msg = "%s se ha unido al chat!" % name
broadcast(bytes(msg, "utf8"))
clients[client] = name
while True:
msg = client.recv(BUFSIZ)
if msg != bytes("{quit}", "utf8"):
broadcast(msg, name+": ")
else:
client.send(bytes("{quit}", "utf8"))
client.close()
del clients[client]
broadcast(bytes("%s se ha ido del chat." % name, "utf8"))
break
class IP():
def __init__(self):
self.raiz = Tk()
self.raiz.geometry("450x200")
self.raiz.resizable(0,0)
self.raiz.title("Ventana de direcciones")
fuente = font.Font(weight="bold")
self.etiq1 = ttk.Label(self.raiz, text="Dirección IP", font=fuente)
self.etiq2 = ttk.Label(self.raiz, text="Puerto", font=fuente)
self.dip = StringVar()
self.port = StringVar()
self.ctext1 = ttk.Entry(self.raiz, textvariable=self.dip , width=40)
self.ctext2 = ttk.Entry(self.raiz, textvariable=self.port, width= 10)
self.separ1 = ttk.Separator(self.raiz, orient=HORIZONTAL)
self.boton1 = ttk.Button(self.raiz, text="Aceptar", command=self.aceptar)
self.etiq1.place(x=30, y=40)
self.etiq2.place(x=30, y=80)
self.ctext1.place(x=150, y=42)
self.ctext2.place(x=150, y=82)
self.separ1.place(x=5, y=145, bordermode=OUTSIDE,height=10, width=420)
self.boton1.place(x=170, y=160)
self.ctext1.focus_set()
self.ctext1.bind("<Button-1>", self.borrar_mensa)
self.ctext2.bind("<Button-1>", self.borrar_mensa)
global ip1
ip1 = 1
def aceptar(self):
global HOST
global PORT
HOST = self.dip.get()
PORT = self.port.get()
if not PORT:
PORT = 33000
PORT = int(PORT)
self.raiz.destroy()
global host1
host1 = 1
global ADDR
ADDR = (HOST, PORT)
global SERVER
SERVER = socket(AF_INET, SOCK_STREAM)
SERVER.bind(ADDR)
def borrar_mensa(self, evento):
self.dip.set("")
self.port.set("")
class main ():
def __init__(self):
# accept_incoming_connections ---------------------------------------------------------------
def host_log(self):
msgs = "%s:%s se ha conectado a las " + current_time + " del día " + today
msg_list.insert(tkinter.END, msgs)
# --------------------------------------------------------------------------------------------
#Parte del Tkinter
#-----------------------------------------
def send(event=None): # event is passed by binders.
"""Handles sending of messages."""
msg1 = my_msg.get()
my_msg.set("") # Clears input field.
msg_list.insert(tkinter.END, msg1)
if msg1 == "{quit}":
client_socket.close()
top.quit()
def on_closing(event=None):
"""This function is to be called when the window is closed."""
my_msg.set("{quit}")
send()
top = tkinter.Tk()
top.title("Servidor - Aristoi.................. V. 1.0")
top.geometry("650x450")
messages_frame = tkinter.Frame(top)
my_msg = tkinter.StringVar() # For the messages to be sent.
my_msg.set("Introduzca función...")
scrollbar = tkinter.Scrollbar(messages_frame) # To navigate through past messages.
# Following will contain the messages.
global msg_list
msg_list = tkinter.Listbox(messages_frame, height=23, width=95, yscrollcommand=scrollbar.set)
def SERVERclose():
SERVER.close()
def EXIT():
top.destroy()
def def_IP():
if ip1 is None:
IP()
else:
tkinter.messagebox.showerror(title="Error", message="IP ya definida. Reinicie")
def listening():
if host1 is None:
tkinter.messagebox.showerror(title="Error", message="No ha definido una IP para el servidor")
else:
SERVER.listen(5)
INPUT = Thread(target=threads.accept_incoming_connections)
INPUT.start()
tkinter.messagebox.showinfo(title="Conectando", message="Esperando conexiones entrantes")
INPUT.join()
SERVER.close()
global host2
host2 = 1
# BOTONES
separl = ttk.Separator(top, orient=HORIZONTAL)
entry_field = tkinter.Entry(top, textvariable=my_msg, width = 95)
entry_field.bind("<Return>", send)
send_button = tkinter.Button(top, text=" Enviar ", fg="black", command=send)
salir = tkinter.Button(top, text=" Salir ",bg="black", fg="white", command=EXIT)
host = tkinter.Button(top, text=" IP ",bg="black", fg="white", command=def_IP)
connect = tkinter.Button(top, text=" Conectar ",bg="black", fg="white", command=listening)
servcls = tkinter.Button(top, text=" ServerCLose ",bg="black", fg="white", command=SERVERclose)
#ESTETICA
scrollbar.pack(side=tkinter.RIGHT, fill=tkinter.Y)
msg_list.pack(side=tkinter.LEFT, fill=tkinter.BOTH)
messages_frame.pack()
entry_field.pack()
send_button.pack()
entry_field.place(x=29, y=380)
send_button.place(x=29, y=410)
salir.place(x=580, y=410)
host.place(x=480, y = 410)
connect.place(x=380, y = 410)
servcls.place(x=280, y = 410)
top.protocol("WM_DELETE_WINDOW", on_closing)
top.mainloop()
# AQUI
clients = {}
addresses = {}
PORT = 33000
BUFSIZ = 1024
opt = int(0)
MK1 = 1
global host1
global host2
global ip1
host1 = None
host2 = None
ip1 = None
t = time.localtime()
current_time = time.strftime("%H:%M:%S", t)
d1 = date.today()
today = d1.strftime("%d/%m/%Y")
if __name__ == "__main__":
main()