I'm receiving data from socket. I connect to socket and receive data with a specific size in while
loop with socket.recv(size)
.
All works normally in high receive frequency.
But, if I add a time.sleep(sec)
in while
loop, I start to receive the outdated value every time. Looks like socket is filled with old data and as long as host send data up to 0.002 times in a sec, I can receive only outdated data with my 1 sec frequency of receiving (as example).
I can't share the data from socket (as long as it will be too wide and heavy for post), but here is the code:
import ctypes
import datetime
import logging
import socket
import time
from app.service.c_structures import RTDStructure
logging.basicConfig(level=logging.DEBUG)
class RTDSerializer:
def __init__(self, ip: str, port: int = 29000, frequency: float = 0.002):
self.data: dict = {}
self.ip = ip
self.port = port
self.frequency = frequency
self.sock = None
self.struct_size = ctypes.sizeof(RTDStructure)
self.logger = logging
print(ctypes.sizeof(RTDStructure))
def connect(self):
try:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((self.ip, self.port))
self.sock.settimeout(None)
logging.debug(f"Socket connect [{self.ip}:{self.port}] --> Ok")
while True:
c_structure = RTDStructure.from_buffer_copy(self.receive_raw_data() * ctypes.sizeof(RTDStructure))
self.data = self.ctypes_to_dict(c_structure)
print(datetime.datetime.now(), self.data['move_des_q'])
time.sleep(self.frequency)
except Exception as error:
logging.error(f"Socket connect [{self.ip}:{self.port}] --> False\n{error}")
return 0
def receive_raw_data(self) -> bytes or connect:
raw_data = self.sock.recv(self.struct_size)
if raw_data == b'':
logging.error('Connection lost')
return self.connect()
return raw_data
def ctypes_to_dict(self, ctypes_obj) -> dict or list:
if isinstance(ctypes_obj, ctypes.Structure):
data_dict = {}
for field_name, field_type in ctypes_obj.get_fields():
field_value = getattr(ctypes_obj, field_name)
if isinstance(field_value, (ctypes.Structure, ctypes.Array)):
data_dict[field_name] = self.ctypes_to_dict(field_value)
else:
data_dict[field_name] = field_value
return data_dict
elif isinstance(ctypes_obj, ctypes.Array):
data_list = []
for element in ctypes_obj:
if isinstance(element, (ctypes.Structure, ctypes.Array)):
data_list.append(self.ctypes_to_dict(element))
else:
data_list.append(element)
return data_list
if __name__ == '__main__':
rtd = RTDSerializer(ip='192.168.0.226', port=29000, frequency=0.05)
rtd.connect()
I receive data from socket. It's a bytestring with C values. I serialize it with ctypes structure and convert to dictionary. The serialization algorithm is not important for this topic.
In addition, the socket returns 0 bytes sometimes, so, I need to check this issue every time in receive_raw_data
func.
I've tried to force reconnect while
loop. And this solves the problem with outdated info somehow. Like this:
def connect(self):
try:
logging.debug(f"Socket connect [{self.ip}:{self.port}] --> Ok")
while True:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((self.ip, self.port))
self.sock.settimeout(None)
c_structure = RTDStructure.from_buffer_copy(self.receive_raw_data() * ctypes.sizeof(RTDStructure))
self.data = self.ctypes_to_dict(c_structure)
print(datetime.datetime.now(), self.data['move_des_q'])
time.sleep(self.frequency)
self.sock.close()
except Exception as error:
logging.error(f"Socket connect [{self.ip}:{self.port}] --> False\n{error}")
return 0
But, it affects the working speed too much when I'm using very high receiving frequency. In terms of host sending frequency, it's really important.
So, how can I solve this problem?
What's the problem with socket disconnection?
Why socket is going fill up and just don't through away the outdated data?
I'm thinking about clear_buffer
function, which will through away all unused data. while the thread is sleeping, but, this is gonna be kinda unreasonable complicated... Any thoughts?
UPD: May be I should just write a printing thread, which will just upwoke once a time, set up by frequency val, print the current value from data_buffer
, which is going to be filled with socket and then sleep again, meanwhile socket thread will run in high frequency and overwrite this data_buffer
value?
New little UPD with micro example:
import ctypes
import datetime
import logging
import socket
import time
logging.basicConfig(level=logging.DEBUG)
class RTDReceiver:
def __init__(self, ip: str, port: int, frequency: float = 0.002):
self.data: dict = {}
self.ip = ip
self.port = port
self.frequency = frequency
self.sock = None
self.struct_size = 1064 # 1064 bytes in my case.
def connect(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((self.ip, self.port))
self.sock.settimeout(None)
def receive_raw_data(self) -> bytes:
raw_data = self.sock.recv(self.struct_size)
if raw_data == b'':
logging.error('Connection lost')
self.connect()
time.sleep(self.frequency)
return raw_data
if __name__ == '__main__':
rec = RTDReceiver(ip='here your ip', port='here is your port', frequency=0.02)
rec.connect()
while True:
print(rec.receive_raw_data())