This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TM4C1294KCPDT: TM4C- How to handle "connection closed by peer" in TI NDK

Part Number: TM4C1294KCPDT
Other Parts Discussed in Thread: SYSBIOS

Dear Team,

I am writing a function to post http requests over TCP using TI-NDK(2.25.00.09). I am able to successfully post HTTP request and get back the response the very first time.

On the second iteration NDK_recv returns 0 with error code (54 -> Connection reset by peer) . I am stuck up on how to handle this. I tried calling NDK_Connect() again in such case but it totally messes up with another set of error codes.

Could you please help on what would be the right way to proceed once i see "Connection reset by peer" error. Should i  close and recreate the complete socket again for each http request ?, or is there a best efficient way to handle this. I placed my code below

/************************************ Code Here ****************************************/


#define ETHERNETHTTP_TXDATASIZE 512 // Max size of data to be pushed to server at a time
#define ETHERNETHTTP_RXDATASIZE 512 // Max size of data to be fetched from server at a time

int EthernetHTTPSendSocket_1 = -1;
int EthernetHTTPRecvSocket_1 = -1;
char EthernetHTTPSendData_1[ETHERNETHTTP_TXDATASIZE] = "";
char EthernetHTTPRecvData_1[ETHERNETHTTP_RXDATASIZE] = "";
struct sockaddr_in EthernetHTTPSocketAddr_1;

char EthernetHTTPSocket_Restartrequired = true; // Set bit to TRUE if restart of socket is required

uint32_t CommunicationIP = 0x67546006; // www.evergreeninternet.in [103.84.96.6]
char CommunicationIP_number[17] = "103.84.96.6";

/* Task created from Sysbios task scheduler. Not created dynamically */

void Http_SenddataServer_1_Task()
{

int returnval, err;
struct timeval timeoutvalue;
struct sockaddr_in EthernetHTTP_HOSTAddr;
HTTPRESPONSE_TYPE HttpResponse_Socket1;

int HndlSelfTask = TaskSelf();
int optval;
char ReconnectSocket_flg = true;

memset(&EthernetHTTPSocketAddr_1, 0, sizeof(EthernetHTTPSocketAddr_1));
memset(&EthernetHTTP_HOSTAddr, 0, sizeof(EthernetHTTP_HOSTAddr));

EthernetHTTPSocketAddr_1.sin_family = AF_INET;
EthernetHTTPSocketAddr_1.sin_addr.s_addr = htonl(INADDR_ANY);
EthernetHTTPSocketAddr_1.sin_port = htons(TCPPORT_HTTP);

EthernetHTTP_HOSTAddr.sin_family = AF_INET;
EthernetHTTP_HOSTAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Host address to be dynamically changed before send */
EthernetHTTP_HOSTAddr.sin_port = htons(TCPPORT_HTTP);

timeoutvalue.tv_sec = 10; // Receive and transmit timeout (10Seconds)
timeoutvalue.tv_usec = 0;


while(1)
{

while(EthernetHTTPSocket_Restartrequired == true)
{
/* Wait until valid IP is obtained */
while(Ethernet_ETHConnStatus.CurrentIP == 0) Task_sleep(1000);

/* Close socket if was already open */
if(EthernetHTTPSendSocket_1 != -1)
{
fdClose(EthernetHTTPSendSocket_1);
fdCloseSession(HndlSelfTask);
}

fdOpenSession(HndlSelfTask);

/* Create socket */
EthernetHTTPSendSocket_1 = NDK_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (EthernetHTTPSendSocket_1 == -1)
{
/* Error handling here */
err = fdError();
}

/* Bind socket */
returnval = NDK_bind(EthernetHTTPSendSocket_1, (struct sockaddr *)&EthernetHTTPSocketAddr_1, sizeof(EthernetHTTPSocketAddr_1));
if(returnval == -1)
{
/* Error handling here */
err = fdError();
}

/* Set socket options */
returnval = NDK_setsockopt(EthernetHTTPSendSocket_1, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
returnval = NDK_setsockopt(EthernetHTTPSendSocket_1, SOL_SOCKET, SO_SNDTIMEO , &timeoutvalue, sizeof(timeoutvalue));
returnval = NDK_setsockopt(EthernetHTTPSendSocket_1, SOL_SOCKET, SO_RCVTIMEO , &timeoutvalue, sizeof(timeoutvalue));

/* Ethernet restart complete */
EthernetHTTPSocket_Restartrequired = false;
ReconnectSocket_flg = true;
}

/* Reconnect socket */
if(ReconnectSocket_flg == true)
{
EthernetHTTP_HOSTAddr.sin_addr.s_addr = htonl(CommunicationIP); // Set Destination host
returnval = NDK_connect(EthernetHTTPSendSocket_1, (struct sockaddr *)&EthernetHTTP_HOSTAddr, sizeof(EthernetHTTP_HOSTAddr));
if(returnval == -1)
{
/* Error handling here */
err = fdError();
if (err == ECONNREFUSED || err == EHOSTDOWN || err == EHOSTUNREACH)
{
EthernetHTTPSocket_Restartrequired = true;
}
else if(err == EISCONN)
{
/* Connection established -> Socket already connected */
ReconnectSocket_flg = false;
}
}
else
{
/* Connection established*/
ReconnectSocket_flg = false;
}
}

/***************************************************************************/
/********************** Send/Receive data to Socket **************/

/* Wait for data ready semaphore */// To be done //
Http_GetConstruct(CommunicationIP_number, "/", EthernetHTTPSendData_1);   // Sample data to test

returnval = NDK_send(EthernetHTTPSendSocket_1, EthernetHTTPSendData_1, strlen(EthernetHTTPSendData_1), 0);
if(returnval == -1)
{
err = fdError();
if(err == ENOTCONN) /* 57 -> Socket is not connected */
{
ReconnectSocket_flg = true;
}
}
else
{
/* TX success, get response */
returnval = NDK_recv(EthernetHTTPSendSocket_1, EthernetHTTPRecvData_1, ETHERNETHTTP_RXDATASIZE-1, 0);
err = fdError();
if(err == ECONNRESET) /* 54 -> Connection reset by peer */
{
/*
* Comment to TI: Ends up here on the second interation of the while loop, when sending the HTTP GET for the second time
*/

ReconnectSocket_flg = true;
}
if(err == 0){ Http_ResponseDeconstruct(EthernetHTTPRecvData_1, &HttpResponse_Socket1); }
}

}

}


/*
* Function creates HTTP Get request from data
*
* data -> Raw Data to construct
* datalength -> length of data
* outputdat -> contrcuted HTTP get data
*
*/
void Http_GetConstruct(char* host, char* data, char* outputdat)
{

strcpy(outputdat, "GET ");
strcat(outputdat, data);
strcat(outputdat, " HTTP/1.1");

strcat(outputdat, "\r\nHost: ");
strcat(outputdat, host);

strcat(outputdat, "\r\nUser-Agent: HTTPCli (ARM; TI-RTOS)");
strcat(outputdat, "\r\nConnection: keep-alive");
strcat(outputdat, "\r\n\r\n");
}

  • Hi Surya,

    An engineer will look at this later today. In the meantime, can you let us know what version of TI-RTOS you are using?

    Todd

  • Hi Todd

    Thanks for looking into it.

    I am using

    TI-RTOS -> 2.16.1.14

    XDCTool -> 3.32.2.25

  • Hello Surya,

    I was able to run your code here, but I didn't run into the issue you stated. I had to comment out the line calling Http_ResponseDeconstruct(), but I doubt that would cause any problems. 

    Although I did notice you are using the NDK_socket() call incorrectly. NDK_socket() will return a SOCK data type and not an int type. And if NDK_socket() fails it will return INVALID_SOCKET. You should have EthernetHTTPSendSocket_1 be of type SOCK. What you have will still run, but it relies on ints being implicitly cast to a SOCK type, so it's best to avoid that. 

    Do you have a wireshark log of your failure case?

    Also you could try using the HTTP Client API provided by the SDK to perform http requests. An example of it can be found in SDK_INSTALL_DIR/packages/examples/source/httpget.

    Regards,

    Dalton

  • Hi Dalton,

    Thanks for looking at it and your quick response :)

    I noticed our servers connection keepalive time was default 5seconds, and as you exactly mentioned i too dont see a problem on a continuous run since the next data is sent before the timeout, but i noticed this when i placed a breakpoint after every ndk_recv the 5seconds exceeded causing this problem (Connection reset by peer). I have now increased this default value from 5 to 100sec and its perfect now.

    Thanks for your suggestions as well, i have now changed the type of the socket. I took the int type from the Tcpecho example where the socket is defined as int. Could be great if its corrected there as well or it might lead to misleading example. Regarding the Http Client API example it really worked out well for me, but i wanted to keep it as simple as possible to reduce the overhead and processing time since my application would be continuously pushing data to the server.

    My next question is how do we really handle this error if we face it. Should i recreate the socket again ? I made the below 2 try's

    Try-1:

    In this error scenario i tried to call the NDK_Connect() again on the same socket but ended with a error response that the socket is already connected

    Try-2:

    Closed the socket and recreated it (on the same task), and this worked with limitations

    - i closed the socket and reopened like in below code, but ran up with memory overflow quickly. Adding a 100ms delay between the Close and open session solved the overflow issue but till i really doubt if its the best way to handle it.

    if(EthernetHTTPSendSocket_1 != -1)
    {
    fdClose(EthernetHTTPSendSocket_1);
    fdCloseSession(HndlSelfTask);
    }

    fdOpenSession(HndlSelfTask); (followed by NDK_Socket, bind, options)..

  • Greetings Mr. Prakash95,

    As a fellow (outsider) my small staff & I must applaud your, "Care, Detail, & Completeness" - all evident w/in your rapid response.    Excellent!

    Your effort is likely to benefit others as well - which nicely amplifies this forum's strength & value.    Very well done...

  • Greeting from my side as well!. :)

    Its really good to hear this acknowledgement from your side.

    It give me immense confidence that i am doing things the right way as intended to the best of everyone. 

    Thanks a lot :). Looking forward for the response to the query.

  • Hello Surya,

    When you get a ECONNRESET from any socket operation the correct procedure would be to close the socket and start the process over with a new socket. Reconnecting with the same socket won't work as the ECONNRESET error indicates the server is closing this connection and will not accept further communication on it. 

    You also won't need to call fdOpenSession() again. This only needs to be called once inside the task, so I'd advise placing it before the while(1) loop. No real need to close the session again either as you never exit the while(1) loop. 

    What exactly are the memory overflow problems you are experiencing? Is it a stack overflow, or are socket calls returning ENOMEM? It might be due to repeated fdCloseSession/fdOpenSession calls, so you can see if that fixes it.

    Finally it is a bit confusing, but the tcpEcho example is actually using the socket return type correctly too. That example includes <sys/socket.h> which is a bsd layer in the ndk. With that header file you call bsd socket calls without the NDK_ prefix and those do return int fds just like bsd sockets. Under the covers those call the NDK_ socket calls too, but some translation occurs to make this work. To sum up, if your socket calls use a NDK_ prefix they will accept the SOCKET type otherwise they will accept int types.

    Sorry for confusing you but I did mean the return type should be SOCKET the first time I responded. There is a SOCK type in the ndk, but it is only used internally in the stack. 

    Regards,

    Dalton

  • Hello Dalton,

    Thanks for your suggestions. I made the below try's

    - Calling fdOpenSession() only once at the beginning of the while loop works out. And you were right, calling fdOpenSession() and fdCloseSession() multiple times caused the error "ENOMEM". On the other hand calling Open and Close session without a delay in between caused a stack overflow issue. A minimum delay of 100ms was required to overcome the stackoverflow issue.

    -  I tried to close the Socket and recreate it in case of "ECONNRESET" error. I called just fdclose(socket) to close the socket. On recreation i face a new error at NDK_bind() as "48 -> EADDRINUSE". Could you please help is i am closing the socket properly or missing something ?. (Info: previous command NDK_Socket() did not cause any errors and the socket was created.)

    Regarding the Socket type: I took it from here in the example. But nevertheless thanks for the info, i have now made the changes in my code to refer to the right type as suggested, and thanks for your explanation as well :)

    Regards 

    Surya

  • Hello Surya,

    The NDK_bind() is failing because you are trying to bind to an address that is already in use. It might appear that fdClose() (which is the correct way to close a socket) would free up that address, but TCP dictates otherwise. When you call fdClose() the TCP protocol will go into a TIME_WAIT state and really close the socket after 2 minutes.

    Don't worry though, you don't need to wait 2 minutes. You should just use a different address each time you make a socket. I see you are setting:

    EthernetHTTPSocketAddr_1.sin_port = htons(TCPPORT_HTTP);

    That does not need to be done. The port on the device can be anything, you just need to make sure the port you are sending to is the correct HTTP port. If you change the code to:

    EthernetHTTPSocketAddr_1.sin_port = 0;

    Then whenever you call bind with that sockaddr struct the NDK will pick any available port for you. 

    What's the stack size of the task you are running? It might need to be bigger.

    Regards,

    Dalton

  • Hi Dalton,

    Thanks, that was a very good explanation. 

    As per your suggestion i set the Port to 0 and i have no issues now. It works perfect.. Hats off :) I simulated nearly 10 tries to end with "Connection reset by peer error" and recreated the socket. In any of these cases i did not endup with any errors now during recreating of sockets :)

    My stack size is 4096. Hope this should be enough ?

    Regards

    Surya

  • HI Surya,

    4096 should be plenty for what you are doing. Do you no longer need the delay after moving fdOpenSession()? If so I think the overflow was just a product of the repeated calls to open and close session.

    Regards,

    Dalton