Команда diff ничего не выводит. Это говорит о том, что входящий и исходящий файлы идентичны.
Обратите внимание: путь к сокету продолжает существовать даже после завершения работы сервера. Именно поэтому перед операцией bind() сервер удаляет любой файл с тем же путем, что у сокета, используя вызов remove() (при наличии подходящих прав доступа данный вызов удаляет файл любого типа, даже если тот не является сокетом). Если этого не сделать и если сокет с тем же путем уже был создан в результате предыдущего запуска сервера, то операция bind() завершится ошибкой.
В обобщенном описании датаграммных сокетов, приведенном в разделе 52.6, утверждалось, что они не обеспечивают надежную передачу данных. Однако это относилось к взаимодействию по сети. В домене UNIX датаграммное взаимодействие происходит внутри ядра и является надежным. Все сообщения доставляются в правильном порядке и без дублирования.
Стандарт SUSv3 не уточняет максимальный размер датаграмм, которые можно отправлять, используя сокеты в домене UNIX. Linux поддерживает довольно большие датаграммы. Ограничение устанавливается с помощью параметра сокета SO_SNDBUF и различных файлов в каталоге /proc (см. справочную страницу socket(7)). Однако в отдельных реализациях UNIX действует более строгое ограничение — например, 2048 байт. Портируемым приложениям, применяющим датаграммные сокеты в домене UNIX, рекомендуется существенно ограничивать максимальный размер датаграмм.
В листингах 53.6 и 53.7 представлено простое клиент-серверное приложение, которое задействует датаграммные сокеты в домене UNIX. В обеих этих программах применяется заголовочный файл из листинга 53.5.
Листинг 53.5. Заголовочный файл, используемый в программах ud_ucase_sv.c и ud_ucase_cl.c
sockets/ud_ucase.h
#include
#include
#include
#include "tlpi_hdr.h"
#define BUF_SIZE 10 /* Максимальный размер сообщений,
которыми обмениваются клиент и сервер */
#define SV_SOCK_PATH "/tmp/ud_ucase"
sockets/ud_ucase.h
Сначала серверная программа (см. листинг 53.6) создает сокет и привязывает его к общеизвестному адресу (предварительно удалив любой существующий путь, который совпадает с этим адресом). Затем она входит в бесконечный цикл, где принимает датаграммы от клиентов, используя вызов recvfrom(), переводит полученный текст в верхний регистр и возвращает результат обратно клиенту; адрес клиента извлекается с помощью все того же вызова recvfrom().
Клиентская программа (см. листинг 53.7) создает сокет и привязывает его к адресу, чтобы сервер мог отправить свой ответ. Адрес клиента делается уникальным за счет добавления к пути идентификатора его процесса. Клиент входит в цикл, отправляя серверу каждый из своих аргументов командной строки в виде отдельного сообщения. Сделав это, он считывает ответ сервера и направляет его в стандартный вывод.
Листинг 53.6. Простой датаграммный сервер в домене UNIX
sockets/ud_ucase_sv.c
#include "ud_ucase.h"
int
main(int argc, char *argv[])
{
struct sockaddr_un svaddr, claddr;
int sfd, j;
ssize_t numBytes;
socklen_t len;
char buf[BUF_SIZE];
sfd = socket(AF_UNIX, SOCK_DGRAM, 0); /* Создаем серверный сокет */
if (sfd == -1)
errExit("socket");
/* Формируем общеизвестный адрес и привязываем к нему серверный сокет */
if (remove(SV_SOCK_PATH) == -1 && errno!= ENOENT)
errExit("remove-%s", SV_SOCK_PATH);
memset(&svaddr, 0, sizeof(struct sockaddr_un));
svaddr.sun_family = AF_UNIX;
strncpy(svaddr.sun_path, SV_SOCK_PATH, sizeof(svaddr.sun_path) — 1);
if (bind(sfd, (struct sockaddr *) &svaddr,
sizeof(struct sockaddr_un)) == -1)
errExit("bind");
/* Принимаем сообщения, переводим в верхний регистр и возвращаем результат клиенту */
for (;;) {
len = sizeof(struct sockaddr_un);
numBytes = recvfrom(sfd, buf, BUF_SIZE, 0,
(struct sockaddr *) &claddr, &len);
if (numBytes == -1)
errExit("recvfrom");
printf("Server received %ld bytes from %s\n", (long) numBytes, claddr.sun_path);
for (j = 0; j < numBytes; j++)
buf[j] = toupper((unsigned char) buf[j]);
if (sendto(sfd, buf, numBytes, 0, (struct sockaddr *)
&claddr, len)!= numBytes)
fatal("sendto");
}
}
sockets/ud_ucase_sv.c
Листинг 53.7. Простой датаграммный клиент в домене UNIX
sockets/ud_ucase_cl.c
#include "ud_ucase.h"
int
main(int argc, char *argv[])
{
struct sockaddr_un svaddr, claddr;
int sfd, j;
size_t msgLen;
ssize_t numBytes;
char resp[BUF_SIZE];
if (argc < 2 || strcmp(argv[1], "-help") == 0)
usageErr("%s msg…\n", argv[0]);
/* Создаем клиентский сокет; привязываем его к уникальному пути (основанному на PID) */
sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sfd == -1)
errExit("socket");