Related
I'm fairly new to Python. I have two scripts running that are communicating with each other, but once the sender process stops sending bytes, the receiver process receives an endless stream of what decodes (UTF-8) to new lines. I've reduced the code as much as I could to keep things simple:
Sender Python script.
import socket
s = socket.socket()
host = "127.0.0.1"
port = 5409
s.bind((host, port))
data_to_send = ['1','2','3','4','5','6','7','8','9']
s.listen(1)
c, addr = s.accept()
print ('Got connection from ', addr,'. Sending data...', sep='')
for data in data_to_send:
message = data.encode('utf-8')
c.sendall(message)
Receiver Python script.
import socket
messages_received = 0
s = socket.socket()
host = "127.0.0.1"
port = 5409
s.connect((host, port))
while True:
incoming_message = s.recv(1024).decode('utf-8')
messages_received += 1
# This condition is just to avoid printing thousands of lines
if messages_received < 10:
print(messages_received, ':', incoming_message)
Receiver output.
1 : 1
2 : 23456789
3 :
4 :
5 :
6 :
7 :
8 :
9 :
What am I doing wrong? I would ideally want the sender script to break out of the "While True" loop if the socket closes.
As #jasonharper pointed out, all I needed to do was to check for empty messages and break the loop as soon as that happens. When the sender doesn't send anything, the receiver doesn't receive empty massages, it just waits for a valid message, which I didn't know. The following code worked for me:
Sender Python script.
import socket
import time
s = socket.socket()
host = "127.0.0.1"
port = 5409
s.bind((host, port))
data_to_send = ['1','2','3','4','5','6','7','8','9']
s.listen(1)
c, addr = s.accept()
print ('Got connection from ', addr,'. Sending data...', sep='')
for data in data_to_send:
message = data.encode('utf-8')
c.sendall(message)
time.sleep(1)
Receiver Python script.
import socket
messages_received = 0
s = socket.socket()
host = "127.0.0.1"
port = 5409
s.connect((host, port))
while True:
incoming_message = s.recv(1024).decode('utf-8')
messages_received += 1
if not incoming_message:
break
if messages_received < 10:
print(messages_received, ':', incoming_message)
Receiver output.
1 : 1
2 : 2
3 : 3
4 : 4
5 : 5
6 : 6
7 : 7
8 : 8
9 : 9
well you can try setting the buffer size on sender side :
socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1024) # Buffer size 1024
if it dosent work you can try even dict format so you send data as json format.
I'm trying to build a TCP-proxy script that sends and receives data, i managed to get it to listen but it doesn't seem to be connecting properly...my code looks right to me and after checking python docs(i'm trying to run it in python 2.7 and 3.6) i get this timeout message:
Output:
anon#kali:~/Desktop/python scripts$ sudo python TCP\ proxy.py 127.0.0.1 21 ftp.target.ca 21 True
[*] Listening on 127.0.0.1:21d
[==>] Received incoming connection from 127.0.0.1:44806d
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
self.run()
File "/usr/lib/python2.7/threading.py", line 754, in run
self.__target(*self.__args, **self.__kwargs)
File "TCP proxy.py", line 60, in proxy_handler
remote_socket.connect((remote_host,remote_port))
File "/usr/lib/python2.7/socket.py", line 228, in meth
return getattr(self._sock,name)(*args)
error: [Errno 110] Connection timed out
i looked into the file "/usr/lib/python2.7/socket.py" but couldn't really understand what i was looking for as it seemed right when i compared it to python docs and my script
my code:
# import the modules
import sys
import socket
import threading
#define the server
def server_loop(local_host,local_port,remote_host,remote_port,receive_first):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
server.bind((local_host, local_port))
server.listen(5)
print ("[*] Listening on %s:%sd" % (local_host, local_port))
except:
print("[!!] Failed to listen on %s:%sd" % (local_host,local_port))
print ("[!!] Check for others listening sockets or correct permissions")
sys.exit(0)
while True:
client_socket, addr = server.accept()
#print out the local connection information
print ("[==>] Received incoming connection from %s:%sd" % (addr[0],addr[1]))
#start a thread to talk to the remote host
proxy_thread = threading.Thread(target=proxy_handler,args=(client_socket,remote_host,remote_port,receive_first))
proxy_thread.start()
else:
print ("something went wrong")
def main():
#no fancy command-line parasing here
if len(sys.argv[1:]) !=5:
print ("Usage: ./TCP proxy.py [localhost] [localport] [remotehost] [remoteport] [receive_first]")
print("Example: ./TCP proxy.py 127.0.0.1 9000 10.12.132.1 9000 True")
#set up local listening parameters
local_host = sys.argv[1]
local_port = int(sys.argv[2])
#set up remote target
remote_host = sys.argv[3]
remote_port = int(sys.argv[4])
#this tells proxy to connect and receive data before sending to remote host
receive_first = sys.argv[5]
if "True" in receive_first:
receive_first = True
else:
receive_first = False
#now spin up our listening socket
server_loop(local_host,local_port,remote_host,remote_port,receive_first)
def proxy_handler(client_socket, remote_host, remote_port, receive_first):
#connect to the remote host
remote_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
remote_socket.connect((remote_host,remote_port))
#receive data from the remote end if necessary
if receive_first:
remote_buffer = receive_from(remote_socket)
hexdump(remote_buffer)
#send it to the repsonse handler
remote_buffer = respsonse_handler(remote_buffer)
#if data is able to be sent to local client, send it
if len(remote_buffer):
print ("[<==] Sending %d bytes to localhost." % len(remote_buffer))
client_socket.send(remote_buffer)
#now loop and read from local,sent to remote,send to local,rinse/wash/repeat
while True:
#read from local host
local_buffer = receive_from(client_socket)
if len(local_buffer):
print ("[==>] Received %d bytes from localhost." % len(local_buffer))
#send it to request handler
local_buffer = request_handler(local_buffer)
#send data to remote host
remote_socket.send(local_buffer)
print ("[==>] Sent to remote.")
#receive back response
remote_buffer = receive_from(remote_socket)
if len(remote_buffer):
print ("[<==] Received %d bytes from remote." % len(remote_buffer))
hexdump(remote_buffer)
#send response to handler
remote_buffer = response_handler(remote_buffer)
#send response to local socket
client_socket.send(remote_buffer)
print ("[<==] Sent to localhost.")
#if no data left on either side, close connection
if not len(local_buffer) or not len(remote_buffer):
client_socket.close()
remote_socket.close()
print ("[*] No more data, closing connections.")
break
#this is a pretty hex dumping function taken from the comments of http://code.activestate.com/recipes/142812-hex-dumper/
def hexdump(src, length=16):
result = []
digits = 4 if isinstance(src,unicode) else 2
for i in xrange(0,len(src), length):
s = src[i:i+length]
hexa = b' '.join(["%0*X" % (digits, ord(x)) for x in s])
text = b' '.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s])
result.append( b"%04X %-*s %s" % (i, length*(digits + 1), hexa, text) )
print (b'/n'.join(result))
def receive_from(connection):
buffer = ""
#set a 2 second timeout; depending on your target this may need to be adjusted
connection.settimeout(2)
try:
#keep reading the buffer until no more data is there or it times out
while True:
data = connection.recv(4096)
if not data:
break
buffer += data
except:
pass
return buffer
#modify any requested destined for the remote host
def request_handler(buffer):
#perform packet modifications
return buffer
#modify any responses destined for the local host
def response_handler(buffer):
#perform packet modifications
return buffer
main()
i have tried different ftp servers/sites,etc but get the same result, where am i going wrong with my code? any input or direction would be greatly appreciated.
okay so turns out my script is good just the ftp servers i was running weren't haha
this is the final output:
anon#kali:~/Desktop/python scripts$ sudo python TCP\ proxy.py 127.0.0.1 21 ftp.uconn.edu 21 True
[*] Listening on 127.0.0.1:21d
[==>] Received incoming connection from 127.0.0.1:51532d
0000 32 32 30 20 50 72 6F 46 54 50 44 20 31 2E 32 2E 2 2 0 P r o F T P D 1 . 2 ./n0010 31 30 20 53 65 72 76 65 72 20 28 66 74 70 2E 75 1 0 S e r v e r ( f t p . u/n0020 63 6F 6E 6E 2E 65 64 75 29 20 5B 31 33 37 2E 39 c o n n . e d u ) [ 1 3 7 . 9/n0030 39 2E 32 36 2E 35 32 5D 0D 0A 9 . 2 6 . 5 2 ] . .
[<==] Sending 58 bytes to localhost.
[==>] Received 353 bytes from localhost.
[==>] Sent to remote.
[<==] Received 337 bytes from remote.
I've seen and read a lot about this particular issue on the internet.
I am writing a simple chat server and client using socket in python for learning purpose mainly.
I've observed an issue here.
Here is my server code :
__author__ = 'pchakraverti'
import socket
import select
import sys
class NickSocketMap(object):
count = 0
def __init__(self, nick, client_socket):
self.nick = nick
self.client_socket = client_socket
NickSocketMap.count += 1
#staticmethod
def display_count():
print "Total number of clients is %d" % NickSocketMap.count
host = ""
port = 7575
socket_list = []
nick_list = []
cnt = 0
recv_buffer = 1024
def register_nick(nick, client_socket):
obj = NickSocketMap(nick, client_socket)
nick_list.append(obj)
def process_request(request_string, client_socket):
parts = request_string.split("|")
if parts[0] == "set_nick":
register_nick(parts[1], client_socket)
client_socket.send("nick_set")
elif parts[0] == "transmit_msg":
broadcast_message(parts[1], parts[2])
return 1
def broadcast_message(message, client_nick):
for s in nick_list:
if s.nick == client_nick:
try:
s.client_socket.send(message)
except socket.errno, ex:
print ex
break
def run_server():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.bind((host, port))
except socket.errno, ex:
print ex
sys.exit(-1)
sock.listen(10)
# add the parent socket in the list
socket_list.append(sock)
# keep the server alive
while True:
try:
read_ready, write_ready, in_error = select.select(socket_list, [], [], 0)
except select.error, ex:
print ex
continue
for s in read_ready:
# check if s is the parent socket
if s == sock:
# accept new connection and append to list
try:
con, addr = s.accept()
if con not in socket_list:
socket_list.append(con)
except socket.errno, ex:
print ex
else:
try:
# receive packet from connected client
packet = s.recv(recv_buffer)
if not packet:
socket_list.remove(s)
read_ready.remove(s)
for n in nick_list:
if n.client_socket == s:
nick_list.remove(n)
break
break
print packet
except socket.errno, ex:
print ex
continue
process_request(packet, s)
sock.close()
if __name__ == "__main__":
run_server()
and here is my client code:
__author__ = 'pchakraverti'
import socket
nick = ""
host = "192.168.0.167"
port = 7575
sock = ""
def welcome():
print "Welecome to SecuChat!"
print "---------------------"
def init():
nick = raw_input("Enter your chat nickname : ")
print nick
global sock
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect((host, port))
except socket.errno, ex:
print ex
sock.send("set_nick|"+nick)
#sock.close()
if __name__ == "__main__":
welcome()
init()
In the client code, when I don't do the sock.close(), the server runs into an exception :
Traceback (most recent call last):
File "server.py", line 102, in <module>
run_server()
File "server.py", line 84, in run_server
packet = s.recv(recv_buffer)
socket.error: [Errno 104] Connection reset by peer
how ever, when I add that line, the problem doesn't occur.
Now I've two questions :
i) I've handled exceptions in the server.py, why is this exception not being handled and why is it crashing the code ? How can I make the server more robust and what am I missing ?
ii) What is the logic behind this crash and exception in relation to the sock.close() line in the client ?
i) Your try-except block doesn't catch any exceptions.
The first argument to except must be the type of the exception you want to catch. socket.errno is not an exception class but a module. You need to catch socket.error:
except socket.error, ex:
print ex
It "crashes" your code because any exception that isn't handled somewhere in the call stack propagates outwards until it hits an except. If there is no handler the program is terminated.
ii) When the client terminates without closing the connection, a RST packet is sent by the TCP/IP stack of your OS. This is roughly the equivalent of hanging up a phone without saying goodbye. Python converts this into an exception with the text "Connection reset by peer". It simply means that since you called read() Python assumed you expect to receive something and when the connection suddenly disconnected, Python informs you of this by raising the exception.
So I'm making a port scanner in python...
import socket
ip = "External IP"
s = socket.socket(2, 1) #socket.AF_INET, socket.SOCK_STREAM
def porttry(ip, port):
try:
s.connect((ip, port))
return True
except:
return None
for port in range(0, 10000):
value = porttry(ip, port)
if value == None:
print("Port not opened on %d" % port)
else:
print("Port opened on %d" % port)
break
raw_input()
But this is too slow, I want to somehow be able to some how close or break code after a period of time of not returning anything.
In addition to setting socket timeout, you can also apply multi-threading technique to turbo boost the process. It will be, at best, N times faster when you have N ports to scan.
# This script runs on Python 3
import socket, threading
def TCP_connect(ip, port_number, delay, output):
TCPsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
TCPsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
TCPsock.settimeout(delay)
try:
TCPsock.connect((ip, port_number))
output[port_number] = 'Listening'
except:
output[port_number] = ''
def scan_ports(host_ip, delay):
threads = [] # To run TCP_connect concurrently
output = {} # For printing purposes
# Spawning threads to scan ports
for i in range(10000):
t = threading.Thread(target=TCP_connect, args=(host_ip, i, delay, output))
threads.append(t)
# Starting threads
for i in range(10000):
threads[i].start()
# Locking the main thread until all threads complete
for i in range(10000):
threads[i].join()
# Printing listening ports from small to large
for i in range(10000):
if output[i] == 'Listening':
print(str(i) + ': ' + output[i])
def main():
host_ip = input("Enter host IP: ")
delay = int(input("How many seconds the socket is going to wait until timeout: "))
scan_ports(host_ip, delay)
if __name__ == "__main__":
main()
here is a quick and simple port scanner, it scans 100000 ports in 180 sec:
import threading
import socket
target = 'pythonprogramming.net'
#ip = socket.gethostbyname(target)
def portscan(port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(0.5)#
try:
con = s.connect((target,port))
print('Port :',port,"is open.")
con.close()
except:
pass
r = 1
for x in range(1,100):
t = threading.Thread(target=portscan,kwargs={'port':r})
r += 1
t.start()
Consider setting a timeout instead of a for loop by using socket.setdefaulttimeout(timeout).
This should be a bit faster.
#-*-coding:utf8;-*-
#qpy:3
#qpy:console
import socket
import os
# This is used to set a default timeout on socket
# objects.
DEFAULT_TIMEOUT = 0.5
# This is used for checking if a call to socket.connect_ex
# was successful.
SUCCESS = 0
def check_port(*host_port, timeout=DEFAULT_TIMEOUT):
''' Try to connect to a specified host on a specified port.
If the connection takes longer then the TIMEOUT we set we assume
the host is down. If the connection is a success we can safely assume
the host is up and listing on port x. If the connection fails for any
other reason we assume the host is down and the port is closed.'''
# Create and configure the socket.
sock = socket.socket()
sock.settimeout(timeout)
# the SO_REUSEADDR flag tells the kernel to reuse a local
# socket in TIME_WAIT state, without waiting for its natural
# timeout to expire.
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Like connect(address), but return an error indicator instead
# of raising an exception for errors returned by the C-level connect()
# call (other problems, such as “host not found,” can still raise exceptions).
# The error indicator is 0 if the operation succeeded, otherwise the value of
# the errnovariable. This is useful to support, for example, asynchronous connects.
connected = sock.connect_ex(host_port) is SUCCESS
# Mark the socket closed.
# The underlying system resource (e.g. a file descriptor)
# is also closed when all file objects from makefile() are closed.
# Once that happens, all future operations on the socket object will fail.
# The remote end will receive no more data (after queued data is flushed).
sock.close()
# return True if port is open or False if port is closed.
return connected
con = check_port('www.google.com', 83)
print(con)
One can use threading.Thread and threading.Condition to synchronize port check and spawning new threads.
Script example usage:
python port_scan.py google.com 70 90
Checking 70 - 80
Checking 80 - 84
Checking 84 - 90
Found active port 80
Checking 90 - 91
Checking 91 - 94
All threads started ...
port_scan.py:
# import pdb
import socket, threading
from traceback import print_exc
class AllThreadsStarted(Exception): pass
class IPv4PortScanner(object):
def __init__(self, domain, timeout=2.0, port_range=(1024, 65535), threadcount=10):
self.domain = domain
self.timeout = timeout
self.port_range = port_range
self.threadcount = threadcount
self._lock = threading.Lock()
self._condition = threading.Condition(self._lock)
self._ports_active = []
self._ports_being_checked = []
self._next_port = self.port_range[0]
def check_port_(self, port):
"If connects then port is active"
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.settimeout(self.timeout)
try:
sock.connect((self.domain, port))
with self._lock:
self._ports_active.append(port)
print ("Found active port {}".format(port))
sock.close()
except socket.timeout, ex:
return
except:
print_exc()
# pdb.set_trace()
def check_port(self, port):
"updates self._ports_being_checked list on exit of this method"
try:
self.check_port_(port)
finally:
self._condition.acquire()
self._ports_being_checked.remove(port)
self._condition.notifyAll()
self._condition.release()
def start_another_thread(self):
if self._next_port > self.port_range[1]:
raise AllThreadsStarted()
port = self._next_port
self._next_port += 1
t = threading.Thread(target=self.check_port, args=(port,))
# update books
with self._lock:
self._ports_being_checked.append(port)
t.start()
def run(self):
try:
while True:
self._condition.acquire()
while len(self._ports_being_checked) >= self.threadcount:
# we wait for some threads to complete the task
self._condition.wait()
slots_available = self.threadcount - len(self._ports_being_checked)
self._condition.release()
print ("Checking {} - {}".format(self._next_port, self._next_port+slots_available))
for i in xrange(slots_available):
self.start_another_thread()
except AllThreadsStarted, ex:
print ("All threads started ...")
except:
print_exc()
if __name__ == "__main__":
import sys
domain = sys.argv[1]
port_s = int(sys.argv[2])
port_e = int(sys.argv[3])
scanner = IPv4PortScanner(domain=domain, port_range=(port_s, port_e))
scanner.run()
I think that this one snippet could help you : http://www.coderholic.com/python-port-scanner/
socket.setdefaulttimeout(0.5)
This will make the program faster!
socket.setdefualttimeout (time)
is used to keep trying to connect with port for perticular time...when you send request and there is timeout set for 2 seconds so it will try to connect with port for 2 seconds....if there will be no response from that port in 2 seconds....it will be count as a dead port
The following port scanner has a few constants defined at the top that you can modify as needed:
PURPOSE -- help message for the command line
PORTS -- range of ports you would like scanned
POOL_SIZE -- number of processes to scan with
TIMEOUT -- how long to wait for server connection
Feel free to adapt this according to your requirements. Maybe add some command line arguments?
#! /usr/bin/env python3
import argparse
import collections
import itertools
import multiprocessing
import operator
import socket
PURPOSE = 'Scan for open ports on a computer.'
PORTS = range(1 << 16)
POOL_SIZE = 1 << 8
TIMEOUT = 0.01
def main():
"""Get computer to scan, connect with process pool, and show open ports."""
parser = argparse.ArgumentParser(description=PURPOSE)
parser.add_argument('host', type=str, help='computer you want to scan')
host = parser.parse_args().host
with multiprocessing.Pool(POOL_SIZE, socket.setdefaulttimeout, [TIMEOUT]) \
as pool:
results = pool.imap_unordered(test, ((host, port) for port in PORTS))
servers = filter(operator.itemgetter(0), results)
numbers = map(operator.itemgetter(1), servers)
ordered = sorted(numbers)
print(f'Ports open on {host}:', *format_ports(ordered), sep='\n ')
field_names = 'family', 'socket_type', 'protocol', 'canon_name', 'address'
AddressInfo = collections.namedtuple('AddressInfo', field_names)
del field_names
def test(address):
"""Try connecting to the server and return whether or not it succeeded."""
host, port = address
for info in itertools.starmap(AddressInfo, socket.getaddrinfo(host, port)):
try:
probe = socket.socket(info.family, info.socket_type, info.protocol)
except OSError:
pass
else:
try:
probe.connect(info.address)
except OSError:
pass
else:
probe.shutdown(socket.SHUT_RDWR)
return True, port
finally:
probe.close()
return False, port
def format_ports(ports):
"""Convert port numbers into strings and show all associated services."""
if ports:
for port in ports:
try:
service = socket.getservbyport(port)
except OSError:
service = '?'
yield f'{port:<5} = {service}'
else:
yield 'None'
if __name__ == '__main__':
main()
I've just finished tinkering with Concurrent Futures on a port scanner and by God it's fast:
import concurrent.futures
import socket
def scan_port(domainip: str, port: int) -> tuple:
try:
# Use a faster socket implementation
s = socket.create_connection((domainip, port), timeout=0.5)
# Check if the connection was successful
if s:
return (port, "open")
else:
return (port, "closed")
except Exception as e:
print(f"Error scanning port {port}: {e}")
return (port, "error")
openports = {}
# Scan the ports in parallel using the faster scanning code
with concurrent.futures.ThreadPoolExecutor() as executor:
futures = [executor.submit(scan_port, domainip, port) for port in range(1, 1024)]
for future in concurrent.futures.as_completed(futures):
status = future.result()
if status[1] == "open":
openports[status[0]] = status[1]
I am writing a simple TCP server in python, and am trying to input a timeout. My current code:
import socket
def connect():
HOST = '' # Symbolic name meaning the local host
PORT = 5007 # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
s.settimeout(5)
conn, addr = s.accept()
print 'Connected by', addr
return conn
conn = connect()
while 1:
data = conn.recv(1024)
if not data: break
print data
conn.close()
Issue is when I try to connect I get an error at data = conn.recv(1024)
error: [Errno 10035] A non-blocking socket operation could not be completed immediately
Code works without the timeout.
You can turn on blocking:
# ...
conn.setblocking(1)
return conn
# ...
Try to set the timeout on the socket and the blocking on the connection. Like this:
import socket
def connect():
HOST = '' # Symbolic name meaning the local host
PORT = 5007 # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)
s.bind((HOST, PORT))
s.listen(1)
return s
s = connect()
while 1:
conn, addr = s.accept()
print 'Connected by', addr
conn.setblocking(1)
data = conn.recv(1024)
conn.close()
if not data: break
print data
s.close()
Ran into the same problem 30 minutes ago. Found a simple non-elegant work around...if you give the socket time to breathe by doing time.sleep(1), catching the 10035 error and doing a retry it works. I'm using 2.7.5...maybe this is a bug that got fixed. Not real sure.
Code sample...please understand this is very simplistic test code I use (only recv 1 byte at a time).So where 's' is the socket with a 10s timeout and 'numbytes' is number of bytes I need...
def getbytes(s,numbytes):
din = ''
socketerror10035count = 0
while True:
try:
r = s.recv(1).encode('hex')
din += r
if len(din)/2 == numbytes:
print 'returning',len(din)/2, 'bytes'
break
except socket.timeout as e:
din = 'socket timeout'
break
except socket.error as e:
if e[0] == 10035 and socketerror10035count < 5:
socketerror10035count = socketerror10035count +1
time.sleep(1)
else:
din = 'socket error'
break
except:
din = 'deaddead'
break
return din
For Python 3 and above, the above code which references e as a scriptable object will need to be changed to "e.errno". And, of course the print statements require parenthesis around the arguments.
Additionally, you may want to change the "except socket.error as e:" line to "except BlockingIOError as e:". However, the code works as is under Python 3.8.5 on Windows.