Using fd set in the select method

I don't understand how to use the fd_set structure together with select and non-blocking sockets to create a tcp server.Below is a sample framework for how this all interacts. I do not understand how the select works, what are the "rules" for working with it, what does the structure with fd_set have to do with it and what it is needed for. Why in the example there are two such structures that decide when to receive information from the client, and when to only send it.

int ls;    // Сокет, прослушивающий соединения
int cs[N]; // Сокеты с подключенными клиентами
fd_set rfd;
fd_set wfd;
int nfds = ls;
int i;
struct timeval tv = {1, 0};
while (1) {
  FD_ZERO(&rfd);
  FD_ZERO(&wfd);
  FD_SET(ls, &rfd);
  for (i = 0; i < N; i++) {
    FD_SET(cs[i], &rfd);
    FD_SET(cs[i], &wfd);
    if (nfds < cs[i])
      nfds = cs[i];
  }
  if (select(nfds + 1, &rfd, &wfd, 0, &tv) > 0) {
    // Есть события
    if (FD_ISSET(ls, &rfd)) {
      // Есть события на прослушивающем сокете, можно вызвать accept,
      принять
      // подключение и добавить сокет подключившегося клиента в массив cs
    }
    for (i = 0; i < N; i++) {
      if (FD_ISSET(cs[i], &rfd)) {
        // Сокет cs[i] доступен для чтения. Функция recv вернет данные,
        recvfrom - дейтаграмму
      }
      if (FD_ISSET(cs[i], &wfd)) {
        // Сокет cs[i] доступен для записи. Функция send и sendto будет
        успешно завершена
      }
    }
  } else {
    // Произошел таймаут или ошибка
  }
}
Author: isnullxbh, 2019-04-17

1 answers

I do not understand how the select works, what are the "rules" for working with it, what does the structure with fd_set have to do with it and what it is needed for.

This function (select ) allows a process to tell the kernel that it needs to wait for certain events to occur, and to bring the process out of the waiting state only after one of these events occurs or a specified amount of time has passed.

Function signature:

int select(int nfds
    , fd_set *readfds      // Дескрипторы, готовые для чтения
    , fd_set *writefds     // Дескрипторы, готовые для записи
    , fd_set *exceptfds    // Дескрипторы, требующие обработки исключения
    , struct timeval *timeout);

Why are there two in the example? such structures that decide when to accept information from the client, and when to only send it.

Arguments readfds, writefds and exceptfds define a list of descriptors that the kernel should check for readability, writeability, and exceptions, respectively. A list of descriptors is usually an array of integers, and each such number is treated as a bitset, that is, in an array of 4 32-we will be able to store the following bit numbers information:

fds[0]; // биты соответствуют дескрипторам от 0  до 31
fds[1]; // биты соответствуют дескрипторам от 31 до 63
fds[2]; // биты соответствуют дескрипторам от 64 до 95
fds[3]; // биты соответствуют дескрипторам от 96 до 127

The implementation of such a set is the data type fd_set. You don't need to understand how it is implemented, because the following macros are provided for working with it:

void FD_CLR(int fd, fd_set *set);
int  FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

We place a set of descriptors in memory and use the above macros to work with it.

During execution, the select function changes the values that are in the descriptor sets. Before calling select, we specify the descriptors that we are interested in, after it is executed we get a set of descriptors that are ready. You can check the descriptor using the FD_ISSET macro - if the descriptor is not ready, then the corresponding bit will be reset.

Set the bits you are interested in (read as the descriptors you are interested in) each time you call the select function.

The socket is read-ready under the following conditions:

  1. Number of bytes of data in the socket receiving buffer >= minimum amount of data (LWM)
  2. Connection closes (FIN segment received);
  3. An error occurs.

The socket is ready for writing under the following conditions:

  1. Number of bytes of data in the socket send buffer >= minimum amount of data (see SO_SNDLOWAT);
  2. Connection closed (get SIGPIPE);
  3. An error occurs.

Select and non-blocking sockets

You will need to change the processing logic for example, select will tell us that the handle is ready for reading, we start reading, and the read function returns an error EWOULDBLOCK - you don't need to handle it (I mean, take drastic measures).

 3
Author: isnullxbh, 2019-04-18 04:29:04