Python Socket Communicating Between PC and RPi - python

I am trying to make a small game and a part of it is to send a country name to Raspberry Pi from a laptop. Right now I am just sending a string, but later that string will be used to draw an image from a pen plotter attached to the RPi.
I am having trouble sending and receiving data using Python sockets (complete beginner). What I am trying to do is make the server keep sending the country name until the client receives it and then send a signal from the client back to the server that it has received the country name to make the server stop sending it and switch to listening mode. The client does some processing which right now is just print(f"Drawing country {country_name}") and then sends the server signal that it is finished.
Unfortunately both programs are running but nothing is happening. I am able to ping the laptop from RPi and vice versa. Furthermore the program in this link works for me:
Communication between two computers using python socket
My code for server side and client side is provided below:-
Server side (laptop)
#Import the necessary libraries
from socket import socket, gethostbyname, AF_INET, SOCK_DGRAM
import sys
import time
#Designate a port over which the communication will happen
PORT_NUMBER = 5000
#Protocol ?
SIZE = 1024
#Get the name of the host, this is our laptop
SERVER_NAME = gethostbyname('')
#Create the server socket, this is our laptop
Server_Socket = socket( AF_INET, SOCK_DGRAM )
Server_Socket.bind( (SERVER_NAME, PORT_NUMBER) )
#Set the client IP, this is our raspberry pi
CLIENT_IP = "192.168.88.244"
print ("Test server listening on port {0}\n".format(PORT_NUMBER))
#Create a variable that will become True if the client responds
client_response = False
#Start of while loop to keep sending data until client responds
while client_response == False:
#Create a delay so the client is not overwhelmed
time.sleep(3)
Server_Socket.sendto(b"Country_Name", (CLIENT_IP, PORT_NUMBER))
(client_response,addr) = Server_Socket.recvfrom(SIZE)
print(client_response)
#End of while loop to keep sending data until client responds
#Set client_response again to false
client_response = False
#Start of while loop to listen until client has done the task, this is our raspberry pi finishing the drawing
while client_response == False:
(client_response,addr) = Server_Socket.recvfrom(SIZE)
#End of while loop to listen until client has done the task, this is our raspberry pi finishing the drawing
#Close the server socket
Server_Socket.close()
print("Done")
sys.ext()
Client side (Raspberry Pi)
#Import the necessary libraries
from socket import socket, gethostbyname, AF_INET, SOCK_DGRAM
import sys
import time
#Set the server IP, this is our laptop
SERVER_IP = '192.168.88.250'
#Designate a port over which the communication will happen
PORT_NUMBER = 5000
#Protocol ?
SIZE = 1024
#Create the cleint socket, this is our raspberry pi
Client_Socket = socket( AF_INET, SOCK_DGRAM )
Client_Socket.bind( (SERVER_IP, PORT_NUMBER) )
#Set a variable that becomes True if the data sent by server is received
server_data_rcvd = False
#Start of while loop to listen until data from server is received
while server_data_rcvd == False:
#Create a delay so the client is not overwhelmed
time.sleep(1)
#Store country name
(country_name, addr) = Client_Socket.recv(size)
print("country_name")
#Start of if statement to check if country name is received
if type(country) == str:
server_data_rcvd = True
#Send response to the server
Client_Socket.sendto(True, (SERVER_IP, PORT_NUMBER))
#End of if statement to check if country name is received
#End of while loop to listen until data from server is received
#Draw the country
print(f"Drawing country {country_name}")
Client_Socket.sendto(True, (SERVER_IP, PORT_NUMBER))
#Close the socket
Client_Socket.close()
sys.exit()
What mistake am I making ? And how can I make the code more efficient and readable ?
Thanks in advance !

The client have to connect to the server with socket.connect, not socket.bind.
#Create the cleint socket, this is our raspberry pi
Client_Socket = socket( AF_INET, SOCK_DGRAM )
Client_Socket.connect( (SERVER_IP, PORT_NUMBER) )

There are a number of issues that could be causing your problem outside of the couple of typos (ex: size vs SIZE, country vs country_name, sys.ext vs sys.exit, etc).
UDP vs TCP
First off, you are using SOCK_DGRAM which suggests UDP as a protocol rather than TCP. This is fine as long as this is what you were expecting. UDP is connectionless where TCP requires your client to connect to your server.
Bytes
If you are using Python3, recv(buffersize[, flags]) -> data returns bytes, not a tuple of the data and an address. If you update that line to accept country, you'll then want to convert it to a string via country.decode('utf-8')
If you are using Python3, sendto(data[, flags], address) -> count expects data (not a boolean). What you can do is send bytes via b'True' or send 'True'.encode('utf-8').
Blocking recv
The biggest issue I see and probably what is causing you the most trouble is that your while loops are problematic. Not only does the while loop in your server never exit, but recv is a blocking action so if you were to start the server and then start your rpi, they will both be stuck at recv and neither will be sending. In your current setup, you have to launch rpi first so that it can listen, then start your server so it can send the country to the rpi. Otherwise, they are deadlocked since they are both listening for the other end to send something.

Related

Python UDP socket.recv() stops recieving on Windows 10

I have a udp client function written in python running on Windows 10. I'm aware this is not production quality code but I'm only trying to grasp the fundamentals here
client = socket(AF_INET, SOCK_DGRAM)
client.bind(('192.168.0.107', CLIENT_PORT))
client.setblocking(False)
while True:
try:
data = client.recv( 1024 )
except:
continue
if data is not None:
print(data.decode('utf-8'))
I have a server running on an embedded device sending out small udp packets periodically (udp payload size of 22). This client gets about 10 of these packets give or take a few then the script stops receiving the udp packets. The only exception raised in the try/catch block is that there is no data to receive. If I change to blocking the behaviour is the same.
[WinError 10035] A non-blocking socket operation could not be completed immediately
The server is still sending the packets, I can see them in Wireshark with the expected IP addresses, ports, and verified checksums.
The thing is if I add a send after the receive stops the problem goes away entirely and I can continue receiving the udp payloads
client = socket(AF_INET, SOCK_DGRAM)
client.bind(('192.168.0.107', CLIENT_PORT))
client.setblocking(False)
while True:
try:
data = client.recv( 1024 )
except:
continue
if data is not None:
print(data.decode('utf-8'))
client.sendto("a_udp_payload".encode('utf-8'), ('192.168.0.108' , SERVER_PORT))
Is there something fundamental that I am missing here? Why does the stop working on the first snippet but not the second? Is there some buffer flushing issue?
Many thanks
Try to set timeout for the socket client.settimeout(2)

How to make 2 clients connect each other directly, after having both connected a meeting-point server?

I'm writing a toy meeting-point/relay server listening on port 5555 for two clients "A" and "B".
It works like this: every byte received by the server from the firstly-connected client A will be sent to the secondly-connected client B, even if A and B don't know their respective IP:
A -----------> server <----------- B # they both connect the server first
A --"hello"--> server # A sends a message to server
server --"hello"--> B # the server sends the message to B
This code is currently working:
# server.py
import socket, time
from threading import Thread
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket.bind(('', 5555))
socket.listen(5)
buf = ''
i = 0
def handler(client, i):
global buf
print 'Hello!', client, i
if i == 0: # client A, who sends data to server
while True:
req = client.recv(1000)
buf = str(req).strip() # removes end of line
print 'Received from Client A: %s' % buf
elif i == 1: # client B, who receives data sent to server by client A
while True:
if buf != '':
client.send(buf)
buf = ''
time.sleep(0.1)
while True: # very simple concurrency: accept new clients and create a Thread for each one
client, address = socket.accept()
print "{} connected".format(address)
Thread(target=handler, args=(client, i)).start()
i += 1
and you can test it by launching it on a server, and do two netcat connections to it: nc <SERVER_IP> 5555.
How can I then pass the information to the clients A and B that they can talk directly to each other without making the bytes transit via the server?
There are 2 cases:
General case, i.e. even if A and B are not in the same local network
Particular case where these two clients are in the same local network (example: using the same home router), this will be displayed on the server when the 2 clients will connect to the server on port 5555:
('203.0.113.0', 50340) connected # client A, router translated port to 50340
('203.0.113.0', 52750) connected # same public IP, client B, router translated port to 52750
Remark: a previous unsuccesful attempt here: UDP or TCP hole punching to connect two peers (each one behind a router)
and UDP hole punching with a third party
Since the server knows the addresses of both clients, it can send that information to them and so they would know each others adress. There are many ways the server can send this data - pickled, json-encoded, raw bytes. I think the best option is to convert the address to bytes, because the client will know exactly how many bytes to read: 4 for the IP (integer) and 2 for the port (unsigned short). We can convert an address to bytes and back with the functions below.
import socket
import struct
def addr_to_bytes(addr):
return socket.inet_aton(addr[0]) + struct.pack('H', addr[1])
def bytes_to_addr(addr):
return (socket.inet_ntoa(addr[:4]), struct.unpack('H', addr[4:])[0])
When the clients receive and decode the address, they no longer need the server, and they can establish a new connection between them.
Now we have two main otions, as far as I know.
One client acts as a server. This client would close the connection to the server and would start listening on the same port. The problem with this method is that it will only work if both clients are on the same local network, or if that port is open for incoming connections.
Hole punching. Both clients start sending and accepting data from each other simultaneously. The clients must accept data on the same address they used to connect to the rendezvous server, which is knwn to each other. That would punch a hole in the client's nat and the clients would be able to communicate directly even if they are on different networks. This proccess is expleined in detail in this article Peer-to-Peer Communication Across Network Address Translators, section 3.4 Peers Behind Different NATs.
A Python example for UDP Hole Punching:
Server:
import socket
def udp_server(addr):
soc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
soc.bind(addr)
_, client_a = soc.recvfrom(0)
_, client_b = soc.recvfrom(0)
soc.sendto(addr_to_bytes(client_b), client_a)
soc.sendto(addr_to_bytes(client_a), client_b)
addr = ('0.0.0.0', 4000)
udp_server(addr)
Client:
import socket
from threading import Thread
def udp_client(server):
soc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
soc.sendto(b'', server)
data, _ = soc.recvfrom(6)
peer = bytes_to_addr(data)
print('peer:', *peer)
Thread(target=soc.sendto, args=(b'hello', peer)).start()
data, addr = soc.recvfrom(1024)
print('{}:{} says {}'.format(*addr, data))
server_addr = ('server_ip', 4000) # the server's public address
udp_client(server_addr)
This code requires for the rendezvous server to have a port open (4000 in this case), and be accessible by both clients. The clients can be on the same or on different local networks. The code was tested on Windows and it works well, either with a local or a public IP.
I have experimented with TCP hole punching but I had limited success (sometimes it seems that it works, sometimes it doesn't). I can include the code if someone wants to experiment. The concept is more or less the same, both clients start sending and receiving simultaneously, and it is described in detail in Peer-to-Peer Communication Across Network Address Translators, section 4, TCP Hole Punching.
If both clients are on the same network, it will be much easier to communicate with each other. They would have to choose somehow which one will be a server, then they can create a normal server-client connection. The only problem here is that the clients must detect if they are on the same network. Again, the server can help with this problem, as it knows the public address of both clients. For example:
def tcp_server(addr):
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.bind(addr)
soc.listen()
client_a, addr_a = soc.accept()
client_b, addr_b = soc.accept()
client_a.send(addr_to_bytes(addr_b) + addr_to_bytes(addr_a))
client_b.send(addr_to_bytes(addr_a) + addr_to_bytes(addr_b))
def tcp_client(server):
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect(server)
data = soc.recv(12)
peer_addr = bytes_to_addr(data[:6])
my_addr = bytes_to_addr(data[6:])
if my_addr[0] == peer_addr[0]:
local_addr = (soc.getsockname()[0], peer_addr[1])
... connect to local address ...
Here the server sends two addresses to each client, the peer's public address and the client's own public address. The clients compare the two IPs, if they match then they must be on the same local network.
The accepted answer gives the solution. Here is some additional information in the case "Client A and Client B are in the same local network".
This situation can indeed be detected by the server if it notices that both clients have the same public IP.
Then the server can choose Client A as "local server", and Client B as "local client".
The server will then ask Client A for its "local network IP". Client A can find it with:
import socket
localip = socket.gethostbyname(socket.gethostname()) # example: 192.168.1.21
and then send it back to the server. The server will communicate this "local network IP" to Client B.
Then Client A will then run a "local server":
import socket
soc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
soc.bind(('0.0.0.0', 4000))
data, client = soc.recvfrom(1024)
print("Connected client:", client)
print("Received message:", data)
soc.sendto(b"I am the server", client)
and Client B will run as a "local client":
import socket
soc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server = ('192.168.1.21', 4000) # this "local network IP" has been sent Client A => server => Client B
soc.sendto("I am the client", server)
data, client = soc.recvfrom(1024)
print("Received message:", data)

Python - Send and Receive of UDP packets

I've started with Python a few month ago. Now I've reached a point where I need some help concerning networking.
I've the following scenario:
One PC and 3 external devices in a small local net.
The PC wants to observe a switch state (on/off) on each device. This will be done by an initial command from the PC to each device.
After receiving this command each device sends automatically a packet with the new state to the PC whenever the switch state has changed.
All the packets on the network are UDP packets
On the PC I have the following Python script running (just for one device):
import socket
UDP_IP1 = "192.168.1.64"
UDP_IP2 = "192.168.1.73"
UDP_IP3 = "192.168.1.74"
UDP_PORT = 45 # used port on external device
port = 52129
message = "Message to observe..." # not the real command string
def SendData():
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("", port))
msg = message.encode('utf-8')
s.sendto(msg, (UDP_IP1, UDP_PORT))
except:
print("Sending Error!!!\n")
return s
def ReceiveData(sock):
print("Receiving data: \n")
while True:
data, addr = sock.recvfrom(4096) # buffer size is 4096 bytes
print ("received message:", data)
if __name__ == "__main__":
s = SendData()
ReceiveData(s)
My Problem: In principal this script is running, but after approximately 2 minutes without any change of the switch on the device I do not receive any more packets from the external device when the switch state changes. But I can see the incoming packets with the installed Wireshark.
It looks like a time-out when I don't receive anything but 'sock.recvfrom' is still waiting for data.
Is there anyone who has/had the same problem?

Communicating with multiple clients using one TCP socket python

I am using TCP sockets to communicate between my server and clients. The server code and socket code are as below:
server:
from socket import *
HOST = 'xx.xx.xx.xx'
PORT = 1999
serversocket = socket(AF_INET,SOCK_STREAM)
serversocket.bind((HOST,PORT))
print 'bind success'
serversocket.listen(5)
print 'listening'
while True:
(clientsocket, address) = serversocket.accept()
print ("Got client request from",address)
#clientsocket.send('True')
data = clientsocket.recv(1024)
print data
clientsocket.send('True')
clientsocket.close()
client:
import socket
import sys
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Connect the socket to the port on the server given by the caller
server_address = ('xx.xx.xx.xx', 1999)
print >>sys.stderr, 'connecting to %s port %s' % server_address
sock.connect(server_address)
try:
message = 'This is the message. It will be repeated.'
print >>sys.stderr, 'sending'
for x in range (0,1):
name=raw_input ('what is ur name')
print type(name)
sock.send(name)
print sock.recv(1024)
finally:
sock.close()
I am able to communicate with the server from client and able to send and receive data. But the problem I am facing is that I am not able to send and receive data continuously from the server. I have to restart my client code on my laptop to send and receive data again from the server. The way the above client code is working is that when I give a keyboard input, then the socket sends data to server and server responds back. But in the client code, in the for loop if I do two iterations, for the second iteration the data I enter from keyboard is not reaching server. I need to restart my client code to send data again. How do I fix this ?
Also, when once client is connected to the server, the other cannot connect to the server. Any ideas on how to do this ?
You need to design and implement a protocol that specifies what each side is supposed to do and then implement that protocol. You're expecting it to work by magic.
For example:
data = clientsocket.recv(1024)
I suspect you are expecting this to receive a "message". But TCP has no notion of messages. If you need to send and receive messages, you need to define precisely what a "message" is for your protocol and write code to send and receive them.
It may be helpful to look at the specifications for other protocols that use TCP such as HTTP, FTP, or IRC. It really is worth the time to write out a specification of your protocol before you write any code. It will save a lot of pain.

networking program crashes

i got this code from http://www.evolt.org/node/60276 and modified it to listen for a single "1" coming from the other side
but whenever i run this program it stops and python IDLE goes to non-responding on "data1,addr = UDPSock.recvfrom(1024)"
def get1():
# Server program, receives 1 if ball found
# ff1 is file w/ received data
import socket
import time
# Set the socket parameters
host = "mysystem"
port = 21567
#buf = 1024
addr = (host,port)
# Create socket (UDP) and bind to address
UDPSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
UDPSock.bind(addr)
# Receive messages
while 1:
print "waiting..............."
data1,addr = UDPSock.recvfrom(1024)
print "got 1"
if not data1:
print "Client has exited!"
break
else:
print "\nReceived message '", data1,"'"
UDPSock.close() # Close socket
print "socket closed\n"
#call some other function that uses 1
and client side
def send1():
# Client program, sends 1 if ball found
# mf1 is file with data to be sent
import socket
# Set the socket parameters
host = "mysystem"
port = 21567
buf = 1024
addr = (host,port)
# Create socket (UDP)
UDPSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
mf1=1
print mf1
# Send messages
if(UDPSock.sendto(str(mf1),addr)):
print "Sending message '",str(mf1),"'....."
# Close socket
UDPSock.close()
does anyone know what might be the cause of this? (sorry for long post)
As a second guess (I replaced my first guess with this) I suspect that you are running the receiver in IDLE and then IDLE is hanging so you can't run the client. I don't know exactly how IDLE works as I never use it, but the line containing recvfrom will stop the Python thread its running in until data is sent. So you need to start the client in a separate instance of IDLE or from the command line or something.
At any rate, I have tested the program in question on my Python with 127.0.0.1 as the host, and it worked fine, for some values of fine. The recvfrom does hang, but only until some data is sent, then it comes back with the data and prints it out and everything. You do have a bug that happens after that though. :-)

Categories