I have been using a TCP communications between Python and C# program. Python side (server) runs on a raspberry pi 4 and C# side (client) runs on my computer. RPi and PC connected over Wi-Fi. I have a class in python to listen selected port:
class ListenPort:
def __init__(self, port: int, event_handler=None, delay: float = 0.004):
self.__port = port
# other
self.__stop_thread = False
self.out_string = 'null'
self.out_bytes = b'null'
self.__sct = None
self.__thread = None
self.__event_handler = event_handler
self.__delay = delay
def event_call(self):
if self.__event_handler is not None:
self.__event_handler()
def start_listening(self):
self.__thread = Thread(target=self.listening, args=())
self.__thread.start()
def listening(self):
self.__sct = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
self.__sct.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.__sct.bind(('0.0.0.0', self.__port))
self.__sct.listen(1)
connection_out = self.__sct.accept()[0].makefile('rwb')
handler = SplitFrames(connection_out)
while not self.__stop_thread:
try:
handler.write("Waiting for data".encode("utf-8"))
self.out_string = handler.read().decode("utf-8")
self.event_call()
time.sleep(self.__delay)
except (ConnectionAbortedError, BrokenPipeError) as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
file_name = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
# logging
break
self.__sct.shutdown(socket.SHUT_RDWR)
self.__sct.close()
def reset_out(self):
self.out_string = 'null'
self.out_bytes = b'null'
def stop_listening(self):
self.__stop_thread = True
self.reset_out()
if self.__sct is not None:
try:
self.__sct.shutdown(socket.SHUT_RDWR)
except (OSError, Exception) as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
file_name = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
# logging
if self.__thread is not None:
st_time = time.time()
while self.__thread.is_alive():
if time.time() - st_time > 1:
try:
self.__sct.close()
except (OSError, Exception) as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
file_name = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
# logging
st_time = time.time()
and a class SplitFrames that I took somewhere, that helps me with working with TCP on low level:
class SplitFrames(object):
def __init__(self, connection):
self.connection = connection
self.stream = io.BytesIO()
self.count = 0
self.name = ""
def write_camera(self, buf, name):
if buf.startswith(b'\xff\xd8'):
# Start of new frame; send the old one's length
# then the data
size = self.stream.tell()
if size > 0:
nm = self.name.encode("utf-8")
self.connection.write(struct.pack('<L', len(nm)))
self.connection.flush()
self.connection.write(nm)
self.connection.flush()
self.connection.write(struct.pack('<L', size))
self.connection.flush()
self.stream.seek(0)
self.connection.write(self.stream.read(size))
self.count += 1
self.stream.seek(0)
self.connection.flush()
self.stream.write(buf)
self.name = name
def write(self, buf):
self.connection.write(struct.pack('<L', len(buf)))
self.connection.flush()
self.connection.write(buf)
self.count += 1
self.connection.flush()
def read(self) -> bytearray:
data_len = struct.unpack('<L', self.connection.read(struct.calcsize('<L')))[0]
return self.connection.read(data_len)
On the C# side I have a class to talk to selected port:
namespace mynamespacehere
{
public class TalkPort
{
public event EventHandler<EventArgs> OnNeedToSetMessage;
public string OutString = "";
private bool stopThread = false;
private IPEndPoint ipPoint;
private Socket sct;
public Thread thread;
internal TalkPort(IPEndPoint ipPoint)
{
this.ipPoint = ipPoint;
}
private void SetUpSocket()
{
sct = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sct.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
}
public void StartTalking()
{
OutString = "";
this.stopThread = false;
SetUpSocket();
thread = new Thread(() => { Talking(); });
thread.Start();
}
private void Talking()
{
sct.Connect(ipPoint);
while (!this.stopThread)
{
try
{
if (OnNeedToSetMessage != null)
{
OnNeedToSetMessage(this, EventArgs.Empty);
}
string outString = Encoding.ASCII.GetString(FuncadHelper.GetInputBytes(sct));
if (outString == string.Empty)
{
sct.Shutdown(SocketShutdown.Both);
sct.Close();
try
{
SetUpSocket();
sct.Connect(ipPoint);
}
catch (SocketException exx)
{
// logging
}
}
FuncadHelper.SetOutputBytes(Encoding.ASCII.GetBytes(OutString), sct);
}
catch (Exception ex)
{
// logging
}
}
}
public bool IsAlive()
{
if (thread != null)
return thread.IsAlive;
return false;
}
public void StopTalking()
{
this.stopThread = true;
try
{
sct.Shutdown(SocketShutdown.Both);
sct.Close();
}
catch (Exception e)
{
// logging
try
{
sct.Close();
}
catch (Exception e2)
{
// logging
}
}
ResetOut();
}
public void ResetOut()
{
OutString = "";
}
}
}
and the class that helps me with working with TCP on low level:
namespace mynamespacehere
{
public class FuncadHelper
{
public static byte[] GetInputBytes(Socket clientSocket)
{
byte[] rcvLenBytes = new byte[4];
clientSocket.Receive(rcvLenBytes);
UInt32 rcvLen = BytesToInt(rcvLenBytes);
byte[] rcvBytes;
byte[] clientData;
List<byte> rcvBytesList = new List<byte>();
int totalBytes = 0;
while (totalBytes < rcvLen)
{
if (rcvLen - totalBytes < 262144)
{
clientData = new byte[rcvLen - totalBytes];
}
else
{
clientData = new byte[262144];
}
int bytesReceived = clientSocket.Receive(clientData);
rcvBytesList.AddRange(clientData.Take(bytesReceived).ToArray());
totalBytes += bytesReceived;
}
rcvBytes = rcvBytesList.ToArray();
return rcvBytes;
}
public static void SetOutputBytes(byte[] data, Socket clientSocket)
{
byte[] toSendBytes = data;
byte[] toSendLenBytes = System.BitConverter.GetBytes(data.Length);
clientSocket.Send(toSendLenBytes);
clientSocket.Send(toSendBytes);
}
}
}
And the main problem is that when I want to listen to about 10 ports the delay between two sending of data on each port is over 50 ms. There is also a port that conducts a video from camera so delay on the port is over 800 ms. Is there a way to speed up TCP connection using my code?