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.

EK-TM4C129EXL: NDK 2.25 TCP connect using sockets in non-blocking mode

Part Number: EK-TM4C129EXL

Hi, I'm trying to connect using sockets in non-blocking mode to a remote host that accepts the connection and does not send anything back, it is my responsibility to start the dialog (MQTT server).

I can see the connection establishes (Wireshark) and 15 seconds later the server gets bored of my silence and disconnects. I'm waiting on select(), and there is no indication of connection establishment, only read and error get set when the server closes the connection.

If I try with a telnet server, for example, since the server sends data on connection, I can detect connection establishment because select() returns read poll ready. What do I need to do to detect connection established if the server does not send anything on connection accept ?

NDK is version 2.25, my task is created on netIPAddrHook because my address is assigned by DHCP. Code follows.

#include <serrno.h>
#include <sys/socket.h>


  struct sockaddr_in myaddr;

  memset(&myaddr, 0, sizeof(myaddr));
  myaddr.sin_family = AF_INET;
  myaddr.sin_port = htons(1883);
  myaddr.sin_addr.s_addr = inet_addr("3.72.239.144");

  int fd = socket(AF_INET, SOCK_STREAM, 0);
  if (fd == -1) {
    printf("socket(): %d\n", errno);
  } else {
    int val = 0;
    setsockopt(fd, SOL_SOCKET, SO_BLOCKING, &val, sizeof(val));
    int status = 0;
    int res = SockStatus(fd, FDSTATUS_SEND, &status);
    if (res == 0 && status > 0) {
      val = status / 2;
      int val_size = sizeof(val);
      res = SockSet(fd, SOL_SOCKET, SO_SNDLOWAT, &val, val_size);
    }
    if (connect(fd, (struct sockaddr *) &myaddr, sizeof(myaddr)) == 0) {
      printf("just connected\n");
    } else if (errno == EINPROGRESS) {
      printf("connect pending\n");
    } else {
      printf("connect failed: %d\n", errno);
    }
    int rc = 0;

    while (!rc) {
      fd_set rset, wset, eset;
      struct timeval tv = {1, 0};

      FD_ZERO(&rset);
      FD_ZERO(&wset);
      FD_ZERO(&eset);
      FD_SET(fd, &eset);
      FD_SET(fd, &rset);
      FD_SET(fd, &wset);

      if ((rc = select(fd + 1, &rset, &wset, &eset, &tv)) < 0) {
        printf("error: %d %d\n", rc, errno);
      } else {
        printf("select: %d\t%d %d %d\n", rc, FD_ISSET(fd, &rset), FD_ISSET(fd, &wset), FD_ISSET(fd, &eset));
      }
    }
  }

void netIPAddrHook(unsigned int IPAddr, unsigned int IfIdx, unsigned int fAdd) {

    ...
    taskHandle = Task_create((Task_FuncPtr) mgTask, &taskParams, &eb);
}

void netOpenHook() {
  System_flush();
}

Output:

Using MAC address in flash
Service Status: DHCPC    : Enabled  :          : 000
Service Status: DHCPC    : Enabled  : Running  : 000
Network Added: If-1:192.168.69.243
Service Status: DHCPC    : Enabled  : Running  : 017
connect pending
select: 0    0 0 0
select: 0    0 0 0
select: 0    0 0 0
select: 0    0 0 0
select: 0    0 0 0
select: 0    0 0 0
select: 0    0 0 0
select: 0    0 0 0
select: 0    0 0 0
select: 0    0 0 0
select: 0    0 0 0
select: 0    0 0 0
select: 0    0 0 0
select: 0    0 0 0
select: 0    0 0 0
select: 2    1 0 1

  • Hi,

      I have not seen select() being used on the client side. I have mostly seen select() used for the server side. I must say I'm not an expert in how to use the select().

      If the server closes the connection, can the client periodically ping the server to find out if the connection is still there?

      I find this link talking about how to detect if a server closed a TCP connection. Hopefully, it will help. 

    https://linuxgazette.net/issue76/tag/9.html

  • My question was on how to detect connection establishment, it is not working for me as per the docs:

    API (SPRU524J) section 3.3.3 page 59

    connect       Initiate a Connection on a Socket
    Return Value
    If it succeeds, the function returns 0. Otherwise, a value of -1 is returned and the
    function errno() can be called to determine the type of error. Possible errors are:
    EINPROGRESS  The request was accepted and is pending (non-blocking sockets).
    It is possible to select() a socket for the purposes of doing a connect by selecting it for
    writing.


    This very scheme works OK on Linux and other RTOS/stacks with minimal name changes (mainly returned value and how to set the socket to non-blocking mode).

    The read indicator is not activated unless the server sends something. The write indicator is not activated even though the docs claim it will be. Either I'm not setting the socket properly or the docs are wrong or NDK has a bug. I'd rather like it to be my fault.


    As the output shows, I'm already detecting connection closure.

  • Hi,

      Never mind about what I just posted. I think I understand what you are saying. You are saying you are expecting the select() to return an error but it does not. Is that right?

  • NO, please see the program output above, reproduced and  commented here
    The 1st number is what select() returns, the other 3 numbers are 1 if select() says the socket is ready for Read, Write, or has an Error condition
    Timeout is 1 second, each line is 1 second after the other

    Using MAC address in flash
    Service Status: DHCPC    : Enabled  :          : 000
    Service Status: DHCPC    : Enabled  : Running  : 000
    Network Added: If-1:192.168.69.243
    Service Status: DHCPC    : Enabled  : Running  : 017
    connect pending    <--- SYN sent
    select: 0    0 0 0     1 second later I see SYN ACK +  ACK, the connection has been established
    select: 0    0 0 0     but Write is not active, my task does not know it is connected
    select: 0    0 0 0    
    select: 0    0 0 0
    select: 0    0 0 0
    select: 0    0 0 0
    select: 0    0 0 0
    select: 0    0 0 0
    select: 0    0 0 0
    select: 0    0 0 0
    select: 0    0 0 0
    select: 0    0 0 0
    select: 0    0 0 0
    select: 0    0 0 0
    select: 0    0 0 0
    select: 2    1 0 1    <--- Server closes connection (Read + Error), I do see the connection close

    What I don't see is the connection establishment, when the connection is done, when the server accepts the connection, when the TCP 3-way handshake is finished and the data phase takes place, when I should be ready to send data.

  • Hi,

    connect pending    <--- SYN sent
    select: 0    0 0 0     1 second later I see SYN ACK +  ACK, the connection has been established
    select: 0    0 0 0     but Write is not active, my task does not know it is connected

      I suppose you are expecting the below to happen right after the SYNC is sent - much earlier than the server closes the connection, right?

      select: 3  1 1 1

        I'm really not an expert in NDK but I have a few questions.

      - Is it possible that the select() returns zero because of the timeout? What if you increase the timeout value to a larger value? Will the select() return a positive non-zero value?

      - What if you use blocking mode?

      - What if you use poll() instead of select()?

      - Are there other approaches you can try if select() is not working as expected? Can you ping the server until the connection is established?

  • No, I'm expecting

    select:  0 1 0

    The server won't send anything so R won't be triggered, and no error should have happened so E won't be triggered.

    The TCP 3-way handshake is the following: the client sends SYN, the server returns SYN ACK, the client sends ACK. At that point the connection has established and the socket becomes ready for writing. select() should signal that, by returning with the proper indication before the timeout expires. As I sniff the network, the connection establishes in less than a second, and that is what I get when I run this in other systems.

    Of course select() returns zero because of the timeout, this is not NDK-specific, all BSD socket compliant systems work this way. If the server sends any data, the socket becomes ready for reading and select() returns, this works, but in this case the server won't send anything and according to the docs (as I quoted and referenced in prior mails), the socket should become ready for writing when the server accepts the connection, and this is not happening.

    If I increase the timeout to infinity, all those 0's will disappear cause the sum of many timeouts with no data is a big, longer, timeout with no data. select() will return when the server closes the connection.

    If I could use blocking mode I wouldn't be here explaining this to a TI expert

    select() internally calls fdSelect() which converts the data to the poll descriptor list format and calls fdPoll(). That is in the NDK documentation: SPRU524J section 3.3.2 page 54, and section 3.2.1 page 47

    No, I can't ping the server, a ping is an ICMP exchange and is in no way related to the acceptance of a connection request in TCP. This is about to port to TI-RTOS a system that is already working with FreeRTOS-TCP, Linux, lwIP over FreeRTOS, Keil RTX, Windows with and without Winsock, Mac, and Zephyr.

    The code above is a minimal representation which, as I already explained, works OK with minimal function name changes on all the other systems, please forward this to the appropriate personnel.

  • I mean:

    connect pending
    select: 1    0 1 0

  • Hi Sergio,

      Thanks for the explanation. I must admit you are way more knowledgeable than me on this regard and I wish I could help. TI-RTOS/NDK is in maintenance mode right now and we don't have an expert in our group whose knowledge is mainly on the MCU, not the NDK stack. I could try to ask around outside of our group who may be able to answer your question. Reading your code and online help about the file descriptor handling, I don't really see an issue with your code and most likely others who look at your code will agree with me.  Perhaps it is something in the stack. I hate to ask you to debug the stack on your own but if you find something obvious, you are free to modify as NDK is open source. As I mentioned, NDK is in maintenance mode with no plan for update for TM4C129. In the meantime, I will search e2e archives to see if your problem description has been reported and if there is any remedy or workaround for it.  Sorry for lack of guidance.

  • I just solved it.

    There is a socket option, SO_SNDLOWAT:

    The default value for SO_SNDLOWAT is set to a
    convenient size for network efficiency, often 1024.

    Well, it turns out that the stack decided to set it to 2048 and the application was configured for a smaller buffer size.

    A select operation testing the ability to
    write to a socket will return true only if the low water mark amount could
    be processed

    SPRU524J section 3.3.3 page 63

    Hence, the socket wasn't actually ready for writing...

  • Hi Sergio,

      Thank you so much for your update and really glad you solved the problem. I will bookmarked your answer so I can reference it for future inquiry of the same topic.