-1

I am writing a little web server which involves epoll and multithread. For small and short http/1.1 requests and responses, it works as expected. But when working with large size file downloads, it is always interrupted by the timer I devised. I expire the timers with a fixed timeout value, but I also have a if statement to check if the response was sent successfully.

static void
_expire_timers(list_t *timers, long timeout)
{
  httpconn_t *conn;
  int sockfd;

  node_t *timer;
  long cur_time;
  long stamp;

  timer = list_first(timers);
  if (timer) {
    cur_time = mstime();
    do {
      stamp = list_node_stamp(timer);

      conn = (httpconn_t *)list_node_data(timer);
      if ((cur_time - stamp >= timeout) && httpconn_close(conn)) {
        sockfd = httpconn_sockfd(conn);
        DEBSI("[CONN] socket closed, server disconnected", sockfd);
        close(sockfd);

        list_del(timers, stamp);
      }

      timer = list_next(timers);
    } while (timer);
  }
}

I realized that in a non-blocking environment, the write() function might be interrupted during the request-response communication. I wonder how long write() can hold or how much data write() can send, so I can tweek the timout setting in my code.

This is the code which involves write(),

void
http_rep_get(int clifd, void *cache, char *path, void *req)
{
  httpmsg_t *rep;
  int len_msg;
  char *bytes;

  rep = _get_rep_msg((list_t *)cache, path, req);
  bytes = msg_create_rep(rep, &len_msg);

  /* send msg */
  DEBSI("[REP] Sending reply msg...", clifd);
  write(clifd, bytes, len_msg);
  /* send body */
  DEBSI("[REP] Sending body...", clifd);
  write(clifd, msg_body_start(rep), msg_body_len(rep));

  free(bytes);
  msg_destroy(rep, 0);
}

And the following is the epoll loop I use to process the incoming requests,

  do {
    nevents = epoll_wait(epfd, events, MAXEVENTS, HTTP_KEEPALIVE_TIME);
    if (nevents == -1) perror("epoll_wait()");

    /* expire the timers */
    _expire_timers(timers, HTTP_KEEPALIVE_TIME);

    /* loop through events */
    for (i = 0; i < nevents; i++) {
      conn = (httpconn_t *)events[i].data.ptr;
      sockfd = httpconn_sockfd(conn);

      /* error case */
      if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) ||
          (!(events[i].events & EPOLLIN))) {
        perror("EPOLL ERR|HUP");
        list_update(timers, conn, mstime());
        break;
      }

      else if (sockfd == srvfd) {
        _receive_conn(srvfd, epfd, cache, timers);
      }

      else {
        /* client socket; read client data and process it */
        thpool_add_task(taskpool, httpconn_task, conn);
      }
    }
  } while (svc_running);

The http_rep_get() is executed by the threadpool handler httpconn_task(), HTTP_KEEPALIVE_TIME is the fixed timeout. The handler httpconn_task() will add a timer to the timers once a request arrives. Since the write() is executed in http_rep_get(), I think it might be interrupted by the timers. I guess I can change the way to write to the clients, but I need to make sure how much the write() can do.

If you are interested, you may browser my project to help me with this. https://github.com/grassroot72/Maestro

Cheers, Edward

  • 1
    Please provide a [minimal verifiable example](https://stackoverflow.com/help/minimal-reproducible-example) that illustrates the problem. As it is, there is no `write` call shown in your code and it is not clear how the function shown relates to what you are asking about. – kaylum Sep 12 '20 at 05:17
  • That is not a minimal verifiable example. So it's still not clear what you are asking - what does the write have to do with the timeout? There is nothing in your code that links the two code snippets so it's not clear at all what you are talking about. Please review: [How to ask](https://stackoverflow.com/help/how-to-ask) – kaylum Sep 12 '20 at 05:41
  • sorry for put an ambiguous question, I will try my best to make it clear. – Grassroot72 Sep 12 '20 at 06:13
  • 1
    `write` can always write less than requested, so you need to place in a loop. (Same goes for `read`.) – ikegami Sep 12 '20 at 08:30
  • ..and the same applies to read/recv, assuming TCP. You MUST correctly and completely handle the results returned from such system calls. Also, sending 'ping-pong' replies makes your protocol subject to large latency delays:( – Martin James Sep 12 '20 at 08:36
  • It's true that 'ping-pong' replies cause latency delays, thanks @Martin James for mentioning that :) – Grassroot72 Sep 12 '20 at 14:20

1 Answers1

0

Is there a size limit of write() for a socket fd?

It depends on what you mean by a limit.

As the comments explain, a write call may write fewer bytes than you ask it to. Furthermore, this is expected behavior if you perform a large write to a socket. However, there is no reliable way to determine (or predict) how many bytes will be written before you call write.

The correct way to deal with this is to check how many bytes were actually written each time, and use a loop for ensure that all bytes are written (or until you get a failure).

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Yes, the write() is unpredictable as Stephen C and @ikegami mentioned. I put write() in a loop and the problem is solved, thank you Stephen C, ikegami. – Grassroot72 Sep 12 '20 at 14:17