Greetings!
When I recently updated to RTCS 4.2.0.0 (and RTCS 4.2.0.1). I have the OPT_KEEPALIVE property enabled on one of my "control" sockets that I am using for a home brew FTP server.
In RTCS 4.1, no issues, seems to work as intended.
In RTCS 4.2, when the control socket connection sits idle for a few minutes the server side (MQX/RTCS) the first KEEPALIVE sent ends up resetting the socket connection. KEEPALIVE should not do this unless the remote side is gone. Two keep alive probes from RTCS (ID=80 and 81), one keepalive response from the host , then RTCS resets the connection.
Any clues?!?!
PMT
HI PMT,
Workaround could be to call the setsockopt() after listen()/connect() returns.
Regards,
Carlos
Hi PMT,
I am nt having issueas with OPT_KEEPALIVE.
Can you please describe how can I reproduce this behavior?
Regards,
Carlos
This is basically what I am doing:
Create a listening socket on the RTCS side with the following socket options:
OPT_KEEPALIVE = 2
OPT_TIMEWAIT_TIMEOUT = 5000
Connect to the socket (i.e. TELNET from PC).
Let the connection sit idle for the KEEPALIVE period, 2 minutes.
Connection will then reset as a result of the KEEPALIVE message.
Let me know if you can't reproduce, then I can work on creating a code snippet test case.
Thanks,
PMT
Hi PMT,
it would be useful if you could share the code snippet.
Thanks!
Carlos
Carlos,
Test case at bottom.
Instructions:
Call ListenTask() with a port number of your choice, example ListenTask(23);
telnet to the port from a PC, "telnet x.x.x.x"
Don't any any activity on the socket... simply wait a couple minutes
The application has a keepalive of 1 minute. It will incorrectly disconnect after this time. Change the keepalive to 0 by changing FTPD_SOCKET_KEEPALIVE_TIMEOUT_M (0)
Try the same as above. Socket will not disconnect.
This only happens in RTCS4.2, but not 4.1.
#include <mqx.h>
#include <bsp.h>
#include <rtcs.h>
#define FTPD_SOCKET_WAIT_TIMEOUT_MS (5000)
#define FTPD_SOCKET_KEEPALIVE_TIMEOUT_M (1)
#define FTPD_MAX_MESSAGE_SIZE (128)
#define FTPD_SESSION_LIMIT (6)
#define FTPD_PENDING_LIMIT (3)
static const uint32_t OptValueTimeWaitTimeout = FTPD_SOCKET_WAIT_TIMEOUT_MS;
static const uint32_t OptValueKeepAliveTimeout = FTPD_SOCKET_KEEPALIVE_TIMEOUT_M;
// Session information
typedef struct
{
bool Active;
uint32_t CtrlSocket; // Socket handle for control connection
}
FTPD_SESSION;
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static _task_id TaskCreate(void (_CODE_PTR_ TaskPtr)(uint32_t),
uint32_t StackSize,
uint32_t Priority,
const char *Name,
uint32_t Parameter)
{
TASK_TEMPLATE_STRUCT Task;
memset((void *) &Task, 0, sizeof(Task));
Task.TASK_ADDRESS = TaskPtr;
Task.TASK_STACKSIZE = StackSize;
Task.TASK_PRIORITY = Priority;
Task.TASK_ATTRIBUTES = MQX_FLOATING_POINT_TASK;
Task.TASK_NAME = (char *) Name;
Task.CREATION_PARAMETER = Parameter;
return _task_create(0,0,(uint32_t) &Task);
}
// Handle a new session
static void SessionTask(uint32_t Parameters)
{
FTPD_SESSION *SessionPtr = (FTPD_SESSION *) Parameters;
char cmd[FTPD_MAX_MESSAGE_SIZE];
uint32_t count = FTPD_SOCKET_KEEPALIVE_TIMEOUT_M;
RTCS_attachsock(SessionPtr->CtrlSocket);
printf("Socket connected with KEEPALIVE = %u\r\n", count);
while(1)
{
// read from socket and throw away data
count = recv(SessionPtr->CtrlSocket, cmd, 1, 0);
if (count == RTCS_ERROR) break;
}
printf("Socket shutting down\r\n");
shutdown(SessionPtr->CtrlSocket, FLAG_CLOSE_TX);
SessionPtr->Active = FALSE;
_task_destroy(_task_get_id());
return;
}
// Listen Socket
void ListenTask(uint32_t ListenPort)
{
struct sockaddr_in remoteAddr, localAddr;
FTPD_SESSION FtpdSession[FTPD_SESSION_LIMIT];
uint16_t addrLen;
uint32_t i, Socket, SocketNew, Priority;
uint32_t MaxPending = FTPD_PENDING_LIMIT;
// Init FTPd session tracking
for (i=0; i<FTPD_SESSION_LIMIT; i++) FtpdSession[i].Active = FALSE;
// store the current task priority
_task_get_priority(_task_get_id(), &Priority);
// create TCP/IP socket
if ( (Socket = socket(AF_INET, SOCK_STREAM, 0)) == RTCS_HANDLE_ERROR)
{
_task_destroy(_task_get_id());
return;
}
setsockopt(Socket, SOL_TCP, OPT_KEEPALIVE, (void *) &OptValueKeepAliveTimeout, sizeof(OptValueKeepAliveTimeout));
setsockopt(Socket, SOL_TCP, OPT_TIMEWAIT_TIMEOUT, (void *) &OptValueTimeWaitTimeout, sizeof(OptValueTimeWaitTimeout));
// Bind socket to the listen port
localAddr.sin_family = AF_INET;
localAddr.sin_port = ListenPort;
localAddr.sin_addr.s_addr = INADDR_ANY;
if (bind(Socket, (struct sockaddr_in *) &localAddr, sizeof(localAddr)) != RTCS_OK)
{
shutdown(Socket, FLAG_CLOSE_TX);
_task_destroy(_task_get_id());
return;
}
// Listen on listen port
if (listen(Socket, MaxPending) != RTCS_OK)
{
shutdown(Socket, FLAG_CLOSE_TX);
_task_destroy(_task_get_id());
return;
}
while(1)
{
// Accept new sessions
addrLen = sizeof(remoteAddr);
SocketNew = accept(Socket, (struct sockaddr_in *) &remoteAddr, &addrLen);
if (SocketNew == RTCS_HANDLE_ERROR)
{
_time_delay(1000);
}
else
{
setsockopt(SocketNew, SOL_TCP, OPT_TIMEWAIT_TIMEOUT, (void *) &OptValueTimeWaitTimeout, sizeof(OptValueTimeWaitTimeout));
// find a session and start the session task
for (i=0; i<FTPD_SESSION_LIMIT; i++)
{
if (FtpdSession[i].Active == FALSE)
{
// start a new session
memset(&(FtpdSession[i]), 0, sizeof(FtpdSession[0]));
FtpdSession[i].Active = TRUE;
FtpdSession[i].CtrlSocket = SocketNew;
RTCS_detachsock(SocketNew);
// Spawn session handler
if (TaskCreate(SessionTask, 1024, Priority, "Session", (uint32_t) &FtpdSession[i]) == MQX_NULL_TASK_ID)
{
FtpdSession[i].Active = FALSE;
shutdown(SocketNew, FLAG_CLOSE_TX);
}
break;
}
}
// If no free session so close the connection
if (i == FTPD_SESSION_LIMIT)
{
shutdown(SocketNew, FLAG_CLOSE_TX);
}
}
// Limit the number of accept loops we process every second.
_time_delay(250);
}
}
HI PMT,
thank you, I reproduced this issue and confirmed this is a bug. I reported it to MQX development team. Just for your reference the report number is MQX-5684.
I will keep you informed about the status of the issue.
We really appreciate this inputs.
Regards,
Carlos