|
26 | 26 | socket.setdefaulttimeout() -- set the default timeout value |
27 | 27 | create_connection() -- connects to an address, with an optional timeout and |
28 | 28 | optional source address. |
| 29 | +create_server() -- create a TCP socket and bind it to a specified address. |
29 | 30 |
|
30 | 31 | [*] not available on all platforms! |
31 | 32 |
|
@@ -748,3 +749,64 @@ def getaddrinfo(host, port, family=0, type=0, proto=0, flags=0): |
748 | 749 | _intenum_converter(socktype, SocketKind), |
749 | 750 | proto, canonname, sa)) |
750 | 751 | return addrlist |
| 752 | + |
| 753 | + |
| 754 | +def create_server(address, family=None, backlog=None, reuse_port=False): |
| 755 | + """Convenience function which creates a SOCK_STREAM type socket |
| 756 | + bound to *address* (a 2-tuple (host, port)) and return the socket |
| 757 | + object. |
| 758 | +
|
| 759 | + *family* should be either AF_INET or AF_INET6. |
| 760 | + *backlog* is the queue size passed to socket.listen(). |
| 761 | + *reuse_port* dictates whether to use the SO_REUSEPORT socket option. |
| 762 | + *dualstack_ipv6*: if true and the platform supports it, it will |
| 763 | + create an AF_INET6 socket able to accept both IPv4 or IPv6 |
| 764 | + connections. When false it will explicitly disable this option on |
| 765 | + platforms that enable it by default (e.g. Linux). |
| 766 | +
|
| 767 | + >>> with create_server(('', 8000)) as server: |
| 768 | + ... while True: |
| 769 | + ... conn, addr = server.accept() |
| 770 | + ... # handle new connection |
| 771 | + """ |
| 772 | + if family is None: |
| 773 | + family = getattr(socket, "AF_UNSPEC", 0) |
| 774 | + |
| 775 | + host, port = address |
| 776 | + err = None |
| 777 | + |
| 778 | + # AI_PASSIVE tells getaddrinfo we intend to bind() to this address |
| 779 | + try: |
| 780 | + addr_infos = getaddrinfo( |
| 781 | + host, port, family, SOCK_STREAM, 0, AI_PASSIVE |
| 782 | + ) |
| 783 | + except gaierror as e: |
| 784 | + raise OSError(f"getaddrinfo failed for {address}: {e}") |
| 785 | + |
| 786 | + for res in addr_infos: |
| 787 | + af, socktype, proto, canonname, sa = res |
| 788 | + sock = None |
| 789 | + try: |
| 790 | + sock = socket(af, socktype, proto) |
| 791 | + |
| 792 | + # Standard server socket option |
| 793 | + sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) |
| 794 | + |
| 795 | + if reuse_port: |
| 796 | + if hasattr(socket, "SO_REUSEPORT"): |
| 797 | + sock.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1) |
| 798 | + |
| 799 | + sock.bind(sa) |
| 800 | + sock.listen(backlog if backlog is not None else SOMAXCONN) |
| 801 | + |
| 802 | + return sock # Success! |
| 803 | + |
| 804 | + except OSError as e: |
| 805 | + err = e |
| 806 | + if sock is not None: |
| 807 | + sock.close() |
| 808 | + |
| 809 | + # If we get here, no address worked |
| 810 | + if err is not None: |
| 811 | + raise err |
| 812 | + raise OSError(f"Could not create server on {address}") |
0 commit comments