C:使用选择呼叫,在我阅读时,如何跟踪数据?

时间:2010-01-29 03:04:48

标签: c networking select buffer

首先,我以前从未使用过C(主要是Java,这就是你会发现我写一些天真的C代码的原因)。我在C写一个简单的命令解释器。我有这样的东西:

//Initialization code

if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
    perror("Select dead");
    exit(EXIT_FAILURE);
}

....
....
//Loop through connections to see who has the data ready
//If the data is ready
if ((nbytes = recv(i, buf, sizeof(buf), 0)) > 0) {
     //Do something with the message in the buffer
}

现在,如果我正在查看类似于长段命令的内容,很明显256字节缓冲区将无法获得整个命令。目前,我正在使用2056字节缓冲区来获取整个命令。但是,如果我想使用256字节缓冲区,我该怎么做呢?我是否跟踪哪个客户端给了我什么数据并将其附加到某个缓冲区?我的意思是,使用像二维数组这样的东西吗?

4 个答案:

答案 0 :(得分:3)

是的,通常的方法是为每个客户端提供一个“我收到但未处理的数据”的缓冲区,大小足以容纳最大的协议消息。

您读入该缓冲区(始终跟踪缓冲区中当前有多少数据),并在每次读取后检查您是否有完整的消息(或消息,因为您可能得到两个一旦!)。如果这样做,您将处理该消息,将其从缓冲区中删除,并将所有剩余数据移至缓冲区的开头。

大致类似于:

for (i = 0; i < nclients; i++)
{
    if (!FD_ISSET(client[i].fd, &read_fds))
        continue;

    nbytes = recv(client[i].fd, client[i].buf + client[i].bytes, sizeof(client[i].buf) - client[i].bytes, 0);

    if (nbytes > 0)
    {
        client[i].bytes += nbytes;

        while (check_for_message(client[i]))
        {
            size_t message_len;

            message_len = process_message(client[i]);
            client[i].bytes -= message_len;
            memmove(client[i].buf, client[i].buf + message_len, client[i].bytes);
        }
    }
    else
        /* Handle client close or error */
}

顺便说一下,如果errno == EINTR返回-1,你应检查select(),然后再循环 - 这不是致命的错误。

答案 1 :(得分:2)

我会为每个客户保留一个结构。每个结构都包含一个指向读取命令的缓冲区的指针。也许你在没有使用时释放缓冲区,或者你可以保留它们。该结构也可以包含客户端的fd。然后你只需要一个你循环的客户端数组(或列表)。

除了256字节可能不够之外,你想要这样做的另一个原因是recv并不总是填充缓冲区。部分数据可能仍在通过网络传输。

但是,如果为每个客户端保留缓冲区,则可能会遇到“slowloris”攻击,即单个客户端不断发送少量数据并占用所有内存。

答案 2 :(得分:1)

当您通过网络获取大量数据时,这可能会非常严重。在分配大数组或多次读取与数据移动之间存在持续的交易。您应该考虑获取现成的缓冲区链表,然后在读取链表的每个节点中的缓冲区时遍历链表。这样它可以优雅地缩放,您可以快速删除已处理的内容。我认为这是最好的方法,也是增强asio实现缓冲读取的方式。

答案 3 :(得分:1)

如果您正在处理多个客户端,则为每个连接分配fork / exec的常用方法。你的服务器会侦听传入的连接,当一个连接时,它会fork并执行一个子版本,然后处理问题的“命令解释器”部分。

这样您就可以让操作系统管理客户端进程 - 也就是说,您不必在程序中使用数据结构来管理它们。在终止时,您仍需要清理服务器中的子进程。

至于管理缓冲区...在发布响应之前,您期望获得多少数据?您可能需要准备动态调整缓冲区的大小。