I can run two instances of my program and connect them to each other, but when someone else at a different ip trys to connect to my server their socket fails to connect. My code doesn't have input the ip of the users computer, but has my ip(as I am going to be the only person running the server) hard coded in.
Here is the server class connect function:
def connect(self,host,port):
self.host = host
self.port = port
self.unconnected_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.unconnected_socket.bind((self.host,self.port))
self.unconnected_socket.listen(5)
This is the when the program creates a game
self.server = server.Server()
self.server.connect(ip,port) #ip is my computer's ip address
self.serverThread = Thread(target=self.server.serve_forever)
self.serverThread.daemon = True
self.serverThread.start()
self.client.connect(ip,port)
Now here is the class client connect function:
def connect(self,host,port):
self.host = host
self.port = port
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((self.host,self.port))
When someone on a different computer runs the program but chooses to join the game:
self.client.connect(ip,port)
I am using a slightly modified Mastermind Networking Lib - 1.5.2 from pygames.
Blind guess: You are probably binding your server to the address 127.0.0.1 (or localhost). This address cannot be reached from outbound connections.
Instead, you should be setting the host of the bound address to socket.INADDR_ANY
Related
I tried setting up a TCP socket with Python, and it works totally fine as long as I'm in the same network. But my problem is that I can't get it to work if one of the devices isn't in the same network.
I already tried setting the bind IP to '0.0.0.0', and if I try connecting I get socket error 10060. I think the problem probably has something to do with port-forwarding, but I don't know how to do it in Python.
Here is the code I used to test it, and I don't know how I get the public IP from my device in Python (not the gethostbyname() function, that only returns the private IP).
I hope you can help me.
Server.py
HOST = '0.0.0.0'
PORT = 5000
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT))
s.listen()
conn, addr = s.accept()
with conn:
while True:
text = input("Enter text: ").encode("utf-8")
send_msg(conn,text)
echo = recv_msg(conn).decode("utf-8")
print(echo)
client.py
#HOST = '192.168.0.220'
HOST = 'x.x.x.x' #ip address of server
PORT = 5000`
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
while True:
data = recv_msg(s)
send_msg(s,data)
def send_msg(sock, msg):
msg = struct.pack('>Q', len(msg)) + msg
sock.sendall(msg)
def recv_msg(sock):
raw_msglen = recvall(sock, 8)
if not raw_msglen:
return None
msglen = struct.unpack('>Q', raw_msglen)[0]
return recvall(sock, msglen)
def recvall(sock: socket.socket, n):
data = bytearray()
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data.extend(packet)
return data
The code shown is fine. bind()'ing the server to 0.0.0.0 is typically the correct thing to do, that allows it to listen on all local IPs installed on its machine. Or, you can bind() it to the specific IP of the adapter that is connected to your network. Either way is fine.
On the client side, if the client is on the same network, it can connect() to the server's LAN IP, and all is well.
The problem comes when the client is not on the same network. That means the client simply cannot connect to your server directly. There is no physical route to facilitate that connection. So, to remedy that, you must setup a Port Forwarding rule in your LAN router. You must open a public IP/Port on the router's WAN, and map it to the server's LAN IP/Port. Then the client will be able to connect to the router's WAN IP/Port and let the router forward the packets to the server's LAN IP/Port, and vice versa.
If your router supports uPNP, your server code can setup this Port Forwarding programmably. There are uPNP libraries available, or your OS may even have APIs for that. Otherwise, your router admin must setup the Port Forwarding rule by hand.
To discover your router's WAN IP for the client to connect to, your server code can query an external site like https://whatismyip.com, or even query the router directly (if the router has an API for this purpose). Otherwise, you should subscribe to a 3rd party Dynamic DNS service that assigns you a static hostname that the client can always connect to, and then your server code, or even the router itself, can update the IP for that hostname whenever the router's WAN IP changes.
I have been working on a project to connect computers located in different locations together through Python. Initially, while testing, I used my private IP address (I did not know it was private at the time) to connect computers on the same network as mine. But as soon as I tried doing this with computers located on different networks in different locations, it simply did not work.
And I assume this is because the program is using the local IP address of my computer that can connect only to computers on the same network. Here are my simplified programs:
Here is my server-side script:
server = socket.gethostbyname(socket.gethostname()) # 10.128.X.XXX which is the Internal IP
print(server)
port = 5555
clients = 0
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((server, port))
s.listen(2)
print("Waiting for connection...")
while True:
conn, addr = s.accept()
print("Connected to: ", addr)
conn.send(str.encode(f"{clients}"))
clients += 1
and here is my client side-script:
class Network:
def __init__(self):
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server = "10.128.0.2"
self.port = 5555
self.addr = (self.server, self.port)
self.id = int(self.connect())
def connect(self):
self.client.connect(self.addr)
return self.client.recv(2048).decode()
network = Network()
print(f"Connected as client {network.id}")
Now when I tried replacing the private IP address with the global IP address (as specified here: How do I get the external IP of a socket in Python?) I got the following error:
# Getting the Global IP Address
from requests import get
server = get("https://api.ipify.org").text
s.bind((server, port))
OSError: [WinError 10049] The requested address is not valid in its context
I have tried searching a lot on how to communicate (transfer small amounts of data as strings) between multiple computers located in different locations using different networks, but I haven't really gotten a solution. Is there a way that I can do this?
In server you always use local IP (it is IP of one of network cards in computer or 0.0.0.0 to use all network cards)
s.bind( (local_IP, port) )
# or
s.bind( ('0.0.0.0', port) )
In client you use external IP
s.connect( (external_IP, port) )
External client uses external IP to connect with your Internet Provider route and this router knows that this external IP is assigned to your computer and it redirects it your server.
At the same time local client can use local IP to connect with the same server.
external_client --> router(externa_IP) --> server(local_IP) <-- local_client
I have written a simple program to connect a client to a server in python.
When I run this on my local network with my local IP address, everything works fine.
I've gotten a google cloud server and a linux VM on it. I've uploaded server.py onto the server,
but when I run it, I cannot connect from my computer with the client code.
I've tried pinging the server from my computer and that works. I also looked at which ports were being
listened to on the server side. When I left the code as below, port 5555 was not listed.
When I replaced the server in server.py by server = "", then tested for which ports were being listened to,
port 5555 was being used, but not by the public IP address, instead by the local one.
Here is the code (I've removed everything irrelevant to establishing the network connection):
server.py:
import socket
from _thread import *
import pickle
server = "34.89.182.513"
port = 5555
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.bind((server, port))
except socket.error as e:
str(e)
s.listen(4)
print("Waiting for a connection, Server Started")
def threaded_client(conn, player_id):
pass # I've removed this code, since it is not relevant to question
while True:
conn, addr = s.accept()
print("Connected to:", addr)
start_new_thread(threaded_client, (conn, 0))
client.py:
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect("34.89.182.513", 5555)
The google cloud VM lists two different IP addresses, one private and one public. I've been using the public
one. Does that have anything to do with the problem?
I am new to socket library and server side programming. I made 2 scripts which runs perfectly on my machine i.e. server.py and client.py. But when i test it on two different computers it doesn't worked.
What i want is to make my server.py file connected to client.py,
where server.py will run on my machine and it will be connected to
client.py on a separate machine at any location in the world.
I just know socket only. But if this problem can be solved by use of other library, then also it will be fine.
Here is my code:
server.py
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = socket.gethostbyname(socket.gethostname())
port = 12048
s.bind((host, port))
s.listen()
print("Server listening # {}:{}".format(host, port))
while True:
c, addr = s.accept()
print("Got connection from", addr)
c.send(bytes("Thank you", "utf-8"))
client.py
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = '192.168.1.162' # The IP printed by the server must be set here
port = 12048
s.connect((socket.gethostname(), port))
msg = s.recv(1024)
print(msg.decode("utf-8"))
I don't know how it's possible but if it is then please answer this.
Also, i want to receive files from client.py to my machine. Is it possible in socket or i have to import any other library?
Any help will be appreciated.
The reason the client will only connect to the server running on the same computer is because you are using s.connect((socket.gethostname(), port)) instead of s.connect((host, port)). Your host IP variable is never being used. This error means that the client will be trying to connect to its own hostname, which would be itself, and so that is why it only works on one single computer.
You should modify client.py like this:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = '192.168.1.162' # Make sure this is set to the IP of the server
port = 12048
s.connect((host, port))
msg = s.recv(1024)
print(msg.decode("utf-8"))
Now you will be able to connect to a server running on a different computer.
In Client.py you're connecting the socket to socket.gethostname() instead of the ip address of your server. Now, your client is trying to a server that should be running on the same ip as the client. Logically this will work when server and client run on the same ip, but when the client resides on another machine you need to connect to the correct ip address:
s.connect((host, port))
Also, make sure that port is actually open and not blocked by another program. This website helped me open port 7777 on two different laptops and run your edited code on them. You can do the same for port 12048.
Right-click the Start button.
Click Search.
Type Windows Firewall.
Click Windows Firewall.
Click Advanced settings.
Click Inbound Rules in the left frame of the window.
Click New Ruleā¦ in the right frame of the window.
Click Port.
Click Next.
Click either TCP or UDP.
Click Specific local ports.
Type a port number. (In this case, we will open port 12048.)
Click Next.
Click Allow the connection.
Click Next.
Click any network types you'd like to allow the connection over.
Click Next.
Type a name for the rule.
Click Finish.
I believe for a socket you have to open the TCP port but if that doesn't work you can make a new rule for the UDP port as well.
I tried the basic programs for client and server from realpython (https://realpython.com/python-sockets/#echo-client-and-server)
While these work fine when running on the same computer, there is following problem when trying on different machines:
ConnectionRefusedError: [WinError 10061] No connection could be made because the target machine actively refused it
Client code:
HOST = '10.0.0.55' # The server's hostname or IP address
PORT = 65432 # The port used by the server
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
s.sendall(b'Hello, world')
data = s.recv(1024)
print('Received', repr(data))
Server Code:
import socket
HOST = '127.0.0.1' # Standard loopback interface address (localhost)
PORT = 65432 # Port to listen on (non-privileged ports are > 1023)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT))
s.listen()
conn, addr = s.accept()
with conn:
print('Connected by', addr)
while True:
data = conn.recv(1024)
if not data:
break
conn.sendall(data)
I can make pings from one computer to the other.
Firewall is turned down
Wireshark shows that the SYN message arrives on the second computer which is answered by a RST message (Wireshark PC server)
If you want the server to be open to other computers, you can't listen on 127.0.0.1 which is basically an inner local loop only located on the computer running the program (that's why it's called loopback in comments). You should have the server listen on its own real address (for example: 10.0.0.55 explicitly).
This however can be annoying if your host can change addresses, an easy workaround is to just use the local IP address like this (on the server):
HOST = socket.gethostbyname(socket.gethostname())
Or if you specifically want to use the address from one network interface:
HOST = '10.0.0.55'
Or, if you want to listen on all network interfaces:
HOST = '0.0.0.0'