How to send data with libcurl in persistent connections?

Viewed 81

I am using libcurl to connect to a LightStreamer server. Consider the following minimal example:

#include <curl/curl.h>

static size_t writeCallback(char *ptr, size_t size, size_t nmemb, void* userdata)
{
        printf("%.*s\n", size * nmemb, ptr);
        return size * nmemb;
}

int main() {
        const char url[] =
        "http://push.lightstreamer.com/lightstreamer/create_session.txt?LS_protocol=TLCP-2.0.0";
        const char data[] =
        "LS_adapter_set=DEMO&LS_cid=mgQkwtwdysogQz2BJ4Ji%20kOj2Bg";

        CURL *curl = curl_easy_init();

        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);

        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, sizeof(data));

        curl_easy_perform(curl);

        return 0;
}

When running this, the server never closes the connection; this means that curl_easy_perform never returns.

However, I need to transmit data on the same connection to the server, e.g. to subscribe to data items. But from my understanding from the curl documentation I should not call curl_easy_perform a second time while the first perform is still running.

How could this be achieved? I looked into the documentation for the multi interface, but I am not sure if this is what I need.

1 Answers
#include <curl/curl.h>

static size_t writeCallback(char *ptr, size_t size, size_t nmemb, void* userdata) {
  printf("%.*s\n", size * nmemb, ptr);
  return size * nmemb;
}

static size_t headerCallback(char *buffer, size_t size, size_t nitems, void *userdata) {
  int *keep_running = (int *)userdata;
  if (nitems * size == 2 && !strncmp(buffer, "\r\n", 2))
    *keep_running = 0;
  return nitems * size;
}

int main() {
  const char url[] =
        "http://push.lightstreamer.com/lightstreamer/create_session.txt?LS_protocol=TLCP-2.0.0";
  const char data[] =
        "LS_adapter_set=DEMO&LS_cid=mgQkwtwdysogQz2BJ4Ji%20kOj2Bg";

  CURL *curl = curl_easy_init();

  int keep_running = 1;  // keep running until response headers received

  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
  curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headerCallback);
  curl_easy_setopt(curl, CURLOPT_HEADERDATA, &keep_running);
  curl_easy_setopt(curl, CURLOPT_URL, url);
  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
  curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, sizeof(data));

  CURLM *multi_handle = curl_multi_init();
  curl_multi_add_handle(multi_handle, curl);

  CURLMcode mc;
  int still_running = 1;  // keep number of running handles
  do {
    mc = curl_multi_perform(multi_handle, &still_running);
 
    if (!mc) {
      // wait for activity, timeout or "nothing"
      mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);
    }
    if(mc) {
      fprintf(stderr, "curl_multi_poll() failed, code %d.\n", (int)mc);
      break;
    }
  } while(keep_running && still_running);
    
  int queed = 0;
  CURLMsg *msg = curl_multi_info_read(multi_handle, &queed);
  if (msg && (msg->msg == CURLMSG_DONE)) {
    // request finished
    CURLcode rc = msg->data.result;
  } else if (!mc) {
    // request is alive
    curl_socket_t sockfd;
    if (!curl_easy_getinfo(curl, CURLINFO_ACTIVESOCKET, &sockfd)) {
      do {
        if (write(sockfd, "OK\r\n", 4) < 0)
          break;
        
        // keep alive
        CURLMcode mc = curl_multi_perform(multi_handle, &still_running);
        if(mc)
          break;
      } while (still_running);
    }
  }

  curl_multi_remove_handle(multi_handle, curl);
  curl_easy_cleanup(curl);
  curl_multi_cleanup(multi_handle);

  curl_global_cleanup();
  return 0;
}
Related