Reputation: 872
Below I have two scripts that work when I bind the server.py
to my computers IPV4 address and connect the client.py
to that same address. The next step I'm trying to take is the ability to use this socket across different networks. I thought that it would be as simple as changing the value of SERVER
to my public IP address (which can be found by clicking this link). When I make that change, I get this error upon running server.py
:
Traceback (most recent call last):
File "C:\Users\gmbra\Downloads\Python Programs\Concepts\Sockets\server.py", line 13, in <module>
server.bind(ADDRESS)
OSError: [WinError 10049] The requested address is not valid in its context
import socket
import threading
PORT = 5050
# SERVER = '192.168.0.30'
SERVER = socket.gethostbyname(socket.gethostname()) # Change this to the public IP address to allow access across multiple networks
ADDRESS = (SERVER, PORT)
HEADER = 64 # The number of bytes expected to get from the client
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "!DISCONNECT"
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # AF_INET is the type of addresses we are looking for
server.bind(ADDRESS)
def handle_client(conn, address):
print(f"[NEW CONNECTION] {address} connected.")
while True:
msg_length = conn.recv(HEADER).decode(FORMAT) # This line also blocks until it receives a message
if msg_length:
msg_length = int(msg_length)
msg = conn.recv(msg_length).decode(FORMAT)
if msg == DISCONNECT_MESSAGE:
conn.close()
break
print(f'[{address}] {msg}')
conn.send("Message Received".encode(FORMAT))
def start():
server.listen()
print(f'[LISTENING] Server is listening on {SERVER}.')
while True:
conn, address = server.accept() # This line will wait until it finds a connection.
thread = threading.Thread(target=handle_client, args=(conn, address))
thread.start()
print(f"[ACTIVE CONNECTIONS] {threading.active_count() - 1}.")
print("[STARTING] Server is starting...")
start()
import socket
HEADER = 64
PORT = 5050
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = '!DISCONNECT'
SERVER = "192.168.0.30"
ADDRESS = (SERVER, PORT)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(ADDRESS)
# The pickle module could be used to send objects instead of strings.
def send(msg):
message = msg.encode(FORMAT)
# For some reason we have to send a byte string first with the integer size of the message, concatenated with spaces
# until it hits the length of the header.
send_length = str(len(message)).encode(FORMAT)
send_length += b' '*(HEADER - len(send_length))
client.send(send_length)
client.send(message)
print(client.recv(2048).decode(FORMAT))
while True:
message_ = input('Message: ')
send(message_)
if message_ == "!DISCONNECT":
break
What must I do to make this work across different networks?
Upvotes: 0
Views: 1508
Reputation: 598001
The server socket can bind only to an IP that is local to the machine it is running on (or, to the wildcard IP 0.0.0.0
to bind to all available local IPs). Clients running on the same network can connect to the server via any LAN IP/Port the server is bound to.
However, when the server machine is not directly connected to the Internet, but is running behind a router/proxy, the server socket cannot bind to the network's public IP. So, for what you are attempting, you need to bind the server socket to its local LAN IP (or wildcard) and Port, and then configure the network router to port-forward inbound connections from its public WAN IP/Port to the server's private LAN IP/Port. Then, clients running on other networks can connect to your public WAN IP/Port, and then be forwarded to the server.
If the router supports UPnP (Universal Play-by-play) and it is enabled, the server code can programmably setup the port forwarding rules dynamically. Otherwise, the rules need to be configured manually in the router's configuration by an admin.
Upvotes: 2