11
from socket import socket, AF_INET, SOCK_STREAM

sock = socket(AF_INET, SOCK_STREAM)
sock.bind(("localhost", 7777))
sock.listen(1)
while True:
    try:
        connection, address = sock.accept()
        print("connected from " + address)
        received_message = sock.recv(300)
        if not received_message:
            break
        connection.sendall(b"hello")

    except KeyBoardInterrupt:
        connection.close()

so Im trying to wrap my head around sockets and have this pretty simple script but for some reason I can't kill this script with a KeyboardInterrupt

how do I do kill the script with a KeyboardInterrupt that and why can't I kill it with a KeyboardInterrupt?

alter_igel
  • 6,899
  • 3
  • 21
  • 40
Zion
  • 1,570
  • 6
  • 24
  • 36

7 Answers7

5
  1. To break to get out the while loop. Without break, the loop will not end.
  2. To be safe, check whether connection is set.

from socket import socket, AF_INET, SOCK_STREAM

sock = socket(AF_INET, SOCK_STREAM)
sock.bind(("localhost", 7777))
sock.listen(1)
while True:
    connection = None # <---
    try:
        connection, address = sock.accept()
        print("connected from ", address)
        received_message = connection.recv(300)
        if not received_message:
            break
        connection.sendall(b"hello")
    except KeyboardInterrupt:
        if connection:  # <---
            connection.close()
        break  # <---

UPDATE

  • There was a typo: KeyBoardInterrupt should be KeyboardInterrupt.
  • sock.recv should be connection.recv.
falsetru
  • 357,413
  • 63
  • 732
  • 636
  • 10
    this is odd because. running this still won't close the script with a `CTRL-C` – Zion Jan 19 '16 at 08:05
  • 1
    since I am using this on windows is this the reason why the keyboard interrupt is not being triggered? – Zion Jan 19 '16 at 08:50
  • 1
    @Zion, I tried on Linux, and it worked. http://asciinema.org/a/dsatnomhvpvvl3skdunbpinov – falsetru Jan 19 '16 at 08:51
  • 1
    @Zion, How about `Ctrl` + `Break`? – falsetru Jan 19 '16 at 08:54
  • thanks man. I guess its a windows thing. I tried it on OS X and it works. – Zion Jan 19 '16 at 09:31
  • This code appears that CTRL-C works on any Nix system (Mac or linux or at least worked on Mac for me) but will not on Windows systems. For Windows I have to use this weird CTRL + ScrLk combination using the virtual keyboard and have it open in a stand alone powershell. Got that idea from this thread: https://stackoverflow.com/questions/42039231/ctrl-c-for-quitting-python-in-powershell-now-not-working – Jeremy Bailey May 15 '22 at 04:13
4

Try to use timeout to make the program periodically "jumps out" from the accept waiting process to receive KeyboardInterrupt command.

Here is an example of socket server:

import socket

host = "127.0.0.1"
port = 23333

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

sock.bind((host,port))
sock.listen()

sock.settimeout(0.5)

print("> Listening {}:{} ...".format(host,port))

try:
    while True:
        try:
            conn, addr = sock.accept()
            data = conn.recv(1024)
            if not data:
                print("x Client disconnected!")
                # break
            else:
                print("> Message from client: {}".format(data.decode()))
                msg = "> Message from server".format(data.decode()).encode()
                conn.sendall(msg)
        except socket.timeout:
            # print("Timeout")
            pass
        except KeyboardInterrupt:
            pass
except KeyboardInterrupt:
    print("Server closed with KeyboardInterrupt!")
    sock.close()
Hansimov
  • 597
  • 7
  • 10
3

Try adding a timeout to the socket, like so:

from socket import socket, AF_INET, SOCK_STREAM
sock = socket(AF_INET, SOCK_STREAM)
sock.bind(("localhost", 7777))
sock.settimeout(1.0)
sock.listen(1)
while True:
    try:
        connection, address = sock.accept()
        print("connected from " + address)
        received_message = sock.recv(300)
        if not received_message:
            break
        connection.sendall(b"hello")
    except IOError as msg:
        print(msg)
        continue    
    except KeyboardInterrupt:
        try:
            if connection:
                connection.close()
        except: pass
        break
sock.shutdown
sock.close()
Rolf of Saxony
  • 21,661
  • 5
  • 39
  • 60
  • dos it make a difference if I am on windows because doing this on a mac a keyboard interrupt is captured? – Zion Jan 19 '16 at 08:49
  • I couldn't tell you as I'm on Linux, try it and see :) – Rolf of Saxony Jan 19 '16 at 08:51
  • You can also use this for a send-only setup (i.e. not doing any `sock.recv()` because you're using TCP for what is essentially UDP) by using a `try` matched to an `except socket.timeout: continue` around just the `sock.accept()` line then use the `except KeyboardInterrupt:` outer block however you want. – Dan Dec 27 '19 at 00:07
3

I had this issue on Windows. Here's how I handle stopping the process:

    try:
        while self.running:
            try:
                c, addr = self.socket.accept()
                print("Connection accepted from " + repr(addr[1]))
                # do special stuff here...
                print("sending...")
                continue
            except (SystemExit, KeyboardInterrupt):
                print("Exiting....")
                service.stop_service()
                break
            except Exception as ex:
                print("======> Fatal Error....\n" + str(ex))
                print(traceback.format_exc())
                self.running = False
                service.stop_service()
                raise
    except (SystemExit, KeyboardInterrupt):
        print("Force Exiting....")
        service.stop_service()
        raise

def stop_service(self):
    """
    properly kills the process: https://stackoverflow.com/a/16736227/4225229
    """
    self.running = False
    socket.socket(socket.AF_INET,
                  socket.SOCK_STREAM).connect((self.hostname, self.port))
    self.socket.close()

Note that in order to trigger a KeyboardInterrupt exception, use:

Ctrl+Fn+PageUp(Pause/Break)

JacobIRR
  • 8,545
  • 8
  • 39
  • 68
3

The CTRL+C event can be caught in a separate process and sent back to another thread running in the main process to kill the socket. Example below, tested successfully on Windows 10 with Python 3.5.4. Placed some comments and print statements around so you can see what's happening.

from multiprocessing import Pipe, Process
from socket import socket, AF_INET, SOCK_STREAM
from threading import Thread
import time

def detect_interrupt(conn):
    try:
        print("Listening for KeyboardInterrupt...")
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        print("Detected KeyboardInterrupt!")
        print("Sending IPC...")
        conn.send(True)
        conn.close()

def listen_for_interrupt(conn, sock):
    print("Listening for IPC...")
    conn.recv()
    print("Detected IPC!")
    print("Closing sock...")
    sock.close()

if __name__ == "__main__":

    sock = socket(AF_INET, SOCK_STREAM)
    sock.bind(("localhost", 7777))
    sock.listen(1)

    # Crate a Pipe for interprocess communication
    main_conn, detect_conn = Pipe()
    # Create a thread in main process to listen on connection
    listen_for_interrupt_thread = Thread(
        target=listen_for_interrupt, args=(main_conn, sock), daemon=True)
    listen_for_interrupt_thread.start()
    # Create a separate process to detect the KeyboardInterrupt
    detect_interrupt_process = Process(
        target=detect_interrupt, args=(detect_conn,))
    detect_interrupt_process.start()

    connection = None
    try:
        while True:
            print("Running socket accept()")
            connection, address = sock.accept()
            print("connected from " + address)
            received_message = sock.recv(300)
            if not received_message:
                break
            connection.sendall(b"hello")
    except KeyboardInterrupt:
        print("Handling KeyboardInterrupt")
        sock.close()
        if connection:
            connection.close()
GratefulGuest
  • 777
  • 7
  • 15
1

For windows users,

Above solutions which try to catch KeyBoard interrupts don't seem to work. I ended up setting a timeout on my socket.

Something like: server_socket.settimeout(10)

Here an exception is raised after 10 seconds of inactivity (like not receiving anything for 10 secs)

Uddhav Bhosle
  • 43
  • 2
  • 8
1

If the far-end sends data rarely, you should set timeout for the connection as well. In this case the connection will raise timeout Exception, when the KeyboardInterrupt can be checked.

from socket import socket, AF_INET, SOCK_STREAM
sock = socket(AF_INET, SOCK_STREAM)
sock.bind(("localhost", 7777))
sock.settimeout(1.0)
sock.listen(1)
while True:
try:
    connection, address = sock.accept()
    connection.settimeout(1.0)
    print("connected from " + address)
    received_message = sock.recv(300)
    if not received_message:
        break
    connection.sendall(b"hello")
except socket.timeout:
    continue
except IOError as msg:
    print(msg)
    continue
except KeyboardInterrupt:
    try:
        if connection:
            connection.close()
    except: pass
    break
sock.shutdown
sock.close()
betontalpfa
  • 3,454
  • 1
  • 33
  • 65