What is wrong with my hearbeat code??

117 views
Skip to first unread message

Bill Engelke

unread,
Jul 21, 2020, 10:54:57 AM7/21/20
to libuv
I need code that will send a "heartbeat" to a web service (say, once per minute), and receive a response. This works fine with a browser, so I know the server is reachable and responding correctly. I have tried a vast number of different ways of doing this using libuv and c code, and so far, partial success at best. I have been able to connect to the server and send a request; but only the first request would work, and further sends would not make it to the server.  In my most recent attempts, even the connection request stopped working. I don't know if the problem is my incorrect use of libuv, or something in the networking part that I don't understand.  I have put the code into a single app for testing below; can anyone offer any suggestions??

```

#include <stdio.h>
#include <stdlib.h>
#include <uv.h>
#include <string.h>
#include <unistd.h>

typedef struct {
  uv_write_t req;
  uv_buf_t buf;
 } write_req_t;

static uv_tcp_t* socket_central;
static struct sockaddr_in central_dest;

static void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
  buf->base = malloc(suggested_size);
  buf->len = suggested_size;
}

uv_loop_t *loop;

/////   Callback for after TCP stream is closed  //////////////////
void on_close(uv_handle_t * handle)
  {
  printf("uv close triggered\n");
  }

// process response from central server
void proc_central_response(uv_stream_t *central, ssize_t nread, const uv_buf_t *buf)
  {
  printf("RESPONSE from Central Control; nread=%li\n",nread);
  printf("---------------------------------------------------------------\n");
  if (nread > 0) {
    printf("buf='%s'\n",buf->base);
  printf("---------------------------------------------------------------\n");
    }
  if (nread < 0) {
    if (nread != UV_EOF)
          fprintf(stderr, "Read error %s\n", uv_err_name(nread));
    free(buf->base);
    uv_close((uv_handle_t*) central, on_close);
    return;
    }
  //uv_close((uv_handle_t*) central, on_close);
  free(buf->base);
  return;
  }

// now connected; send http POST
void on_central_connect(uv_connect_t* connection, int status)
  {
  uv_stream_t* stream;
  uv_write_t write_req;
  printf("Connected to Central Contro, status = %il\n",status);
  if (status < 0) {
     fprintf(stderr, "Error in in connecting to Central Control %s\n", uv_strerror(status));
        // error!
      return;
    }
  printf(" - - - - Connect to Central Control - - - ;\n");
    // get the handle to the stream passed in connection
  stream = connection->handle;

    // make ready to receive response from Central
  uv_read_start((uv_stream_t*) stream, alloc_buffer, proc_central_response);

  // Now, prepare a heartbeat message to send to Central Control

  struct timeval timeout;
  char message[1024];
  timeout.tv_sec = 10;  // if Central Host doesn't respond, we ignore
  timeout.tv_usec = 0;  //  and will try again after the pause time
  char tbuf[30];
  struct timeval tv;
  time_t curtime;

  char mytoken[75];
  strcpy(mytoken,"T12345XYZ");  // hard code this for now

  gettimeofday(&tv, NULL);
  curtime = tv.tv_sec;
  strftime(tbuf, 30, "%m-%d-%Y-%T.",localtime(&curtime));
  printf("Heartbeat TOD: '%s' %ld\n",tbuf,tv.tv_usec);
  // write message to go to host
  // message contains identity token and time stamp
  sprintf(message,"POST /apikey/%s-%s HTTP/1.0\r\n\r\n",mytoken,tbuf);
  uv_buf_t buf = uv_buf_init(message,strlen(message));
  int r1 = uv_write(&write_req, stream, &buf, 1, NULL);
  }

void heartbeat(void *arg) {
  while(1==1)
  {
  printf("alloc socket\n");
  socket_central = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));\
  printf("tcp init\n");
  uv_tcp_init(loop, socket_central);
  printf("alloc connection\n");
  uv_connect_t* connect = (uv_connect_t*)malloc(sizeof(uv_connect_t));
  uv_ip4_addr("192.168.1.67", 4000, &central_dest);
  printf("CONNECT TO CENTRAL\n");
  uv_tcp_connect(connect, socket_central, (const struct sockaddr*)&central_dest, on_central_connect);
  printf("CONNECTION REQUEST DONE\n");
  printf("sleep start\n");
  sleep(5);
  printf("sleep done\n");
  }
}

int main() {
  printf("start\n");
  loop = uv_default_loop();

  printf("alloc socket\n");
  socket_central = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));\
  printf("tcp init\n");
  uv_tcp_init(loop, socket_central);

  uv_thread_t hb_id;
  printf("start thread\n");
  uv_thread_create(&hb_id, heartbeat, NULL);
  printf("past thread start\n");

  uv_run(loop, UV_RUN_DEFAULT);

  sleep(10);

  printf("Now quitting.\n");

  uv_loop_close(loop);
  //free(loop);
  return 0;
}
```

Reza Mahdi

unread,
Jul 21, 2020, 11:01:57 AM7/21/20
to li...@googlegroups.com
Well, you are using HTTP 1.0
In this version, after the server sends the response, it will close the connection.
You may use HTTP 1.1 with "Connection: keep-alive" header, to signal the server not to close the connection.

--
You received this message because you are subscribed to the Google Groups "libuv" group.
To unsubscribe from this group and stop receiving emails from it, send an email to libuv+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/libuv/08a1297f-db66-4f6c-9682-5fb3ca3aca27o%40googlegroups.com.

Ariel Machado

unread,
Jul 21, 2020, 2:20:32 PM7/21/20
to libuv
Some hints:
* Don't use the event loop from the main thread in another thread. Libuv is not-thread safe.
* You don't need to create a thread to execute the heartbeat, use a timer (uv_timer) to repeat the request.
* Your code must support connection drop and reconnect even in http 1.1. Send a header Connection: keep-alive to keep the connection open for Http 1.0 server.
* every time that the timer runs check connection dropped and reconnect it. When connected or if already connected send the heartbeat requested.
* You detect a connection dropped by status on uv_read_start callback.

Bill Engelke

unread,
Jul 22, 2020, 11:39:49 AM7/22/20
to libuv
Thanks for the feedback.  A follow-up question:  My heartbeat runs only once a minute (or even less); if I would like to let the server close the connection each time (and re-establish it the next time), could I just let it close the connection, and not change that? 
To unsubscribe from this group and stop receiving emails from it, send an email to li...@googlegroups.com.

Bill Engelke

unread,
Jul 22, 2020, 11:41:54 AM7/22/20
to libuv
Thanks for the feedback - the timer is a great idea, I hadn't thought of.  An implementation question:  when my timer callback function is called, a handle is passed.  I need to free this handle at end of callback routine, correct?

Reza Mahdi

unread,
Jul 22, 2020, 11:44:10 AM7/22/20
to li...@googlegroups.com
In fact, you may not relay on other program's behavior. Your app must be failed tolerant. So, after any event that may occur, check the status. 

Suppose the connection get dropped by a wire disconnect....

Get ready for anything

To unsubscribe from this group and stop receiving emails from it, send an email to libuv+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/libuv/f46dd23f-e7f4-462d-aa9c-95e012ee7951o%40googlegroups.com.

WILLIAM n SUSIE ENGELKE

unread,
Jul 22, 2020, 12:12:01 PM7/22/20
to li...@googlegroups.com
Reza - good advice.  To check connection status, I try to write to the socket and then check if the return code (# bytes written) is < 0 - correct? Or is there another status check that you are referring to?

Reza Mahdi

unread,
Jul 22, 2020, 12:26:40 PM7/22/20
to li...@googlegroups.com
Well, in addition to that, I advise you to check the socket validity first ( by uv_is_active) because in some rare cases, the OS may send some portion of buffer, that means the return code is positive but not as equal to buffer size. So, do both of them.

Reply all
Reply to author
Forward
0 new messages