Spaces:
Sleeping
Sleeping
# Copyright (c) Microsoft Corporation. All rights reserved. | |
# Licensed under the MIT License. See LICENSE in the project root | |
# for license information. | |
import socket | |
import sys | |
import threading | |
from debugpy.common import log | |
from debugpy.common.util import hide_thread_from_debugger | |
def create_server(host, port=0, backlog=socket.SOMAXCONN, timeout=None): | |
"""Return a local server socket listening on the given port.""" | |
assert backlog > 0 | |
if host is None: | |
host = "127.0.0.1" | |
if port is None: | |
port = 0 | |
try: | |
server = _new_sock() | |
if port != 0: | |
# If binding to a specific port, make sure that the user doesn't have | |
# to wait until the OS times out the socket to be able to use that port | |
# again.if the server or the adapter crash or are force-killed. | |
if sys.platform == "win32": | |
server.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1) | |
else: | |
try: | |
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
except (AttributeError, OSError): # pragma: no cover | |
pass # Not available everywhere | |
server.bind((host, port)) | |
if timeout is not None: | |
server.settimeout(timeout) | |
server.listen(backlog) | |
except Exception: # pragma: no cover | |
server.close() | |
raise | |
return server | |
def create_client(): | |
"""Return a client socket that may be connected to a remote address.""" | |
return _new_sock() | |
def _new_sock(): | |
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP) | |
# Set TCP keepalive on an open socket. | |
# It activates after 1 second (TCP_KEEPIDLE,) of idleness, | |
# then sends a keepalive ping once every 3 seconds (TCP_KEEPINTVL), | |
# and closes the connection after 5 failed ping (TCP_KEEPCNT), or 15 seconds | |
try: | |
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) | |
except (AttributeError, OSError): # pragma: no cover | |
pass # May not be available everywhere. | |
try: | |
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1) | |
except (AttributeError, OSError): # pragma: no cover | |
pass # May not be available everywhere. | |
try: | |
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 3) | |
except (AttributeError, OSError): # pragma: no cover | |
pass # May not be available everywhere. | |
try: | |
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5) | |
except (AttributeError, OSError): # pragma: no cover | |
pass # May not be available everywhere. | |
return sock | |
def shut_down(sock, how=socket.SHUT_RDWR): | |
"""Shut down the given socket.""" | |
sock.shutdown(how) | |
def close_socket(sock): | |
"""Shutdown and close the socket.""" | |
try: | |
shut_down(sock) | |
except Exception: # pragma: no cover | |
pass | |
sock.close() | |
def serve(name, handler, host, port=0, backlog=socket.SOMAXCONN, timeout=None): | |
"""Accepts TCP connections on the specified host and port, and invokes the | |
provided handler function for every new connection. | |
Returns the created server socket. | |
""" | |
assert backlog > 0 | |
try: | |
listener = create_server(host, port, backlog, timeout) | |
except Exception: # pragma: no cover | |
log.reraise_exception( | |
"Error listening for incoming {0} connections on {1}:{2}:", name, host, port | |
) | |
host, port = listener.getsockname() | |
log.info("Listening for incoming {0} connections on {1}:{2}...", name, host, port) | |
def accept_worker(): | |
while True: | |
try: | |
sock, (other_host, other_port) = listener.accept() | |
except (OSError, socket.error): | |
# Listener socket has been closed. | |
break | |
log.info( | |
"Accepted incoming {0} connection from {1}:{2}.", | |
name, | |
other_host, | |
other_port, | |
) | |
handler(sock) | |
thread = threading.Thread(target=accept_worker) | |
thread.daemon = True | |
hide_thread_from_debugger(thread) | |
thread.start() | |
return listener | |