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.
Related
So I am writing this program that will allow me to run commands from a different computer on the same network (my own version of ssh) in Python. I want the client program to run in the background of the target which I've already figured out the logistics to. What I would like to do is start the client program and never have to start it again but after I close the server program on the host computer, I get tons of errors. What I would like to do is after I close the host program, the client will continue to try to connect to the server program until I run it again. The code for my client program is here:
import socket
import os
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def connect():
try:
s.connect(('localhost', 1234))
except:
connect()
while True:
connect()
while True:
try:
msg = s.recv(1024)
os.system(msg.decode("utf-8"))
except:
s.connect('localhost', 1234)
The code for my host program is here:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('localhost', 1234))
s.listen(5)
def main():
while True:
clientsocket, address = s.accept()
while address != None:
message = input("Enter Message: ")
messageb = message.encode("utf-8")
clientsocket.send(messageb)
main()
Note: I will change the address from localhost when I put this on a different computer.
Never do the following to start over a function without understanding the consequences of recursion. A function calling itself is called recursion and uses stack space. This will crash if the stack hits the recursion limit.
def connect():
try:
s.connect(('localhost', 1234))
except:
connect()
Here's a solution.
client.py:
import socket
import time
while True:
s = socket.socket()
try:
print('Trying to connect...')
s.connect(('localhost',8000))
print('Connected.')
try:
while True:
data = s.recv(1024)
if not data: break # server closed connection if nothing received.
print(data)
finally:
s.close()
print('Disconnected.')
except ConnectionError: # Any type of connection error, e.g. refused, aborted, reset.
time.sleep(1)
server.py:
import socket
import time
s = socket.socket()
s.bind(('',8000))
s.listen()
while True:
c,a = s.accept()
print(f'Connected: {a}')
try:
while True:
c.send(b'message')
time.sleep(1)
except ConnectionError:
c.close()
print(f'Disconnected: {a}')
Note also that TCP is a streaming protocol. It has no concept of messages. Take the time.sleep(1) out and the messages will all run together. A proper implementation will have a protocol to extract complete messages from the byte stream such as fixed sized messages, size transmitted followed by message, delimiter bytes like newlines between messages, etc.
I write that Program a while ago and now it stopped working. Every time I run it say "timed out". This Error also occurs when I set the timeout higher for example to 10sec.
import sys
import socket
def traceroute(dest_addr, max_hops=30, timeout=0.2):
proto_icmp = socket.getprotobyname('icmp')
proto_udp = socket.getprotobyname('udp')
port = 33434
for ttl in range(1, max_hops+1):
rx = socket.socket(socket.AF_INET, socket.SOCK_RAW, proto_icmp)
rx.settimeout(timeout)
rx.bind(('', port))
tx = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, proto_udp)
tx.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl)
tx.sendto(bytes('', 'utf-8'), (dest_addr, port))
try:
data, curr_addr = rx.recvfrom(512)
curr_addr = curr_addr[0]
except socket.error as err:
print (err)
curr_addr = None
finally:
rx.close()
tx.close()
yield curr_addr
if (curr_addr == dest_addr):
break
if __name__ == "__main__":
dest_name = "www.google.de"
dest_addr = socket.gethostbyname(dest_name)
print("traceroute to %s (%s)" % (dest_name, dest_addr))
for i, v in enumerate(traceroute(dest_addr)):
print("%d\t%s" % (i+1, v))
presumably some router between your computer and Google isn't sending an ICMP "host unreachable" message which your code is unconditionally waiting for.
the code you've posted is very "fragile" and it'll tend to break in weird and wonderful ways on the internet. e.g. the example here is that you always wait for an ICMP message immediately after sending your UDP packet, but you might also get a UDP packet back (if the port happens to be open) or nothing back if a router in the middle is silently dropping packets on TTL expiry.
I'd suggest using select (or similar) to handle waiting for multiple (i.e. both UDP and ICMP) sockets concurrently, or you could use an async library to keep track of everything
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()
Here is the server:
import socket
HOST = '127.0.0.1'
PORT = 4444
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(5)
conn, addr = s.accept()
print addr, 'connected.'
while True:
data = conn.recv(1024)
print '> ', data
reply = raw_input(">> ")
conn.sendall(reply)
if reply == 'bye':
break
conn.close()
And here is the client:
import socket
HOST = '127.0.0.1'
PORT = 4444
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
message = raw_input("> ")
s.send(message)
reply = s.recv(1024)
print ">> ", repr(reply)
if message == 'bye':
break
s.close()
What is the most efficient way to upgrade this code to support multiple instances, and more importantly: how could I assign names (nicknames) to all clients and make it so clients could type whenever they want? I tried to use Threading, but that was a huge disaster .
Multiple client
For multiple client connection, you need to have some non-linear execution.
You can either use coroutines or threads to achieve this.
Python 3 comes with asyncio, library that uses coroutines (and lot of other features) to emulate multithreading. I suggest you look into this.
You can also use threads, it's not that complicated. Whenever a client connects to your server, you just have to create a new thread to execute your function. Python official documentation should help you achieve that.
Nickname
Whenever a client connects to your server, you could instantiate a User object in the newly created thread with your client information, so that each thread has its own User instance.
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.