Python service discovery: Advertise a service across a local network - python

I have a "server" python script running on one of the local network machines, which waits for clients to connect, and passes them some work to do. The server and client code have both been written, and are working as expected...
The problem is, this server might be running from any machine in the local network, so I can't hard code the address in the script... I immediately wondered if I can make a machine advertise about its existence, and clients can respond to that. Is that doable in Python with the standard library? I really don't have time to download twisted or tornado and learn about them, unfortunately, so I need something simple.
I tried to think more about it, and realized I can have a single static IP machine where servers register/unregister from and clients can look for servers from there. Kind of like a torrent tracker, I think. This'll have to do if I can't do the service advertising approach easily.

An easy way to do service announcement/discovery on the local network is by broadcasting UDP packets.
Constants:
PORT = 50000
MAGIC = "fna349fn" #to make sure we don't confuse or get confused by other programs
Announcement:
from time import sleep
from socket import socket, AF_INET, SOCK_DGRAM, SOL_SOCKET, SO_BROADCAST, gethostbyname, gethostname
s = socket(AF_INET, SOCK_DGRAM) #create UDP socket
s.bind(('', 0))
s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) #this is a broadcast socket
my_ip= gethostbyname(gethostname()) #get our IP. Be careful if you have multiple network interfaces or IPs
while 1:
data = MAGIC+my_ip
s.sendto(data, ('<broadcast>', PORT))
print "sent service announcement"
sleep(5)
Discovery:
from socket import socket, AF_INET, SOCK_DGRAM
s = socket(AF_INET, SOCK_DGRAM) #create UDP socket
s.bind(('', PORT))
while 1:
data, addr = s.recvfrom(1024) #wait for a packet
if data.startswith(MAGIC):
print "got service announcement from", data[len(MAGIC):]
This code was adapted from the demo on python.org

Related

Peer to peer socket communication without port forwarding

First of all I am not talking about a tcp or udp or socket implemented in a vps or server
My question is just like client to client socket communication.
Imagine listening at your home pc with a tcp socket. You can connect to this from home inter network anyway. But suppose someone wants to connect to it via the internet. Then you can create a forwarding rule on the router and bring it to working condition. Then the router knows that if an incoming connection comes from a port, the connection will be forwarded to the device in the relevant inter network.
But the ISP I use does not support port forwarding.
I thought these were not impossible because of the team-viewer software. Because when I was connected to a friend in team-viewer, I opened the wire-shark and reviewed it.
Then I saw that the data packet is exchanged peer to peer. Because the destination source addresses were my ip and friend's ip
This means that the video data is exchanged without the participation of an additional server
I highlighted the team-viewer connection.
61.245.175.81 is my friend's public IP. 192.168.1.130 is my internal IP
I want to do the same
Here is my simple socket code. This does not work through the internet because there is no router forwarding rule. I am very new to socket and networking side
Sever
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(('', 12000))
while True:
message, address = server_socket.recvfrom(1024)
message = repr(message)
print("Connected from -> " + str(address) )
print("Received data -> " + message)
reply = b"Hi from server :) "
server_socket.sendto(reply, address)
Client
import time , datetime
import socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client_socket.settimeout(1.0)
message = bytes(str(datetime.datetime.now()),'utf-8')
addr = ("192.168.1.130", 12000)
client_socket.sendto(message, addr)
try:
data, server = client_socket.recvfrom(1024)
print( repr(data) )
except: #socket.timeout:
print('REQUEST TIMED OUT')
Can anyone give an explanation for my question
Pretty sure they do it using UDP hole punching, you'd need to do something similar to implement this.
In a nutshell two clients behind NAT (which is what your router is doing) can use a third server acting as a sort of mediator to establish a connection.

switchable python server client

I am trying to make a python program which when switched on acts as a client and detects the presence of any servers around it for 15 secs. If it doesn't find any, it promotes itself to a server and broadcasts its own ip as a HOST.
#RECIEVE
s=socket(AF_INET, SOCK_DGRAM)
s.bind(('',37885))
s.settimeout(15)
HOST=s.recvfrom(1024)
s.settimeout(None)
print HOST[0]
#BROADCAST
if (len(HOST)==0):
HOST = gethostbyname(gethostname())
print (HOST)
for i in range(10):
s=socket(AF_INET, SOCK_DGRAM)
s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
s.sendto(str(HOST),('255.255.255.255',37885))
time.delay(1)
The problem I am facing is that the program quits as soon as it doesn't find any servers. I need to have a switchable server and client configuration. How can I rectify this problem?

Listen for all incoming traffic on port

With python, I have compiled the following script:
from socket import *
socket = socket(AF_INET, SOCK_DGRAM)
socket.bind(("127.0.0.1", 80))
while True:
data, addr = socket.recvfrom(1024)
print addr[1]
It is supposed to receive all incoming traffic from port 80. However, if I load a webpage, it does not lot anything. Is there a problem with my script?
If you truly want to listen to all incoming traffic on all interfaces, perhaps try to bind to 0.0.0.0 instead of 127.0.0.1?
And as just stated, your socket pairing is a bit odd. This should get you started:
from socket import *
s = socket(AF_INET, SOCK_STREAM)
s.bind(('0.0.0.0', 80))
s.listen(1)
while True:
print s.accept()[1]
There seem to be some misconceptions here, and wanted to make clear for future visitors. When you visit a website you don't send packets out of port 80, you send packets from a random port to the port 80 of a different machine. In this case you are expecting to have packets on your port 80 when you are visiting website: this can't happen. This would only apply if you are hosting a website or listening on that port.

What do I do to make this socket to a websocket?

I've got this code at the moment, it's a simple socket server:
import socket
import time
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = socket.gethostname()
port = 9999
serversocket.bind((host, port))
serversocket.listen(5)
while True:
clientsocket,addr = serversocket.accept()
print(str(addr) + "connected")
test = "Package"
clientsocket.send(test.encode('utf-8'))
clientsocket.close()
I also made a client that gets the message. However, how do I make it so that when I type in the address of my socket on for example Chrome, it displays "Package". I have basic knowledge on handlers and such, but I can't find any DIY websocket tutorials on the internet.
I do not want to use for example tornado, I want to make a simple one myself
Thank you very much in advance!

Making Python sockets visible for outside world?

i already have a post which is quite similiar, but i am getting more and more frustrated because it seems nothing is wrong with my network setup. Other software can be seen from the outside (netcat listen servers etc.) but not my scripts.. How can this be??
Note: It works on LAN but not over the internet.
Server:
import socket
host = ''
port = 80001
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host,port))
s.listen(1)
print 'Listening..'
conn, addr = s.accept()
print 'is up and running.'
print addr, 'connected.'
s.close()
print 'shut down.'
Client:
import socket
host = '80.xxx.xxx.xxx'
port = 80001
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
s.close()
Somebody please help me.
Any help is greatly appreciated.
Jake
Edited again to add:
I think you may be missing some basics on socket communication. In order for sockets to work, you need to ensure that the sockets on both your client and server will meet. With your latest revision, your server is now bound to port 63001, but on the local loopback adapter: 127.0.0.1
Computers have multiple network adapters, at least 2: one is the local loopback, which allows you to make network connections to the same machine in a fast, performant manner (for testing, ipc etc), and a network adapter that lets you connect to an actual network. Many computers may have many more adapters (virtual adapters for vlans, wireless vs wired adapters etc), but they will have at least 2.
So in your server application, you need to instruct it to bind the socket to the proper network adapter.
host = ''
port = 63001
bind(host,port)
What this does in python is binds the socket to the loopback adapter (or 127.0.0.1/localhost).
In your client application you have:
host = '80.xxx.xxx.xxx'
port = 63001
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
Now what your client attempts to do is to connect to a socket to port 63001 on 80.xxx.xxx.xxx (which is your wireless internet adapter).
Since your server is listening on your loopback adapter, and your client is trying to connect on your wireless adapter, it's failing, because the two ends don't meet.
So you have two solutions here:
Change the client to connect to localhost by host = 127.0.0.1
Change the server to bind to your internet adapter by changing host = 80.xxx.xxx.xxx
Now the first solution, using localhost, will only work when your server and client are on the same machine. Localhost always points back to itself (hence loopback), no matter what machine you try. So if/when you decide to take your client/server to the internet, you will have to bind to a network adapter that is on the internet.
Edited to add:**
Okay with your latest revision it still won't work because 65535 is the largest post available.
Answer below was to the original revision of the question.
In your code posted, you're listening (bound) on port 63001, but your client application is trying to connect to port 80. Thats why your client can't talk to your server. Your client needs to connect using port 63001 not port 80.
Also, unless you're running an HTTP server (or your python server will handle HTTP requests), you really shouldn't bind to port 80.
In your client code change:
import socket
host = '80.xxx.xxx.xxx'
port = 63001
And in your Server Code:
import socket
host = ''
port = 63001
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((socket.gethostbyname(socket.gethostname()), port ))
In your server script you have port = 80, but you don't ever use it. It looks like the server is listening on 63001. And the client is connecting to 80.
If you're going to use 80, make sure you don't have an http server trying to use the port at the same time as well.

Categories

Resources