UNIX: разработка сетевых приложений - страница 10

Шрифт
Интервал

стр.


>16  Listen(listenfd, LISTENQ);


>17  for (;;) {

>18   connfd = Accept(listenfd, (SA*)NULL, NULL);


>19   ticks = time(NULL);

>20   snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));

>21   Write(connfd. buff, strlen(buff));


>22   Close(connfd);

>23  }

>24 }

Создание сокета TCP

>10 Создание сокета TCP выполняется так же, как и в клиентском коде.

Связывание заранее известного порта сервера с сокетом

>11-15 Заранее известный порт сервера (13 в случае сервера времени и даты) связывается с сокетом путем заполнения структуры адреса интернет-сокета и вызова функции >bind. Мы задаем IP-адрес как >INADDR_ANY, что позволяет серверу принимать соединение клиента на любом интерфейсе в том случае, если узел сервера имеет несколько интерфейсов. Далее мы рассмотрим, как можно ограничить прием соединений одним-единственным интерфейсом.

Преобразование сокета в прослушиваемый сокет

>16 С помощью вызова функции >listen сокет преобразуется в прослушиваемый, то есть такой, на котором ядро принимает входящие соединения от клиентов. Эти три этапа, >socket, >bind и >listen, обычны для любого сервера TCP при создании того, что мы называем прослушиваемым дескриптором (listening descriptor) (в нашем примере это переменная >listenfd).

Константа >LISTENQ взята из нашего заголовочного файла >unp.h. Она задает максимальное количество клиентских соединений, которые ядро ставит в очередь на прослушиваемом сокете. Более подробно мы расскажем о таких очередях в разделе 4.5.

Прием клиентского соединения, отправка ответа

>17-21 Обычно процесс сервера блокируется при вызове функции >accept, ожидая принятия подключения клиента. Для установки TCP-соединения используется трехэтапное рукопожатие (three-way handshake). Когда рукопожатие состоялось, функция accept возвращает значение, и это значение является новым дескриптором (>connfd), который называется присоединенным дескриптором (connected descriptor). Этот новый дескриптор используется для связи с новым клиентом. Новый дескриптор возвращается функцией >accept для каждого клиента, соединяющегося с нашим сервером.

ПРИМЕЧАНИЕ

Стиль, используемый в книге для обозначения бесконечного цикла, выглядит так:

>for (;;) {

> ...

>}

Библиотечная функция >time возвращает количество секунд с начала эпохи Unix: 00:00:00 1 января 1970 года UTC (Universal Time Coordinated — универсальное синхронизированное время, среднее время по Гринвичу). Следующая библиотечная функция, >ctime, преобразует целочисленное значение секунд в строку следующего формата, удобного для человеческого восприятия:

>Fri Jan 12 14:27:52 1996

Возврат каретки и пустая строка добавляются к строке функцией >snprintf, а результат передается клиенту функцией >write.

ПРИМЕЧАНИЕ

Если вы еще не выработали у себя привычку пользоваться функцией snprintf вместо устаревшей sprintf, сейчас самое время заняться этим. Функция sprintf не в состоянии обеспечить проверку переполнения буфера получателя. Функция snprintf, наоборот, требует, чтобы в качестве второго аргумента указывался размер буфера получателя, переполнение которого таким образом предотвращается.

Функция snprintf была добавлена в стандарт ANSI С относительно нравно, в версии ISO C99. Практически все поставщики программного обеспечения уже сейчас включают эту функцию в стандартную библиотеку языка С. Существуют и свободно распространяемые реализации. В нашей книге мы используем функцию snprintf и рекомендуем вам пользоваться ею в своих программах для повышения их надежности.

Удивительно много сетевых атак было реализовано хакерами с использованием незащищенности sprintf от переполнения буфера. Есть еще несколько функций, с которыми нужно быть аккуратными: gets, strcat и strcpy. Вместо них лучше использовать fgets, strncat и strncpy. Еще лучше работают более современные функции strlcat и strlcpy, возвращающие в качестве результата правильно завершенную строку. Полезные советы, касающиеся написания надежных сетевых программ, можно найти в главе 23 книги [32].

Завершение соединения

>22 Сервер закрывает соединение с клиентом, вызывая функцию >close. Это инициирует обычную последовательность прерывания соединения TCP: пакет FIN посылается в обоих направлениях, и каждый пакет FIN распознается на другом конце соединения. Более подробно трехэтапное рукопожатие и четыре пакета TCP, используемые для прерывания соединения, будут описаны в разделе 2.6.


стр.

Похожие книги