Home > front end >  Socket not shutting down/closing (python3)
Socket not shutting down/closing (python3)

Time:01-14

I have a server variable which is socket object.
For some reason, when I do server.shutdown(socket.SHUT_RDWR) it gives me following error:

OSError: [WinError 10057] A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied

I tried using .close(), but it gave me this:

OSError: [WinError 10038] an operation was attempted on something that is not a socket

Server code:

import sys
import time
import socket
import threading
import tkinter as tk
from tkinter import messagebox as msgbox

HEADER = 64
PORT = 5050
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "%client_disconnect"

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)
server_active = True

messages = []

def add_message(message):
    local_t = time.localtime()
    sent_at = time.strftime("%Y.%m.%d %H:%M:%S", local_t)
    messages.append(f"{sent_at} | {str(message)}")

def handle_client(conn, addr):
    print(f"\n===[DEBUG (SERVER)]\tHandling connection: {addr}")

    connected = True
    while connected:
        msg_len = conn.recv(HEADER).decode(FORMAT)
        if msg_len:
            msg_len = int(msg_len)
            msg = conn.recv(msg_len).decode(FORMAT)
            print(f"\n===[DEBUG ({addr})]\tMessage length: {msg_len}; Message: {msg}")

            if msg == DISCONNECT_MESSAGE:
                connected = False

        if not server_active:
            connected = False

    conn.close()

def start():
    server.listen()
    print(f"\n===[DEBUG (SERVER)]\tListening on {SERVER}")
    while True:
        print("\n===[DEBUG (SERVER)]\tWaiting for client connection")
        conn, addr = server.accept()
        thread = threading.Thread(target=handle_client, args=(conn, addr))
        thread.start()
        print(f"\n===[DEBUG (SERVER)]\tNew connection: ({addr}); "
              f"Current active connections: {threading.active_count() - 1}")

def on_closing():
    global server_active
    if msgbox.askokcancel("Serverside", "Are you sure you want to quit? This will fully shut down the server"
                                        "and disconnect all clients."):
        server_active = False
        window.destroy()
        server.close()

# window is empty
window = tk.Tk()
window.wm_geometry("700x500")
window.title("Serverside")
window.protocol("WM_DELETE_WINDOW", on_closing)

t = time.localtime()
start_time = time.strftime("%Y.%m.%d %H:%M:%S", t)

print("===[DEBUG (SERVER)]\tStarting Server")
server_thread = threading.Thread(target=start)
server_thread.start()
add_message(f"Server started at: {start_time}")

tk.mainloop()

Client code:

import socket
import threading
import tkinter as tk
from tkinter import messagebox as msgbox

HEADER = 64
PORT = 5050
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "%client_disconnect"
SERVER = None
ADDR = None

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

def send(msg):
    message = msg.encode(FORMAT)
    msg_len = len(message)
    send_len = str(msg_len).encode(FORMAT)
    send_len  = b' ' * (HEADER - len(send_len))
    client.send(send_len)
    client.send(message)

def connect_to(server):
    global SERVER, ADDR
    SERVER = server
    ADDR = (SERVER, PORT)
    client.connect(ADDR)

def on_closing():
    if msgbox.askokcancel("Client", "Are you sure you want to quit? You will be disconnected from the server."):
        send(DISCONNECT_MESSAGE)
        window.destroy()

def handle_messages():
    msg = str(entry.get())
    send(msg)

    if msg == DISCONNECT_MESSAGE:
        window.destroy()

connect_to(socket.gethostbyname(socket.gethostname()))

window = tk.Tk()
window.geometry("600x450")
window.title("Client")
window.protocol("WM_DELETE_WINDOW", on_closing)

entry = tk.Entry()
entry.place(x=0, y=0, width=100, height=30)

button = tk.Button(command=handle_messages)
button.place(x=0, y=50, width=50, height=20)

tk.mainloop()

Maybe I am shutting down server incorrectly?

CodePudding user response:

So the error is kind of a weird one. You are shutting down the socket properly but you're not handling your threads appropriately.

What is happening is you have your start thread accepting new connections but you're closing the socket on the main thread. So what happens is the main thread closes the socket but the start thread is still accepting connections but since the socket was closed it throws that error. This error is no big deal you could just ignore it if you want or wrap in a catch and just break out of the while loop or you could join your thread before shutting down the socket and then no more error.

Two things to note:

  1. A try catch would prob be the easier and more graceful option but be care not to catch a error and just discard it. Could make other bugs hard to find
  2. You need to set the threads to be daemons which means that the program will not wait for them to join before exiting.

Heres the modified code to avoid the error using timeout.

def start():
    server.listen()
    print(f"\n===[DEBUG (SERVER)]\tListening on {SERVER}")
    while True:
        print("\n===[DEBUG (SERVER)]\tWaiting for client connection")
        conn, addr = server.accept()
        thread = threading.Thread(target=handle_client, args=(conn, addr), daemon=True) # This is a daemon thread now
        thread.start()
        print(f"\n===[DEBUG (SERVER)]\tNew connection: ({addr}); "
              f"Current active connections: {threading.active_count() - 1}")

...

def on_closing():
    global server_active
    if msgbox.askokcancel("Serverside", "Are you sure you want to quit? This will fully shut down the server"
                                        "and disconnect all clients."):
        server_active = False
        window.destroy()
        server_thread.join(timeout=0.0) # Timeout the thread to force it to join
        server.close()

...

server_thread = threading.Thread(target=start, daemon=True) # I'm a daemon thread so i dont block the program from closing
server_thread.start()
  • Related