Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учеб.пос.СП.doc
Скачиваний:
28
Добавлен:
31.03.2015
Размер:
1.33 Mб
Скачать

Контрольные вопросы

  1. Охарактеризуйте достоинства и недостатки семафоров.

  2. Охарактеризуйте достоинства и недостатки общей памяти.

  3. Охарактеризуйте достоинства и недостатки очередей сообщений.

  4. Охарактеризуйте достоинства и недостатки именованных каналов.

  5. Назовите основные отличия именованных каналов от неименованных.

Литература

  1. Глас Г., Эйблс К. Unix для программистов и пользователей. / Г. Глас, К. Эйблс – СПб.: БХВ-Петербург, 2004. – 848 с.: ил.

  2. Брюс М. Unix/Linux: Теория и практика программирования. / М.Брюс – Издательство: Кудиц-Образ, 2004. -576 с.

  3. Стивенс У.Р. UNIX. Профессиональное программирование. / У.Р. Стивенс. – Издательство: Символ-Плюс, 2007. - 1040 с.

Глава 7.Сетевое взаимодействие и сокеты

Основной способ организации обмена информацией между компьютерами, объединенными в сеть, - сокеты. Для работы с ними существует восемь основных системных вызовов, пять из которых предназначены исключительно для сокетов: socket, bind, listen, accept, connect, read, write и close. Учитывая всю сложность сетевых взаимодействий и многообразие протоколов связи, существует еще около 60 системных вызовов, имеющих отношение к сокетам.

    1. Сокеты

Процесс может открыть именованный канал таким образом: fd = open(“MyFifo”, O_RDONLY);

Этот системный вызов делает следующее:

  1. Создает объект для операций ввода-вывода и назначает ему файловый дескриптор.

  2. Связывает файловый дескриптор с внешним именем MyFifo.

  3. Ожидает, пока кто-нибудь, не откроет канал на запись.

  4. Возвращает файловый дескриптор, который затем может быть использован в системном вызове read.

Аналогичным образом работают и сокеты, только каждый шаг выделен в отдельный системный вызов:

  1. Socket - создает объект (сокет) и назначает ему файловый дескриптор.

  2. Bind – устанавливает адрес (имя) сокета, чтобы другой процесс мог сослаться на него.

  3. Listenпомечает сокет как предназначенный для приема запросов на соединение от других сокетов.

  4. Accept – блокируется в ожидании запроса на соединение.

  5. Connect – устанавливает соединение с сокетом, который был заблокирован на accept.

Последовательность создания и инициализации сокетов на стороне сервера отличается от последовательности действий на стороне клиента.

На стороне сервера:

  • вызывается socket для создания объекта и связанного с ним файлового дескриптора;

  • вызывается bind, для назначения сокету адреса;

  • вызывается listen, чтобы пометить сокет, как предназначенный для приема запросов на соединение;

  • вызывается accept, чтобы дождаться и принять входящее сообщение. После этого вызов accept создает второй сокет с новым файловым дескриптором;

  • в операциях ввода-вывода участвует вновь созданный файловый дескриптор.

На стороне клиента:

  • вызывается socket для создания объекта и связанного с ним файлового дескриптора;

  • для установления соединения с сервером вызывается connect, которому в качестве аргумента передается имя сервера;

  • файловый дескриптор сокета используется для выполнения операций ввода-вывода.

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

#define SOCKETNAME “MySocket”

int main(void)

{

struct sockaddr_un sa;

(void)unlink(SOCKETNAME);

strcpy(sa.sun_path, SOCKETNAME);

sa.sun_family = AF_UNIX;

if (fork() == 0) { /*потомок-клиент */

int fd_skt;

char buf[100];

fd_skt = socket(AF_UNIX, SOCK_STREAM, 0);

while (connect(fd_skt, (struct sockaddr *)&sa, sizeof(sa)) == -1)

if (errno == ENOENT) {

sleep(1);

continue;

}

else

EC_FAIL

write(fd_skt, “Привет!”, 7);

read(fd_skt, buf, sizeof(buf));

printf(“Клиент получил сообщение” \”%s\”\n”, buf);

close(fd_skt);

exit(EXIT_SUCCESS);

}

else { /*предок-сервер */

int fd_skt, fd_client;

char buf[100];

fd_skt = socket(AF_UNIX, SOCK_STREAM, 0);

bind(fd_skt, (struct sockaddr *)&sa, sizeof(sa));

listen(fd_skt,SOMAXCONN);

fd_client = accept(fd_skt, NULL, 0);

read(fd_client, buf, sizeof(buf));

printf(“Сервер получил сообщение” \”%s\”\n”, buf);

write(fd_client, “Пока!”, 9);

close(fd_skt);

close(fd_client);

exit(EXIT_SUCCESS);

}

EC_CLEANUP_BGN

exit(EXIT_FAILURE);

EC_CLEANUP_END

}

Первое, что мы видим, - адрес (имя), которое передается системным вызовам bind и connect. Он представляет собой не просто строку, а целую структуру, которая хранит строку в поле sun_path, и домен адресов – в поле sun_family. Домен AF_UNIX означает локальное взаимодействие ограниченное рамками одной системы. Системный вызов bind не может повторно использовать существующие имена для сокетов типа AF_UNIX, поэтому производится удаление имени, чтобы гарантировать его отсутствие. Сервер (родительский процесс) выполняет последовательность из шести шагов. Клиент (дочерний процесс) также следует заданной последовательности действий, но с одним небольшим отступлением: если обращение к connect на стороне клиента произойдет раньше, чем сервер обратится к bind, вызов connect потерпит неудачу, поскольку ему не с кем будет устанавливать соединение. Поэтому в случае получения ошибки клиент ненадолго засыпает и повторяет попытку.

Когда вызов accept возвращает управление, сервер выполняет операции ввода-вывода над вновь созданным (вторичным) файловым дескриптором. Клиент продолжает использовать свой дескриптор. Системные вызовы accept и connect могут быть заблокированы. Остальные возвращают управление в вызывающую программу немедленно, как только выполнят работу. Соединение может существовать сколь угодно долго, пока одна из сторон не разорвет его, и по своей сути напоминает двунаправленный неименованный канал. Файл сокета действительно является файлом, что видно в выводе команды ls:

$ ls -l MySocket

srwxr-xr-x 1 marc sysadmin 0 Apr 4 10:05 MySocket

В результате работы программы получилось следующее:

Сервер получил сообщение “Привет!”

Клиент получил сообщение “Пока!”