C:套接字:无法读取整个服务器响应

C: sockets: can't read the whole server response

我正在用 C 编写一个 IRC 聊天客户端。除了我无法阅读服务器发送的完整答案外,一切都运行良好。这是代码:

char buffer[2048];
write_on_screen(current_page(), "LOG COMMAND", command);
write(sockfd, command, strlen(command)); //write to socket
bzero(buffer, sizeof(buffer));
read(sockfd, buffer, sizeof(buffer));
write_on_screen(current_page(), "RESPONSE", buffer);
return buffer;

大部分时间缓冲区将只包含响应的一部分(小于 2048 字节),而其他时候它什么也不包含。在这两种情况下,如果我在第一个之后再做一个 read(),它 returns 我剩下的答案或另一个小部分(然后我必须再次做另一个 read())。如果我在 write()read() 之间放置一个 sleep(1) 我会得到完整的答案,但我确信这不是一个好的做法。 有什么办法可以避免这种情况吗?

提前致谢

根据RFC-1459,IRC 中的单行文本最多可包含 512 个字符,并以 CRLF (\r\n) 对结束。然而:

  • 您不能保证每次都能准确 512 字节。例如,您可能会收到来自某人的相对较短的消息频道中的其他人:Hi!
  • 与上述相关:一组 512 字节可能表示不止一条消息。例如,缓冲区可能包含整行,加上 下一个行:PRIVMSG <msgtarget> <message>\r\nPRIVMS

鉴于您的 [=14] 中可能有零个或多个 complete 行加上零个或一个 incomplete 行=] 在任何时候,您都可以尝试按照以下方式做一些事情:

char buffer[2048];

while(keep_going)
{

  char **lines;
  int i, num_lines;

  // Receive data from the internet.
  receiveData(buffer);

  // Create an array of all COMPLETE lines in the buffer (split on \r\n).
  lines = getCompleteLines(buffer, &num_lines);
  removeCompleteLinesFromBuffer(buffer);

  // Handle each COMPLETE line in the array.
  for (i = 0; i < num_lines; ++i) { handle_line(lines[i]); }
  freeLines(lines);

}

这将允许您一次处理零个或多个 complete 行,任何 incomplete 行(即最后一个之后的任何内容) \r\n 对) 一直保留到 下一个 调用 receiveData().

您犯了常见的错误。如果不将 read()recv() 的结果存储到变量中,就不可能编写正确的网络代码。你必须:

  1. 检查它是否为 -1,如果是,则查看 errno 是否是致命的,除了 EAGAIN/EWOULDBLOCK,它几乎总是致命的,如果是致命的,则关闭套接字并放弃进程.
  2. 检查它是否为零,这意味着对等方已断开连接。同样,您必须关闭套接字并放弃进程。
  3. 将其用作实际接收到的字节数。这些函数没有义务也不保证填充缓冲区。它们在阻塞模式下的约定是它们阻塞直到出现错误、流结束或至少传输一个字节。如果你期望超过一个字节,你通常必须循环直到你得到它。

您需要循环 read() 直到检测到 CRLF。

一种可能的方法是:

#include <stdio.h>
#include <unistd.h>
#include <errno.h>

ssize_t read_until_crlf(int sd, char * p, size_t s, int break_on_interupt)
{
  ssize_t bytes_read = 0;
  ssize_t result = 0;
  int read_cr = 0;
  int read_crlf = 0;

  while (bytes_read < s)
  {
    result = read(sd, p + bytes_read, 1);
    if (-1 == result)
    {
      if ((EAGAIN == errno) || (EWOULDBLOCK == errno))
      {
        continue;
      }
      else if (EINTR == errno)
      {
        if (break_on_interupt)
        {
          break;
        }

        continue;
      }
      else
      {
        perror("read() failed");
        break;
      }
    }
    else if (0 == result)
    {
      break; /* peer disconnected */
    }

    if ('\r' == p[bytes_read])
    {
      read_cr = 1;
    }
    else if (('\n' == p[bytes_read]) && read_cr)
    {
      read_crlf = 1;
      break; /* CRLF detected */
    }
    else
    {
      read_cr = 0;
    }

    ++bytes_read;
  }

  if (!read_crlf)
  {
    result = -1; /* Buffer full without having read a CRLF. */
    errno = ENOSPC; /* ... or whatever might suite. */
  }

  return (0 >= result) ?result :bytes_read;
}

这样称呼它:

#include <stdio.h>

ssize_t read_until_crlf(int sd, char * p, size_t s, int break_on_interupt);

int main(void)
{
  int sd = -1;

  /* init sd here */

  {
    char line[2048] = "";
    ssize_t result = read_until_crlf(sd, line, sizeof line, 0);
    if (-1 == result)
    {
      perror("read_until_newline() failed");
    }

    printf("read '%s'\n", line);
  }

  return 0;
}