cURL / Mailing Lists / curl-library / Single Mail

curl-library

curl_multi_socket problem, not reading all data

From: Ragnar Lonn <ragnar_at_gatorhole.com>
Date: Tue, 21 Oct 2008 01:40:49 +0200

Hello curl-library people,

I'm experimenting with using libcurl to simulate multiple web clients
fetching things from a webserver, but I must be doing something wrong
somewhere. I've scanned the list archives and read the docs but can't
understand why it won't work! What happens is my program will only read
part of the data from the webserver. I.e. if I run the program and tell
it to perform 1 fetch of an URL that I know is 8 kB, it will execute and
report getting around 2 kB when it finishes.

What seems to happen is libcurl sets running_handles to 0, which makes
me stop execution but not all data will have been read at that point.
Maybe I have misunderstood something about how it is supposed to work?

Any help would be very appreciated. I'll include the code below. It uses
epoll and has been tested on a Linux (Ubuntu) machine.

cheers,

  /Ragnar

#include <sys/epoll.h>
#include <curl/curl.h>
#include <stdlib.h>

unsigned int total_bytes = 0;
CURL **easy_handles;
CURLM *multi_handle;
int clients_active;
char *url;
int clients;
int epfd;

size_t client_write_data(void *buffer, size_t size, size_t nmemb, void
*userp) {
  total_bytes += (unsigned int)(nmemb * size);
}

int curl_socket_cb(CURL *easy, /* easy handle */
                   curl_socket_t s, /* socket */
                   int action, /* see values below */
                   void *userp, /* private callback pointer */
                   void *socketp) /* private socket pointer */
{
  int interest = 0;
  switch (action) {
    case CURL_POLL_NONE:
      break;
    case CURL_POLL_IN:
      interest = EPOLLIN;
      break;
    case CURL_POLL_OUT:
      interest = EPOLLOUT;
      break;
    case CURL_POLL_INOUT:
      interest = EPOLLIN | EPOLLOUT;
      break;
    case CURL_POLL_REMOVE:
      // XXX TODO: free event structure
      epoll_ctl(epfd, EPOLL_CTL_DEL, s, NULL);
      return 0;
    default:
      printf("Huh? curl_socket_cb() got %d\n", action);
      return 0;
  }
 if (socketp == NULL) {
    // alloc new event struct
    struct epoll_event *ev;
    ev = (struct epoll_event *)calloc(sizeof(struct epoll_event), 1);
    // store its address in CURLs socketp for this socket
    curl_multi_assign(multi_handle, s, ev);
    ev->events = interest;
    ev->data.fd = s;
    epoll_ctl(epfd, EPOLL_CTL_ADD, s, ev);
  }
  else {
    // change event mask
    ((struct epoll_event *)socketp)->events = interest;
    epoll_ctl(epfd, EPOLL_CTL_MOD, s, (struct epoll_event *)socketp);
  }
  return 0;
}

CURL * init_easy_handle(int num) {
  CURL * ret = curl_easy_init();
  curl_easy_setopt(ret, CURLOPT_WRITEFUNCTION, client_write_data);
  curl_easy_setopt(ret, CURLOPT_WRITEDATA, (void *)num);
  curl_easy_setopt(ret, CURLOPT_MAXCONNECTS, 1);
  curl_easy_setopt(ret, CURLOPT_SSL_VERIFYPEER, 0);
  curl_easy_setopt(ret, CURLOPT_COOKIEFILE, "");
  curl_easy_setopt(ret, CURLOPT_URL, url);
  return ret;
}

main(int ac, char **av) {
  int i, ret, ev_mask, rc, running_handles, interest;
  struct epoll_event *events;
  if (ac < 3 || ((clients = atoi(av[1])) == 0)) {
    printf("Usage: %s <clients> <url>\n", av[0]);
    exit(1);
  }
  url = av[2];
  curl_global_init(CURL_GLOBAL_ALL);
  epfd = epoll_create(clients);
  if (epfd < 0) {
    printf("epoll_create() fail\n");
    exit(1);
  }
  clients_active = 0;
  events = (struct epoll_event *)
    calloc(sizeof(struct epoll_event), clients);
  easy_handles = (CURL **)
    calloc(sizeof(CURL *), clients);
  multi_handle = curl_multi_init();
  curl_multi_setopt(multi_handle, CURLMOPT_SOCKETFUNCTION, curl_socket_cb);
  for (i = 0; i < clients; i++) {
    easy_handles[i] = init_easy_handle(i);
    curl_multi_add_handle(multi_handle, easy_handles[i]);
  }
  do {
    rc = curl_multi_socket_all(multi_handle, &running_handles);
  } while (rc == CURLM_CALL_MULTI_PERFORM);
  while (running_handles > 0) {
    ret = epoll_wait(epfd, events, clients, 500);
    if (ret > 0) {
      for (i = 0; i < ret; i++) {
        interest = 0;
        if (events[i].events & EPOLLIN) {
          interest |= CURL_CSELECT_IN;
        }
        if (events[i].events & EPOLLOUT)
          interest |= CURL_CSELECT_OUT;
        do {
          rc = curl_multi_socket_action(multi_handle, events[i].data.fd,
interest, &running_handles);
        } while (rc == CURLM_CALL_MULTI_PERFORM);
      }
    }
  }
  printf("Read a total of %u bytes\n", total_bytes);

}
Received on 2008-10-21