I want to have a Raspberry Pi collect data simultaneously from multiple Mbed microcontrollers with TCP sockets. Right now I have 2 microcontrollers sending out different data packets at regular intervals. The microcontrollers get an error message any time the package is not received so after 3 consecutive failed sends it will close and reopen its socket. With my current code I have 100% packet transfer success with only 1 microcontroller powered but with a second one sending packets it may work briefly but then will begin seeing missed packets and occasional errors.
#The server code is
import sys
import socket
from threading import Thread
from socketserver import ThreadingMixIn
# Multithreaded Python server : TCP Server Socket Thread Pool
class ClientThread(Thread):
def __init__(self,ip,port):
Thread.__init__(self)
self.ip = ip
self.port = port
print ("[ ] New server socket thread started for " ip ":" str(port))
def run(self):
while True :
data = conn.recv(64)
#print ("Server received from " ip ".")
if self.ip == '192.168.1.45':
print("hello GPS")
elif self.ip == '192.168.1.253':
time = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3])
vibes = [0]*10
for i in range(0, 10):
vibes[i] = data[(i*2) 4] << 8 | data [(i*2) 5]
print ("Server received from " ip " data: ", time, vibes[0], vibes[1], vibes[2], vibes[3], vibes[4], vibes[5], vibes[6], vibes[7], vibes[8], vibes[9])
# Multithreaded Python server : TCP Server Socket Program Stub
TCP_IP = '0.0.0.0'
TCP_PORT = 80
BUFFER_SIZE = 64 # Usually 1024, but we need quick response
tcpServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcpServer.bind((TCP_IP, TCP_PORT))
threads = []
while True:
tcpServer.listen(4)
print ("Multithreaded Python server : Waiting for connections from TCP clients...")
(conn, (ip,port)) = tcpServer.accept()
newthread = ClientThread(ip,port)
newthread.start()
threads.append(newthread)
I don't really understand what is happening in this code but I assumed that the way it worked is whenever a new connection was made, a new while loop began and could operate at the same time as others. The terminal mostly prints one of the two messages randomly and on the microcontroller side when its sensor data isn't printed I can see the message not received error. Occasionally I see that a new thread is starting and sometimes an out of range error when sorting the packet.
> Server received from 192.168.1.253 data: 236604 12 6 6 25 12 12 37 18
> 18 48 Server received from 192.168.1.253 data: 236423 9 4 4 17 9 9 25
> 14 14 32 [ ] New server socket thread started for 192.168.1.45:58418
> Multithreaded Python server : Waiting for connections from TCP
> clients... Server received from 192.168.1.45 data: 236423 8 6 6 16 12
> 12 23 18 18 30 hello GPS Exception in thread Thread-3: Traceback (most
> recent call last): File "/usr/lib/python3.9/threading.py", line 954,
> in _bootstrap_inner
> self.run() File "/home/pi/Documents/tcp_server2.py", line 25, in run
> vibes[i] = data[(i*2) 4] << 8 | data [(i*2) 5] IndexError: index out of range hello GPS hello GPS [ ] New server socket thread started
> for 192.168.1.253:45001 Multithreaded Python server : Waiting for
> connections from TCP clients...
Can I receive multiple messages at the same time this way or do I need to do something like synchronizing the clients to not send at the same time? Is it better to put each client on a separate port and just set up tcpServer1, tcpServer2, tcpServer3,...?
CodePudding user response:
Analysis:
IndexError: index out of range
This arises because your array doesn't have adequate bytes inside, that is, len(data)<=(i*2) 4
OR len(data)<=(i*2) 5
.
Explanation:
After conn.recv(64)
, there is no guarantee that your data
will receive an integral 64-length bytes array. Your argument 64
can only put a constaint that your data will be receive 64 bytes at most at a time, since accoding to Python Docs:
socket.recv(bufsize[, flags])
Receive data from the socket. The return value is a bytes object representing the data received. The maximum amount of data to be received at once is specified by bufsize. ...
Therefore you need a way to make sure that your data
array contains all the bytes your need later on. One alternative is to wrap with a while{...} loop, repeatedly receiving data from your conn
until it receive all 64 bytes.
Solution:
You can refer to myreceivce
routine suggested in Python official tutorial:
# ...
def myreceive(self):
chunks = []
bytes_recd = 0
while bytes_recd < MSGLEN: # in your case, MSGLEN might be 64
chunk = self.sock.recv(min(MSGLEN - bytes_recd, 2048))
if chunk == b'':
# ...
# ...
CodePudding user response:
Every ClientThread
uses the same global conn
, which is reassigned each time a new connection is accepted, so all those threads try to read from the most recent connection. You rather have to pass conn
to the thread and have it store and use its private connection just as you do with ip
and port
.
When you corrected this, you also should print self.ip
rather than ip
in run
.