C: Using a select call, when I am reading, how do I keep track of the data?

本小妞迷上赌 提交于 2020-01-15 10:04:36

问题


First of all, I've never worked with C before (mostly Java which is the reason you'll find me write some naive C code). I am writing a simple command interpreter in C. I have something like this:

//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
}

Now if I'm looking at something like a long paragraph of commands, it is obvious that a 256 byte buffer will not be able to get the entire command. For the time being, I'm using a 2056 byte buffer to get the entire command. But if I want to use the 256 byte buffer, how would I go about doing this? Do I keep track of which client gave me what data and append it to some buffer? I mean, use something like two dimensional arrays and such?


回答1:


Yes, the usual approach is to have a buffer of "data I've received but not processed" for each client, large enough to hold the biggest protocol message.

You read into that buffer (always keeping track of how much data is currently in the buffer), and after each read, check to see if you have a complete message (or message(s), since you might get two at once!). If you do, you process the message, remove it from the buffer and shift any remaining data up to the start of the buffer.

Something roughly along the lines of:

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 */
}

By the way, you should check for errno == EINTR if select() returns -1, and just loop around again - that's not a fatal error.




回答2:


I would keep a structure around for each client. Each structure contains a pointer to a buffer where the command is read in. Maybe you free the buffers when they're not used, or maybe you keep them around. The structure could also contain the client's fd in it as well. Then you just need one array (or list) of clients which you loop over.

The other reason you'd want to do this, besides the fact that 256 bytes might not be enough, is that recv doesn't always fill the buffer. Some of the data might still in transit over the network.

If you keep around buffers for each client, however, you can run into the "slowloris" attack, where a single client keeps sending little bits of data and takes up all your memory.




回答3:


It can be a serious pain when you get tons of data like that over a network. There is a constant trade between allocating a huge array or multiple reads with data moves. You should consider getting a ready made linked list of buffers, then traverse the linked list as you read the buffers in each node of the linked list. That way it scales gracefully and you can quickly delete what you've processed. I think that's the best approach and it's also how boost asio implements buffered reads.




回答4:


If you're dealing with multiple clients a common approach to to fork/exec for each connection. Your server would listen for incoming connections, and when one is made it would fork and and exec a child version of itself that would then handle the "command interpreter" portion of the problem.

This way you're letting the OS manage the client processes--that is, you don't have to have a data structure in your program to manage them. You will still need to clean up child processes in your server as they terminate.

As for managing the buffer...How much data do you expect before you post a response? You may need to be prepared to dynamically adjust the size of your buffer.



来源:https://stackoverflow.com/questions/2159628/c-using-a-select-call-when-i-am-reading-how-do-i-keep-track-of-the-data

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!