Unix - статьи



         

Сетевые сокеты - часть 4


Программа-клиент открывает новый сокет с помощью вызова функции socket() аналогично тому, как это делает сервер (дескриптор сокета, который возвращает socket() мы сохраним в переменной sock), и вызывает функцию connect(2) для установки соединения:

if (connect(sock, &serv_addr, sizeof(serv_addr)) < 0) { printf("connect() failed: %d", errno); return EXIT_FAILURE; }

Теперь сокет готов к передаче и приему данных. Программа-клиент считывает символы, вводимые пользователем в окне терминала. Когда пользователь нажимает <Ввод>, программа передает данные серверу, ждет ответного сообщения сервера и распечатывает его.

На протяжении этой статьи мы несколько раз упоминали не-блокирующие сокеты. Остановимся на них немного подробнее. О не-блокирующих сокетах вам нужно знать, прежде всего, то, что ими можно не пользоваться. Благодаря многопоточному (многопрограммному) программированию мы можем использовать блокирующие сокеты во всех ситуациях (и тогда, когда нам нужно обрабатывать несколько сокетов одновременно, и тогда, когда нам требуется возможность прервать операцию, выполняемую над сокетом). Рассмотрим, тем не менее, две функции, необходимые для работы с не-блокирующими сокетами. По умолчанию функция socket() создает блокирующий сокет. Чтобы сделать его не- блокирующим, мы используем функцию fcntl(2):

sock = socket(PF_INET, SOCK_STREAM, 0); fcntl(sock, F_SETFL, O_NONBLOCK);

Теперь любой вызов функции read() для сокета sock будет возвращать управление сразу же. Если на входе сокета нет данных для чтения, функция read() вернет значение EAGAIN. Для поверки состояния не-блокирующих сокетов можно воспользоваться функцией select(2). Функция select() способна проверять состояние нескольких дескрипторов сокетов (или файлов) сразу. Первый параметр функции – количество проверяемых дескрипторов. Второй, третий и четвертый параметры функции представляют собой наборы дескрипторов, которые следует проверять, соответственно, на готовность к чтению, записи и на наличие исключительных ситуаций. Сама функция select() – блокирующая, она возвращает управление, если хотя бы один из проверяемых сокетов готов к выполнению соответствующей операции. В качестве последнего параметра функции select() можно указать интервал времени, по прошествии которого она вернет управление в любом случае. Вызов select() для проверки наличия входящих данных на сокете sock может выглядеть так:

fd_set set; struct timeval interval; FD_SET(sock, &set); tv.tv_sec = 1; tv.tv_usec = 500000; ... select(1, &set, NULL, NULL, &tv); if (FD_ISSET(sock, &set) { // Есть данные для чтения }

Все, что касается функции select() теперь объявляется в заголовочном файле <sys/select.h> (раньше объявления элементов функции select() были разбросаны по файлам <sys/types.h>, <sys/time.h> и <stdlib.h>). В приведенном фрагменте кода FD_SET и FD_ISSET – макросы, предназначенные для работы с набором дескрипторов fd_set.

На этом мы закончим знакомство с увлекательным миром межпроцессного взаимодействия Linux. Следующая статья будет посвящена управлению процессами, сигналам и потокам.




Содержание  Назад  Вперед