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