Подтвердить что ты не робот

Как работают сокеты в C?

Я немного запутался в программировании сокетов в C.

Вы создаете сокет, привязываете его к интерфейсу и IP-адресу и получаете его для прослушивания. Я нашел пару веб-ресурсов и понял это хорошо. В частности, я нашел статью Сетевое программирование под Unix-системами, чтобы быть очень информативным.

Что меня смущает, так это время поступления данных в сокет.

Как вы можете сказать, когда приходят пакеты, и насколько большой пакет, вам нужно сделать все тяжелое поднятие?

Мое основное предположение заключается в том, что пакеты могут иметь переменную длину, поэтому, как только двоичные данные начинают появляться в сокете, как вы начинаете создавать пакеты из этого?

4b9b3361

Ответ 1

Короткий ответ заключается в том, что вы должны сделать все тяжелое поднятие себя. Вы можете получать уведомления о наличии данных для чтения, но вы не знаете, сколько байтов доступно. В большинстве протоколов IP, использующих пакеты с переменной длиной, будет заголовок с известной фиксированной длиной, добавленной к пакету. Этот заголовок будет содержать длину пакета. Вы читаете заголовок, получаете длину пакета, затем читаете пакет. Вы повторяете этот шаблон (прочитайте заголовок, затем прочитайте пакет), пока связь не будет завершена.

При чтении данных из сокета вы запрашиваете определенное количество байтов. Вызов чтения может блокироваться до тех пор, пока запрошенное количество байтов не будет прочитано, но оно может вернуть меньшее количество байтов, чем запрошено. Когда это произойдет, вы просто повторите чтение, запросив оставшиеся байты.

Здесь типичная функция C для чтения заданного количества байтов из сокета:

/* buffer points to memory block that is bigger than the number of bytes to be read */
/* socket is open socket that is connected to a sender */
/* bytesToRead is the number of bytes expected from the sender */
/* bytesRead is a pointer to a integer variable that will hold the number of bytes */
/*           actually received from the sender. */
/* The function returns either the number of bytes read, */
/*                             0 if the socket was closed by the sender, and */
/*                            -1 if an error occurred while reading from the socket */
int readBytes(int socket, char *buffer, int bytesToRead, int *bytesRead)
{
    *bytesRead = 0;
    while(*bytesRead < bytesToRead)
    {
        int ret = read(socket, buffer + *bytesRead, bytesToRead - *bytesRead);
        if(ret <= 0)
        {
           /* either connection was closed or an error occurred */
           return ret;
        }
        else
        {
           *bytesRead += ret;
        }
    }
    return *bytesRead;
}

Ответ 2

Итак, ответ на ваш вопрос зависит от того, используете ли вы UDP или TCP в качестве транспорта.

Для UDP жизнь становится намного проще, поскольку вы можете вызывать recv/recvfrom/recvmsg с требуемым размером пакета (скорее всего, вы отправите пакеты фиксированной длины из источника), и сделайте предположение, что если имеются данные, которые там представлены в нескольких размерах длины пакета. (I.E. Вы вызываете recv * с размером вашего отправляемого бокового пакета, и вы настроены.)

Для TCP жизнь становится немного более интересной - для целей этого объяснения я предполагаю, что вы уже знаете, как использовать функции socket(), bind(), listen() и accept() - последним является то, как вы получите файловый дескриптор (FD) вашего нового соединения.

Существует два способа ввода-вывода для блокировки сокетов, в которых вы вызываете read (fd, buf, N), и чтение находится там, и ждет, пока вы не прочитаете N байтов в buf - -блока, в котором вы должны проверить (используя select() или poll()), является ли FD доступным для чтения, а THEN - для чтения().

При работе с соединениями на основе TCP ОС не обращает внимания на размеры пакетов, поскольку считается непрерывным потоком данных, а не отдельными частями размера пакета.

Если ваше приложение использует "пакеты" (упакованные или распакованные структуры данных, которые вы просматриваете), вы должны иметь возможность вызвать read() с аргументом правильного размера и прочитать всю структуру данных из сокета в время. Единственное предостережение, с которым вам приходится иметь дело, состоит в том, чтобы помнить о том, что вы правильно заказываете данные, которые вы отправляете, в случае, если исходная и целевая системы имеют разные байтовые байты. Это относится как к UDP, так и к TCP.

Что касается программирования сокетов NIX, я настоятельно рекомендую W. Richard Stevens "Unix Network Programming, том 1" (UNPv1) и "Расширенное программирование в среде Unix" (APUE). Первый - это тома, касающийся сетевого программирования, независимо от транспорта, а последний - хорошая универсальная книга программирования, так как она применима к программированию на основе NIX. Также обратите внимание на "TCP/IP Illustrated", тома 1 и 2.

Ответ 3

Когда вы читаете в сокете, вы рассказываете, сколько максимальных байтов читается, но если у него их не так много, оно дает вам, как многие из них есть. Вам решать разработать протокол, чтобы вы знали, есть ли у вас частичный пакет или нет. Например, в прошлом, когда вы отправляли двоичные данные переменной длины, я бы поставил int в начале, который сказал, сколько байтов ожидать. Я бы сделал чтение с запросом на количество байтов, большее, чем самый большой возможный пакет в моем протоколе, а затем я бы сравнил первый int с каким-то количеством байтов, которые я получил, и либо обработал его, либо попробовал прочитать больше, d получил полный пакет, в зависимости.

Ответ 4

Сокеты работают на более высоком уровне, чем сырые пакеты - это как файл, с которого вы можете читать/писать. Кроме того, когда вы пытаетесь прочитать из сокета, операционная система блокирует (переносит на удержание) ваш процесс, пока он не будет иметь данные для выполнения запроса.