Операционная система UNIX - страница 123
>listen(sockfd, ...);
Организовать очередь запросов
>for(;;) {
> newsockfd = accept(sockfd, ...);
Получить запрос
> if (fork() == 0) {
Породить дочерний процесс
> close(sockfd);
Дочерний процесс
> ...
> exit(0);
> } else
> close(newsockfd);
Родительский процесс
>}
В этом сценарии, в то время как дочерний процесс обеспечивает фактический обмен данными с клиентом, родительский процесс продолжает "прослушивать" поступающие запросы, порождая для каждого из них отдельный процесс-обработчик. Очередь позволяет буферизовать запросы на время, пока сервер завершает вызов accept(2) и затем создает дочерний процесс. Заметим, что новый сокет >newsockfd
, полученный в результате вызова accept(2), адресует полностью определенный коммуникационный канал: протокол и полные адреса обоих узлов — клиента и сервера. Напротив, для сокета >sockfd
определена только локальная часть канала. Это позволяет серверу продолжать использовать >sockfd
для "прослушивания" последующих запросов.
Наконец, если для сокетов потока при приеме и передаче данных могут быть использованы стандартные вызовы read(2) и write(2), то сокеты дата- грамм должны пользоваться специальными системными вызовами (эти вызовы также доступны для сокетов других типов):
>#include
>#include
>int send(int s, const char *msg, int len, int flags);
>int sendto(int s, const char *msg, int len, int flags,
> const struct sockaddr* toaddr, int tolen);
>int recv(int s, char *buf, int len, int flags);
>int recvfrom(int s, char *buf, int len, int flags,
> struct sockaddr* fromaddr, int* fromlen);
Функции send(2) и sendto(2) используются для передачи данных удаленному узлу, а функции recv(2) и recvfrom(2) — для их приема. Основным различием между ними является то, что функции send(2) и recv(2) могут быть использованы только для "подсоединенного" сокета, т.е. после вызова connect(2).
Все эти вызовы используют в качестве первого аргумента дескриптор сокета, через который производится обмен данными. Аргумент >msg
содержит сообщение длиной >len
, которое должно быть передано по адресу >toaddr
, длина которого составляет >tolen
байтов. Для функции send(2) используется адрес получателя, установленный предшествовавшим вызовом connect(2). Аргумент >buf
представляет собой буфер, в который копируются полученные данные.
Параметр >flags
может принимать следующие значения:
>MSG_OOB | Передать или принять экстренные данные вместо обычных |
>MSG_PEEK | Просмотреть данные, не удаляя их из системного буфера (последующие операции чтения получат те же данные) |
Пример использования сокетов
В заключение приведем пример использования сокетов для организации межпроцессного взаимодействия. Поскольку в данном разделе не затрагиваются сетевые вопросы, то и сокеты, которые будут использованы в примере, принадлежат домену UNIX. Как и в предыдущих примерах, функциональность нашей распределенной системы не отличается разнообразием: клиент посылает серверу сообщение "Здравствуй, Мир!", а сервер отправляет его обратно клиенту, который после получения выводит сообщение на экран.
В примере использованы сокеты датаграмм, которые в домене UNIX практически не отличаются от сокетов потока. В качестве адреса сервера предлагается имя файла ./echo.server (мы полагаем, что в системе запущен только один сервер из данного каталога). Предполагается, что клиенты заранее знают этот адрес. Сервер связывает созданный сокет с этим локальным адресом и таким образом регистрируется в системе. Начиная с этого момента он готов к получению и обработке сообщений. Сервер начинает бесконечный цикл, ожидая сообщений от клиентов, блокируясь на вызове recvfrom(2). При получении сообщения сервер отправляет его обратно, вызывая sendto(2).
>#include
>#include
>#include
>#define MAXBUF 256 char
>buf[MAXBUF];
>main() {
> struct sockaddr_un serv_addr, clnt_addr;
> int sockfd;
> int saddrlen, caddrlen, max caddrlen, n;
> /* Создадим сокет */
> if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0} {
> printf("Невозможно создать сокет\n");