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

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

21 голосов
/ 08 сентября 2008

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

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

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

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

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

Ответы [ 4 ]

17 голосов
/ 08 сентября 2008

Короткий ответ: вы должны выполнять всю тяжелую работу самостоятельно. Вы можете быть уведомлены о том, что есть данные, доступные для чтения, но вы не будете знать, сколько доступно байтов. В большинстве протоколов 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;
}
13 голосов
/ 08 сентября 2008

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

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

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

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

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

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

Что касается программирования * сокетов NIX, я настоятельно рекомендую У. Ричарда Стивенса «Сетевое программирование Unix, том 1» (UNPv1) и «Расширенное программирование в среде Unix» (APUE). Первая книга посвящена сетевому программированию, независимо от транспорта, а вторая является хорошей книгой по всестороннему программированию, поскольку она относится к программированию на * NIX. Также обратите внимание на «Иллюстрированный TCP / IP», тома 1 и 2.

3 голосов
/ 08 сентября 2008

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

1 голос
/ 08 сентября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...