Broadcast server and client on the same script? - python

I have the following scripts:
import socket
import sys
import traceback
msg = socket.gethostbyname(socket.gethostname())
dest = ('<broadcast>',10100)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.sendto(msg, dest)
print "Sent."
while 1:
(buf,address)=s.recvfrom(10100)
if not len(buf):
break
print "Received from %s: %s" %(address, buf)
And then:
import socket
import traceback
import os
host = ''
port = 10100
sx = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sx.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sx.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sx.bind((host,port))
while 1:
try:
message, address = sx.recvfrom(10104)
print "Got data from", address
sx.sendto("ola",address)
except (KeyboardInterrupt, SystemExit):
raise
except:
traceback.print_exc()
I'm having some trouble having both of them on the same script.
I just wanted to learn more about sockets, etc, this is nothing in particular.
My idea was to have just one script in each machine (Raspberry Pi), and whenever they're on the same network, one will know about the presence of the other.
Hope I had explained right.

You can send and receive on the same socket, you don't need the second one. Use something like select with a timeout to wait for others to contact you, broadcast on the timeout expiring to let others know about yourself (you might want to keep something like time left to next broadcast, so you don't lose broadcasting schedule on receipt of a datagram).

Thanks for all the help.
I've found these two links.
The first one is really great, simple and is working like needed. May help someone else:
http://www.cs.bilgi.edu.tr/~mgencer/Ders%20Malzemeleri/IThingTaggedFile/p2p.py
https://github.com/zetaron/python-udp-p2p

Related

porting Linux socket programming (Python) to Windows

I have a code which consists of something similar to this, which works in Linux. Now i want to port this to Windows. I know that Windows uses winsock for socket programming but is it possible to implement this python module "socket" in windows as well?
```
import socket
host = self.config.get_config('BASE_ADDRESS',BASE_ADDRESS_VAL)
port = self.config.get_config('BASE_PORT',BASE_PORT_VAL)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,socket.IPPROTO_UDP)
if sock is None:
print('could not open socket')
sys.exit(1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.append(sock)
self.log.info('Listening to %s:%s' % (host, port))
try:
sock.bind((host, int(port)))
except socket.error as msg:
print('Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1])
sys.exit()
#sock.bind(("10.0.0.1", ""))
while self.winParent.ongoing==True:
try:
r,w,e = select.select(self.sock, [], self.sock,self.timeout)
for sock in r:
data, addr = sock.recvfrom(556)
#something = sock.recvfrom(556)
#print(something[0].decode())
self.handle(sock, addr, data)
except (Exception, e):
import traceback
self.log.critical('%s\n%s' % (str(e), traceback.format_exc()))
time.sleep(1)
for sock in self.sock:
sock.close()
self.sock = []
My code gets stuck at "r,w,e = select.select(self.sock, [], self.sock,self.timeout)".
On investigating with Wireshark, I can see the following:
[1]: https://i.stack.imgur.com/zNxKH.png
I have the following points of doubt :
(1) Could this be a firewall issue? I have added python.exe to allow through Firewall in Windows Defender. Am I missing something basic?
(2) Should I have the winsock.dll installed separately?
The socket module works just fine on Windows and that's not the source of the problem here. As you've helpfully pointed out, it's select where your code gets stuck (the posix select() is the traditional Unix way of servicing sockets). It's supposed to get stuck, as it's sitting and waiting for incoming packets :) You are sending some packets to it, right?
Since you're expecting incoming UDP packets the Windows Firewall might need additional convincing to allow those. Double-check with a quick Google search.

Send data between different LANs in python

I've been trying for two weeks to create a Python script that will communicate with itself even if run from different LANs.
But I can not perform the NAT traversal. I've tried to check what's going wrong and at least the socket isn't timeouting anymore but I can't receive the data.
I think the issue might be that the NAT is mapping the send and receive to different port but don't know how to check that or fix it if it is really the issue.
Code:
import socket
import time
from threading import Thread
PORT = 4206
peers = [''] # public ip adresses
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
LOCAL_HOST = ('127.0.0.1', PORT)
def main():
print('P2P are set to PORT :', PORT)
Thread(target=nat_traversal).start()
def recv():
time.sleep(5)
while True:
print('response:', sock.recvfrom(4096))
def sec30():
for i in range(0, 29):
time.sleep(1)
print(i, end=',')
time.sleep(1)
print('30')
def nat_traversal():
Thread(target=recv).start()
while True:
t = Thread(target=sec30, args=())
t.start()
for peer in peers:
if peer != '':
try:
sock.sendto(b'Hey', (peer, PORT))
except TimeoutError:
print('timeout')
t.join()
if name == 'main':
main()
Can you give me examples of a Python script that performs nat traversal and can send and receive messages?
Thanks in advance (:
NAT means that it can translate a IP outside to a IP inside, but many NAT types don't alow the IP from outside to start the conneciton. So your program may not work.

Python's recvfrom() function not returning any data

I would like to preface that I am new to networking and still working on my python skills so go easy on me.
I have started working on a network scanning project and have played with multiple protocols to try and find what works best for my purposes. SSDP seems to be the best for me thus I have been playing with a little universal plug and play script I found to try and test how things are working on my network.
import socket
import sys
dst = "239.255.255.250"
st = "upnp:rootdevice"
msg = [
'M-SEARCH * HTTP/1.1',
'Host:239.255.255.250:1900',
'Man:"ssdp:discover"',
'MX:1',
'ST:%s' % (st,),
'']
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.bind((socket.gethostbyname(socket.gethostname()), 0))
print(s.getsockname())
s.settimeout(60)
byte_data = '\r\n'.join(msg)
s.sendto(bytes(byte_data, 'utf-8'), (dst, 1900))
while True:
try:
data, addr = s.recvfrom(32*1024)
except socket.timeout:
print("timeout has occurred")
break
print (f"$ {addr}\n{data}")
s.close()
For some reason this always times out for me and never displays any data. After investigating using WireShark, I can see that my computer is sending out searches and devices are sending replies back to my computer. Does anyone know why this might be occurring (OS is windows 10). I will say I have increased the timeout numerous times to high amounts and still nothing is getting through.
As pointed out in the comments, there should be an extra blank line in the M-SEARCH request header, so.
import socket
import sys
dst = "239.255.255.250"
st = "upnp:rootdevice"
msg = [
'M-SEARCH * HTTP/1.1',
'Host:239.255.255.250:1900',
'Man:"ssdp:discover"',
'MX:1',
'ST:%s' % (st,),
'',
'']
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.bind((socket.gethostbyname(socket.gethostname()), 0))
print(s.getsockname())
s.settimeout(2)
byte_data = '\r\n'.join(msg)
s.sendto(bytes(byte_data, 'utf-8'), (dst, 1900))
while True:
try:
data, addr = s.recvfrom(32 * 1024)
except socket.timeout:
print("timeout has occurred")
break
print(f"$ {addr}\n{data}")
s.close()
Now it works. Also, with the SSDP MX header set to 1 it is pointless setting timeout to 60 - any device responding to the request MUST reply within MX seconds or not at all.
As it is, this should work fine on a system with a single network interface. But on a multi-homed system you will need to send the request on each interface by binding a socket to that interface, otherwise some UPNP devices might not be reachable.

How to incorporate the IP address of a device into a Python script if the address changes

I have a Python script which retrieves the measured data from a smart plug so that I can visualize it on my Rasbperry Pi.
This command gets the data
send_hs_command("192.168.1.26", 9999, b'{"emeter":{"get_realtime":{}}}')
and this is the define
def send_hs_command(address, port, cmd):
data = b""
tcp_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
tcp_sock.connect((address, port))
tcp_sock.send(encrypt(cmd))
data = tcp_sock.recv(2048)
except socket.error:
print(time.asctime( time.localtime(time.time()) ), "Socket closed.", file=sys.stderr)
finally:
tcp_sock.close()
return data
My problem is that if I take the Smart Plug somewhere else, it will have
a new IP-Address, which means I have to keep rewriting it on my Python script. This is not an option for me. What would be the simplest solution? Thanks
I don't have a Pi to run this on.
If the IP address of the target(Smart Plug) is variable, can you not use a pre-determined host-name(located in '/etc/hostname') instead?
the socket library provides a few handy functions;
You can first use
gethostbyaddr to get the host-name if you don't have the host-name information already.
Then from that point onward you can use the known host-name and use
create_connection to establish connections.
However, if you want to use something more dynamic; I'd suggest using the MAC address as the key.
Please be advised that running scapy which perhaps depends on tcpdump on Raspberry Pi might be CPU exhaustive.
Please take a look at the following snippet:
import socket
import time
import sys
from scapy.all import *
def send_hs_command(address, port, cmd):
data = b""
tcp_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
tcp_sock.connect((address, port))
tcp_sock.send(encrypt(cmd))
data = tcp_sock.recv(2048)
except socket.error:
print(time.asctime( time.localtime(time.time()) ), "Socket closed.", file=sys.stderr)
finally:
tcp_sock.close()
print(data)
return data
def get_ip_from_mac():
# Match ARP requests
packet_list = sniff(filter="arp", count=10) # increase number of arp counts
for i in packet_list:
# Show all ARP requests
# print(i[Ether].src, "is broadcasting IP", i[ARP].psrc)
if (i[ARP].hwsrc == '00:0c:29:b6:f4:be'): # target MAC address
return (True, i[ARP].psrc)
return (False, '')
def main():
result = get_ip_from_mac()
if result[0] == True:
print("Succeeded to reach server")
send_hs_command(result[1], 22, b'{"emeter":{"get_realtime":{}}}')
else:
# logic to retry or graciously fail
print("Failed to reach server")
if __name__== "__main__":
main()

Send/receive Packets with TCP sockets

Recently, I managed to create sockets on my PC and my Raspberry Pi to enable communication between both devices. Currently, the client is able to automatically send messages to the server. I was wondering, if it is possible to modify the scripts to send tcp data packets instead of purely text messages, as I would very much like to control the raspberry pi using my PC in the future without having the need to ssh/etc.
I've looked at some examples, but as I don't have much experience in writing my own scripts/codes, I'm not very sure how to go about doing this. I would appreciate if someone could guide me in the right direction with explanation and some examples if possible.
Anyway here is the server/client script I'm running at the moment:
Client:
import socket
import sys
import struct
import time
#main function
if __name__ == "__main__":
if(len(sys.argv) < 2) :
print 'Usage : python client.py hostname'
sys.exit()
host = sys.argv[1]
port = 8888
#create an INET, STREAMing socket
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error:
print 'Failed to create socket'
sys.exit()
print 'Socket Created'
try:
remote_ip = socket.gethostbyname( host )
s.connect((host, port))
except socket.gaierror:
print 'Hostname could not be resolved. Exiting'
sys.exit()
print 'Socket Connected to ' + host + ' on ip ' + remote_ip
#Send some data to remote server
message = "Test"
try :
#Set the whole string
while True:
s.send(message)
print 'Message sent successfully'
time.sleep(1)
print 'Sending...'
except socket.error:
#Send failed
print 'Send failed'
sys.exit()
def recv_timeout(the_socket,timeout=2):
#make socket non blocking
the_socket.setblocking(0)
#total data partwise in an array
total_data=[];
data='';
#beginning time
begin=time.time()
while 1:
#if you got some data, then break after timeout
if total_data and time.time()-begin > timeout:
break
#if you got no data at all, wait a little longer, twice the timeout
elif time.time()-begin > timeout*2:
break
#recv something
try:
data = the_socket.recv(8192)
if data:
total_data.append(data)
#change the beginning time for measurement
begin=time.time()
else:
#sleep for sometime to indicate a gap
time.sleep(0.1)
except:
pass
#join all parts to make final string
return ''.join(total_data)
#get reply and print
print recv_timeout(s)
s.close()
Server:
import socket
import sys
from thread import *
HOST = '' # Symbolic name meaning all available interfaces
PORT = 8888
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket created'
try:
s.bind((HOST, PORT))
except socket.error , msg:
print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
sys.exit()
print 'Socket bind complete'
s.listen(10)
print 'Socket now listening'
#Function for handling connections
def clientthread(conn):
#Sending message to connected client
conn.send('Welcome to the server. Receving Data...\n') #send only takes string
#infinite loop so that function do not terminate and thread do not end.
while True:
#Receiving from client
data = conn.recv(1024)
reply = 'Message Received at the server!\n'
print data
if not data:
break
conn.sendall(reply)
conn.close()
#now keep talking with the client
while 1:
#wait to accept a connection
conn, addr = s.accept()
print 'Connected with ' + addr[0] + ':' + str(addr[1])
#start new thread
start_new_thread(clientthread ,(conn,))
s.close()
socket.socket(socket.AF_INET, socket.SOCK_STREAM) already creates a connection that provides a reliable stream of bytes between two machines. This uses TCP, which is on top of IP and Ethernet. The latter two are package-based, while TCP creates a stream of continuous bytes on top of it. It also adds some error checking and error correction, so it is pretty reliable.
I honestly don't understand what you want to achieve with what you call "send packets". What you don't want to do is to create an implementation of TCP yourself, as that's a non-trivial task, so sending RAW packets is out. In general, even using TCP is already relatively low-level and should be avoided unless really necessary.
Using e.g. ZeroMQ you get a message-based interface that does all the transmission for you. It does so on top of TCP (or other transports) and adds more error correction for e.g. disconnects. There, you also have something like "packets", but those are independent of how many TCP or IP packets were required to send it underneath. If you don't want to implement a specific protocol, I'd suggest you use this framework instead of lowlevel TCP sockets.
Another simple alternative is to use HTTP, for which there is also existing code in Python. The downside is that it is always one side that initiates some communication and the other side only replies. If you want some kind of active notification, you either have to poll or use hacks like delaying an answer.
You are already sending data packets - those packets juts happen to contain text data at the moment. Try looking into pickle in the standard libraries and into pyro.

Categories