Postanowiłem napisać prostą aplikację serwer-klient jednak jestem dość "świeży" w temacie wątków przez co nie wiem jak poprawnie wyłączyć serwer (bez używania Ctrl-C z klawiatury).
#!/usr/bin/env python3
import socket
import sys
import threading
import select
from funkcje import to_bytes, to_strings
host = 'localhost'
class ManagerThread(threading.Thread):
def __init__(self, servsocket):
threading.Thread.__init__(self)
self.servsocket = servsocket
self.is_running = True
def run(self):
while self.is_running:
try:
(read, write, error) = select.select([self.servsocket] + klienci, [], [])
for connection in read:
if connection == self.servsocket:
(connection, address) = self.servsocket.accept()
klienci.append(connection)
else:
message = to_strings(connection.recv(1024))
method = message[0]
username = message[1]
if method == "JOIN":
print("Połączono z użytkownikiem", username, "pod adresem", connection.getsockname())
polaczenia[username] = connection.getsockname()
send_all(to_bytes(username, 1), connection)
elif method == "MSG":
msg = message[2]
send_all(to_bytes(username, 0, msg), connection)
elif method == "QUIT":
print("Użytkownik", username, "rozłączył się")
klienci.remove(connection)
connection.close()
polaczenia.pop(username)
send_all(to_bytes(username, 2))
except socket.error as blad:
print("Błąd przy odbieraniu połączenia!")
print("Opis: ", str(blad))
sys.exit()
def stop(self):
self.is_running = False
self.servsocket.close()
self.join()
def main():
global klienci, port, polaczenia
polaczenia = {}
klienci = []
if len(sys.argv) < 2:
port = 6789
else:
port = int(sys.argv[1])
# Tworzenie gniazda TCP / IPv4
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error as blad:
print("Błąd przy próbie uruchomienia serwera!")
print("Opis: ", str(blad))
sys.exit()
# Dowiązanie do portu
try:
s.bind((host,port))
except socket.error as blad:
print("Błąd: nie udało się dowiązać gniazda do portu %d" % port)
print("Opis: ", str(blad))
sys.exit()
print("Serwer uruchomiony")
print("Gniazdo nasłuchujące przypisane do portu %d" % port)
try:
s.listen(10)
except socket.error as blad:
print("Błąd przy nasłuchu gniazda!")
print("Opis: ", str(blad))
sys.exit()
serwer = ManagerThread(s)
serwer.start()
while True:
menu= 20 * "-" + "\nKomendy:\n\n" + "! - wyłącz serwer\n" + "? - status\n" + 20 * "-" + "\n"
command = input(menu)
if command == "!":
serwer.stop()
s.close()
sys.exit()
elif command == "?":
for user, conn in polaczenia.items():
print(user, conn)
def send_all(message, sender=None):
for klient in klienci:
if klient == sender:
continue
klient.send(message)
if __name__ == "__main__":
main()
Jak widać próbowałem użyć w wątku metody stop, jednak niestety nie robi ona nic. Jedyny sposób wyłączenia aplikacji to podwójne ctrl-c w okienku konsoli co generuje poniższy traceback:
Traceback (most recent call last):
File "serwer.py", line 121, in <module>
main()
File "serwer.py", line 106, in main
serwer.stop()
File "serwer.py", line 59, in stop
self.join()
File "/usr/lib/python3.5/threading.py", line 1054, in join
self._wait_for_tstate_lock()
File "/usr/lib/python3.5/threading.py", line 1070, in _wait_for_tstate_lock
elif lock.acquire(block, timeout):
KeyboardInterrupt
^CException ignored in: <module 'threading' from '/usr/lib/python3.5/threading.py'>
Traceback (most recent call last):
File "/usr/lib/python3.5/threading.py", line 1288, in _shutdown
t.join()
File "/usr/lib/python3.5/threading.py", line 1054, in join
self._wait_for_tstate_lock()
File "/usr/lib/python3.5/threading.py", line 1070, in _wait_for_tstate_lock
elif lock.acquire(block, timeout):
KeyboardInterrupt