【问题标题】:Is there a size limit of write() for a socket fd?套接字 fd 的 write() 是否有大小限制?
【发布时间】:2020-09-12 22:30:35
【问题描述】:

我正在编写一个涉及 epoll 和多线程的小型 Web 服务器。对于小而短的 http/1.1 请求和响应,它按预期工作。但是在处理大文件下载时,它总是被我设计的计时器打断。我使用固定的超时值使计时器过期,但我还有一个 if 语句来检查响应是否发送成功。

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);
  }
}

我意识到在非阻塞环境中,write() 函数可能会在请求-响应通信期间被中断。我想知道 write() 可以保存多长时间或 write() 可以发送多少数据,所以我可以在我的代码中调整 timout 设置。

这是涉及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);
}

以下是我用来处理传入请求的epoll循环,

  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);

http_rep_get() 由线程池处理程序 httpconn_task() 执行,HTTP_KEEPALIVE_TIME 是固定超时。一旦请求到达,处理程序 httpconn_task() 将向计时器添加一个计时器。由于 write() 是在 http_rep_get() 中执行的,我认为它可能会被计时器中断。我想我可以改变写给客户端的方式,但我需要确定 write() 能做多少。

如果你有兴趣,你可以浏览我的项目来帮助我。 https://github.com/grassroot72/Maestro

干杯, 爱德华

【问题讨论】:

  • 请提供说明问题的minimal verifiable example。实际上,您的代码中没有显示write 调用,并且不清楚显示的函数与您所询问的内容有何关系。
  • 这不是一个最小的可验证示例。所以仍然不清楚你在问什么 - 写入与超时有什么关系?您的代码中没有任何内容可以链接两个代码 sn-ps,因此您根本不清楚您在说什么。请查看:How to ask
  • 抱歉提出了一个模棱两可的问题,我会尽力说清楚。
  • write 总是可以写得比请求的少,所以你需要放在一个循环中。 (read 也是如此。)
  • ..同样适用于读取/接收,假设 TCP。您必须正确且完整地处理从此类系统调用返回的结果。此外,发送“乒乓”回复会使您的协议受到很大的延迟延迟:(

标签: c multithreading webserver keep-alive epoll


【解决方案1】:

socket fd 的 write() 有大小限制吗?

这取决于您所说的限制。

正如 cmets 所解释的,write 调用写入的字节数可能比您要求的要少。此外,如果您对套接字执行大量写入,这是预期的行为。但是,在您调用 write 之前,没有可靠的方法来确定(或预测)将写入多少字节。

处理这个问题的正确方法是检查每次实际写入了多少字节,并使用循环来确保所有字节都被写入(或直到你失败)。

【讨论】:

  • 是的,正如 Stephen C 和 @ikegami 提到的,write() 是不可预测的。我把 write() 放在一个循环中,问题就解决了,谢谢 Stephen C,ikegami。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-10-23
  • 2018-06-24
  • 1970-01-01
  • 2020-09-04
  • 2013-12-13
  • 2016-11-15
  • 1970-01-01
相关资源
最近更新 更多