Hello everyone!
I am using a Teensy 4.1 as a server, it can receive commands (separated by \n) over ethernet using the TCP protocol. I have an issue where sometimes the server never responds to a command, or it takes a long time.
I have been able to reduce the server to the following code, which is simply an echo server:
The problem is in the client.stop() call. I will preface that I don't know much about TCP, or networking control flow in general.
If I run the server without the problematic part, then the echoing works fine, but for every transaction the Teensy throws a TCP RST packet, as shown in the Wireshark screenshot below:
This feels wrong to me, and also, the environment I plan to run the device in throws an exception if this RST packet is received. My solution was the code in the problematic part. The client sends "CLOSE\n" when it is done with the socket, and will send no more commands. Then, the server responds with "CLOSE OK\n", and both parties can close the socket on their end. This works well most of the time, as is shown in the following Wireshark screenshot:
No RST packets, and both sides seem happy. There is one problem however: sporadically, the Teensy waits multiple seconds before replying and closing the socket. Sometimes no reply is sent at all. If I comment out the client.stop() part this does not occur. Two examples of this in Wireshark: (packets 26643 and 2664 have ~1.7s between them)
and here another instance (~2.9s):
Here is the python code I use to send the TCP packets, for easy reproduction.
My test setup is a USB-ethernet dongle connected to my PC, with the ethernet cable going to the Teensy. I highly doubt the connection can be the problem, both because without the problematic code everything works fine, and a pingtest with powershell Test-Connection reveals a ping time of 1 ms or 0 ms (great job on the rounding microsoft, I highly doubt it really takes 0ms...).
I suppose my question is two-fold:
Thanks for taking the time to read my post!
I am using a Teensy 4.1 as a server, it can receive commands (separated by \n) over ethernet using the TCP protocol. I have an issue where sometimes the server never responds to a command, or it takes a long time.
I have been able to reduce the server to the following code, which is simply an echo server:
Code:
#include <NativeEthernet.h>
// mac address
byte mac[] = { 0xBE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// Ip address
byte ip[] = { 192, 168, 1, 177 };
int tcp_port = 5025;
char packetBuffer[256];
EthernetServer server = EthernetServer(tcp_port);
void setup()
{
// initialize the ethernet device
Ethernet.begin(mac, ip);
// start server for listenign for clients
server.begin();
}
void loop()
{
// if an incoming client connects, there will be bytes available to read:
EthernetClient client = server.available();
while (client.available()) {
unsigned char read_length = client.readBytesUntil('\n',packetBuffer,256);
// read bytes from the incoming client and write them back
// to the same client connected to the server
client.write(packetBuffer,read_length);
//--------------problematic part BEGIN//
if (strcmp("CLOSE",packetBuffer) == 0){
client.write("CLOSE OK\n",9);
client.stop(); // problematic line
}
//--------------problematic part END//
}
}
If I run the server without the problematic part, then the echoing works fine, but for every transaction the Teensy throws a TCP RST packet, as shown in the Wireshark screenshot below:
This feels wrong to me, and also, the environment I plan to run the device in throws an exception if this RST packet is received. My solution was the code in the problematic part. The client sends "CLOSE\n" when it is done with the socket, and will send no more commands. Then, the server responds with "CLOSE OK\n", and both parties can close the socket on their end. This works well most of the time, as is shown in the following Wireshark screenshot:
No RST packets, and both sides seem happy. There is one problem however: sporadically, the Teensy waits multiple seconds before replying and closing the socket. Sometimes no reply is sent at all. If I comment out the client.stop() part this does not occur. Two examples of this in Wireshark: (packets 26643 and 2664 have ~1.7s between them)
and here another instance (~2.9s):
Here is the python code I use to send the TCP packets, for easy reproduction.
Code:
import asyncio
import time
from timeit import default_timer as timer
async def tcp_echo_client(message):
reader, writer = await asyncio.open_connection(
'192.168.1.177', 5025,limit = 10000000)
writer.write(message + b"CLOSE\n")
await writer.drain()
try:
data = await asyncio.wait_for(reader.read(100),timeout = 5)
except asyncio.TimeoutError:
data = "0"
try:
await asyncio.wait_for(reader.read(9),timeout = 5) # CLOSE OK\n
except asyncio.TimeoutError:
pass
writer.close()
return data
def main():
manual = """Client example."""
print(manual)
while(1):
print("""Type a duration, and press enter to start sending messages.
Type 0 to exit.""")
cmd = int(input())
delaytime = 0.02
if cmd == 0:
exit()
i = 0
times = []
maxdt = 0
start = timer()
while i < cmd:
i += 1
reply = asyncio.run(tcp_echo_client(b"TESTDATA\n"))
current_time = timer()
time_diff_since_start = current_time - start
times.append(time_diff_since_start)
if i > 1:
dt = abs(times[i-2] - time_diff_since_start)
if dt>maxdt:
maxdt = dt
print(time_diff_since_start)
print(reply)
time.sleep(delaytime)
print(f"Maximum time delta was:{maxdt}")
main()
I suppose my question is two-fold:
- What is the proper way of resolving the closing of the socket on both ends? Am I on the right track with my "CLOSE OK\n" message indicating it's OK to close the socket, or should I think of something else/more robust?
- Why does the Teensy freeze up in this case?
Thanks for taking the time to read my post!