So far I have made a VERY basic client/server application that creates a TCP connection. I have a lot of programming experience, just never did this low-level stuff and especially nothing with networks. Note that all the prints are just to help me figuring out what is going on. One of the known issues is that jsonip sometimes gives me an IPv4 and sometimes v6, I don't know why but that doesn't matter for now, just to warn anyone who wants to recreate my code.
Server:
import socket
import requests
port = int(input("Enter port you want to open:\n"))
#todo: add errorhandling
print("Adding socket...")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
hostname = socket.gethostname()
print(f"Hostname: {hostname}")
ip_address = socket.gethostbyname(hostname)
print(f"Host address: {ip_address}")
r = requests.get(r'http://jsonip.com')
public_ip_address = r.json()['ip']
s.bind((ip_address, port))
print("Is open for connections on IP: "+public_ip_address+" and Port: "+str(port))
s.listen(5)
print("Done initialisation, listening for incoming connections...")
while True:
clientsocket, address = s.accept()
print(f"Connection from {address} has been established")
clientsocket.send(bytes(f"You have connected to server: {hostname}", "utf-8"))
Client:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ip = input("Enter IP to connect to:\n")
port = int(input("Enter Port to connect to:\n"))
print(f"Connecting to server {ip} ...")
s.connect((ip, port))
msg = s.recv(1024)
print(msg.decode("utf-8"))
On my local machine: Open 20000 in my server.py, it tells me the host is 127.0.1.1, I then enter 127.0.1.1 into my client script and 20000, and they connect. So the Socket has been bound with the 127.0.1.1. (Side question: What is this IP address, is it like the internal IP address of processes in my PC or something? If running ip a on my other machine it is the first one shown of 2)
Using Virtmanager on my machine and running one Linux Server (command line only) and one normal Ubuntu, the server tells me the host is, again, 127.0.1.1 which I don't need to enter into the other VM to know it won't work, what does work however, is getting the IP-address of the Server via ip a, which in this case is 192.168.122.37, and when I enter this IP address into the client, it connects. But in the socket here I bind, again, the 127.0.1.1, so is it arbitrary what I put here? What SHOULD I bind here, the public, the weird or the 192. address?
The first thing I could not get to work was using 2 physical devices. When opening a server on my Linux machine, I cannot connect with my windows machine at all, no matter if I use my public, my 127. or my 192. IP-address. Now my end goal is doing this over the internet so I am walking myself up, describing here the steps I took to try and get where I want to be but here I hit a brick wall where I don't know what is wrong. Am I binding the wrong address on the server, is my router being a problem, is there something else wrong?
I also tried leaving my network using my friends pc a few countries over, but this also just results in a timeout (my theory is that the Router port he is trying to open is closed and I have now idea how I can make the router send data to his PC, which should be not impossible as firefox and every application using internet does it without me having to manually forward every port, I just don't know how). This is my end goal, creating a connection between my friends PC and mine, and this is how far I got (I wouldn't mind skipping the local network if it is not relevant for fixing the global connection problem), so, tl;dr: what did i do wrong, what do i need to bind and what do i need to do for the final result to work?
There are many questions to answer.
Addresses 127.X.X.X are reserved for the loopback interface, most common one is 127.0.0.1. The loopback is a virtual, but important interface and as you have probably guessed, it is usable on the local machine only. You cannot use 127.X.X.X address to make two hosts to communicate with each other.
Addresses 192.168.X.X (and also 10.X.X.X and 172.16-31.X.X.) are reserved for local LANs. They are not valid on the Internet.
You cannot use these addresses to make two hosts to communicate with each other over the public Internet (unless you create a tunnel, an advanced networking topic)
Almost everybody uses them, because we ran out of IPv4 addresses long time ago, they were difficult to get, expensive, etc. Also such hosts are isolated from the Internet, they can be reached only via a router that translates addresses. Such router feature is called NAT. A typical router has one valid Internet address and all connections to the Internet appear as coming from the router. If you contant a service like jsonip.com from a PC, you get your router's address, not your PC's address.
See also: Finding local IP addresses using Python's stdlib
To make your program working, make it to accept connections on all interfaces. See the first example in the socket docs. On Linux, use port numbers >= 1024. Ports < 1024 are reserved, not available to regular users.
Final point is that a firewall may prevent connections to your server. It depends on your system and setup.
Related
I need my own IP in a small script and in order not to hardcode it, I`ve found a piece of code from here(stackoverflow) that works.
This--
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.connect(("8.8.8.8", 80))
print(s.getsockname()[0])
--
What is not clear for me is why it only works on UDP and not TCP? It has something to do with the google dns server? Thanks in advance.
This has nothing to do with Google and nothing to do with DNS.
All what this code does is to "connect" a UDP socket to an external IP, so that the OS kernel figures out which local IP address it needs to use in order to reach this external system. This is no real connection, i.e. there is no traffic involved but the OS kernel is only checking routing tables and local interfaces in order to decide which IP address to use as source in case one would actually use the socket to send data.
One could do the same with TCP. But in this case a real TCP connection would be established which means that actual traffic would be exchanged and that the connect would fail if the external system would not be reachable on this port (i.e. no listener, firewall in between etc).
With UDP instead connect will not produce any traffic and would fail only if no route to the destination IP address could be determined. This also means that an arbitrary external IP address and port could be used, i.e. ('1.1.1.1',11) would work the same as ('8.8.8.8',80).
this code below is working fine between two computers on the same network but it doesn't work between two computers on two different networks and i have tried to use the public IP address
clint
import socket
s = socket.socket()
port = 10000
ip=input("what is ip : ")
s.connect((ip, port))
print (s.recv(1024))
s.close
server
import socket
s = socket.socket()
ip=socket.gethostbyname(socket.gethostname())
print (ip)
port = 10000
s.bind((ip, port))
s.listen(1)
while True:
c, addr = s.accept()
print ('Got connection from', addr)
c.send(bytes([int(1)]))
c.close()
this question have already been asked many times but all the answers i could found is that i have to do " port forwarding " with out any code examples how to do it in some of the answers they say i should download application that will do the port forwarding to me but i don't know how to use it then in my python code ?
Port forwarding is something you do at the router level. For example if the two computers you are trying to connect are behind two different WiFi routers at two different homes then you need to set both WiFi routers in both locations to forward port 10000 (the one you're using) to forward to the internal IP of the computer where your code is running.
Your WiFi router has a public IP (what you're probably using now) and your computers running your python code have local IPs internal to the network that are assigned by the WiFi routers. Port forwarding takes traffic going to the public IP of a router and forwards it internally in the local network to the computer at some local IP that runs your program.
In other words this is not something you will do in your code — this is something you will do in your router's software.
If you are feeling ambitious you can follow this tutorial that removes the need to configure your devices for port forwarding. Assuming you have uPNP capable routers.
https://www.electricmonk.nl/log/2016/07/05/exploring-upnp-with-python/
And there may even be a module that takes all the leg work out.(not checked how it works)
https://pypi.python.org/pypi/UPnP/1.3
I have a server running by using python's base http server. The host name used is '127.0.0.1' the local host, and the port number is set to 8000. I have the public ip address of the computer operating this server.
If I wanted to send a http get request to this from another computer, what would I type into my browser?
Sounds like you've got your server process running on the wrong interface. 127.0.0.1 is not a hostname but an IP address, specifically the local loopback address. It is not reachable from any other machine (unless something's gone tragically wrong with your network configuration).
You can run anything you like on the 127.0.0.1 interface, and no one else can directly connect to it from a remote machine. That's pretty much the point --- it's for testing programs that use the Internet Protocol, and (in recent years) for starting single-user servers without worrying about security. (Python 2's SimpleHTTPServer does this, as do some personal wikis, and I think iPython Notebook.)
The public address for the host running your Web server is a completely unrelated network interface, with its own hardware and its own port 8000. It doesn't know or care that you've got something listening on some other interface's port 8000, so it should refuse attempts to connect to that port.
Since you didn't post any code, I have no idea what you need to change to get your server running on the correct interface. Assuming you've more or less followed the example in the BaseHTTPServer.HTTPServer docs:
def run(
server_class=BaseHTTPServer.HTTPServer,
handler_class=BaseHTTPServer.BaseHTTPRequestHandler,
):
server_address = ('', 8000) # <----= Replace the string.
httpd = server_class(server_address, handler_class)
httpd.serve_forever()
That server_address tuple is a string containing the IP address ('1.2.101.202' or whatever), followed by an integer port number. So replace the string with your host machine's public-facing IP address.
Note that port 8000 is outside the reserved range (0 up to but not including 1024), so it's possible that some unrelated service is already using that port. (Numerous applications are already squatting port 8000.) If so, you'll just have to choose another port number. You can chose anything from 1024 up to but not including 65536, but as with 8000, someone else might already be using it.
Depending on your operating system and its security setup, you might not have permission to open a socket that listens on an arbitrary port number. If so, that's between you and your ISP or sysadmin.
http://yourip:port/func
yourip is your public ip.
port is 8080
func is your registered function.
and also make sure you port is opened
I recently wrote a client/server pair in python using sockets,but the problem is client doesn't connect to server on another network.I've tried port forwarding and making internal IP address static, a question which really bother's me is do i need External/Public IP address to make the client connect and if this is the case what to do when the ISP changes my External IP address. Please give some suggestions,thanks.
code:
PORT=8888
srvsock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
srvsock.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 )
srvsock.bind( ('', PORT) )
srvsock.listen( 10 )
print 'server now listening on PORT '+str(PORT)
while 1:
clisock, (remhost, remport) = srvsock.accept()
dl_information_file="server.txt"
if os.path.exists(dl_information_file):
f=open('server.txt','rb')
read=f.read()
clisock.send( read )
f.close()
First of all, try to run both server and client from the same computer (connect to localhost)
If that works your problem is port forwarding related.
see: how to portforward
Are the client and server on a local network or is the client and server separated by the Internet?
If the server is running on a machine behind a NAT, you will have to do portforwarding on that and make sure that the machine's IP is static or that you update the client with the updated IP.
If the server is within your own network, you can use socket.gethostbyname(socket.getfqdn()) to get the IP of your interface (careful, you may have more than one) and use that IP to bind a socket.
You may also use WireShark to troubleshoot the connection - you can see what packets are making their way out of your client and what packets are making their way to your server.
Without further code and more information on the network, it's really hard to say any more - it could be the firewall, it could be the NAT, it could be a badly configured interface.
edit: It appears that you're binding the socket to '', which means it should be bound to the localhost port, meaning that the server listens for connections on the local loopback (127.0.0.1). This interface is not accessible to any other machine that the one which the server is running on.
You should use either a statically configured IP in a variable in your script, or that socket.gethostbyname(socket.getfqdn())).
I am playing with my python server, but I'm through with using localhost and I want to go over the internet. My code thus-far is:
import socket
import threading
import socketserver
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
data = self.request.recv(1024)
cur_thread = threading.current_thread()
response = "{}: {}".format(cur_thread.name, data)
self.request.sendall(b'worked')
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
def client(ip, port, message):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((ip, port))
try:
sock.sendall(message)
response = sock.recv(1024)
print("Received: {}".format(response))
finally:
sock.close()
if __name__ == "__main__":
# Port 0 means to select an arbitrary unused port
HOST, PORT = "0.0.0.0", 9001
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
ip, port = server.server_address
# Start a thread with the server -- that thread will then start one
# more thread for each request
server_thread = threading.Thread(target=server.serve_forever)
# Exit the server thread when the main thread terminates
server_thread.daemon = True
server_thread.start()
print("Server loop running in thread:", server_thread.name)
ip = '12.34.56.789' #Not my real ip address This is just to hide my ip
print(ip, PORT)
client(ip, PORT, b'Hello World 1')
#client(ip, port, b'Hello World 2')
#client(ip, port, b'Hello World 3')
server.shutdown()
When I run this i get the error:
Server loop running in thread: Thread-1
12.34.56.789 9001
Traceback (most recent call last):
File "C:/Python32/serverTesty.py", line 43, in <module>
client(ip, PORT, b'Hello World 1')
File "C:/Python32/serverTesty.py", line 18, in client
sock.connect((ip, port))
socket.error: [Errno 10061] No connection could be made because the target machine actively refused it
I know the port works because when I use canyouseeme.org on port 9001 when my program is running it says its active and working. So I think I just have my connection wrong somewhere.
ip = '12.34.56.789' #Not my real ip address, its the one i got from whatismyip.org
The first problem is that '12.34.56.789' isn't a valid IP address at all. Each component has to fit in 8 bits (0-255); 789 is impossible. But I assume that isn't the actual code you're running, because the output shows 12.45.29.122.
The second problem is that you're using an address that isn't your real address.
Your machine presumably has an internal IP address, that can only be accessed from your LAN. Then, your router has an external IP address. The router uses a technique called Network Address Translation to let each machine on your LAN pretend that external address belongs to them, when they're acting as clients (which is why whatismyip.org shows you that address). But that doesn't work when they're acting as servers.
If you think about it, there's really no way it could work. If you make an outbound connection, and someone replies, the router knows that the reply should go to your machine. But if someone just comes along and talks to the router out of the blue, how could it know which machine to send the connection to?
If you're trying to connect from inside the same LAN, there's a very easy solution: use the server's real internal address, not the router's external address.
If you need to connect from outside, you can't, without some extra work. There are four ways around this:
Give your machine a real publicly-addressable IP address (e.g., by putting it on the router's DMZ). This is generally not even an option for home users, and it's a bad option for people who don't know what they're doing (unless you want your machine to be part of someone's botnet by lunchtime).
Set up static port forwarding in your router's configuration. This is different for each router, but the idea is that you tell it "if someone comes looking for port 9001, always send them to machine 192.168.1.64".
Use UPnP to set up port forwarding dynamically.
Set up a NAT hole punching.
Options 3 and 4 are more complex, and I think option 2 is the one you want, so I won't explain them.
On top of all that:
HOST, PORT = "192.168.1.64", 9001
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
You've told the server explicitly "listen on 192.168.1.64". Even if you put your server machine on the DMZ, so it had addresses 192.168.1.64 and 12.45.29.122, your program is only listening for connections on the first one, so nobody would be able to reach it using the second. If you want to listen on all addresses, use 0.0.0.0.
In the edited version, you're now listening on 0.0.0.0, and connecting to the router's public IP, and you claim to have set up port forwarding on the router, and you're still getting the exact same error.
If that's all correct, there are three obvious things that could be going wrong:
You're not actually port forwarding; something is wrong with the setup.
You're not actually listening on 0.0.0.0:9001.
You've got a firewall blocking the connection.
There are a few tests you can do to narrow things down.
Open two terminals. In one, type nc -kl 9001. In the other, type nc 12.34.56.78 9001. They should connect up, so anything you type into one window appears in the other (maybe only after you hit Return). If that works, the port forwarding is working, and there's no firewall problem, so it's a problem in your code.
If that didn't work, please post exactly what you saw in each window. Then Ctrl-C the second nc, and type nc 192.168.1.64 9001. If that now works, either the port forwarding isn't set up right, unless you have a clever firewall that allows same-host (or same-interface) connections but not remote connections.
If neither one worked, it's probably a firewall problem. (Unless you're wrong about your IP addresses or something.) You can probably find logs somewhere, but without knowing what platform you're on and what firewall you're using it's hard to offer much help. (Also, that's probably a problem for a different site than SO.)
If you're on Windows, or some linux distros, you need to get a copy of nc (netcat) from somewhere; on most linux distros, and Mac, it should be built in. Also, GNU, BSD, and Hobbit nc are slightly different, so if nc -kl 6000 gives you an error, you might have to read the man page or --help. (If I remember right, Hobbit nc requires -l -p6000, BSD requires -l 6000, GNU allows either.)
Or you may want ncat, a re-implementation of netcat that I know can handle the syntax I used above, and has a single-file static executable for Windows.
If you can't get started with nc, at least try changing your code to connect to 192.168.1.64 instead of 12.34.56.78. If that fixes the problem, at least you'll know it's either port forwarding or a firewall that allows same-host/interface connections but not remote.