- man(7) unix
- StackOverflow: Проблема с EADDRINUSE
- StackOverflow: Проблема с гарантированным удалением файла
- Unix domain sockets vs. Internet sockets
- Netperf Manual
Для связи процессов на одном компьютере можно использовать доменные сокеты (IPC-сокеты).
Отличия от интернет-сокетов:
- Более высокая производительность.
- Доставка SIGPIPE сразу после обрыва соединения с другого конца.
- Стандартные права доступа unix у файлов сокетов.
Производительность можно измерить с помощью netperf:
$ netperf -t TCP_STREAM # TCP
$ netperf -t STREAM_STREAM # unix domain (компилировать netperf с опцией --enable-unixdomain)
Проблема
Файл сокета создается при вызове bind(2). Если файл уже существует, возвращается ошибка EADDRINUSE.Единственный способ повторного использования файла сокета - вызов unlink(2) перед bind.
Есть две схемы:
- Вызывать unlink перед завершением сервера. Тогда нет гарантии, что при крахе сервера не останется висячего файла, который пользователю придется удалять вручную.
- Вызывать unlink при запуске сервера. Тогда нет гарантии, что не будет удален файл другого запущенного экземпляра сервера.
Решение
Именованные доменные сокеты бывают двух типов: filesystem sockets и abstract namespace sockets, см. unix(7).Filesystem sockets
Простое решение - использовать дополнительный lock-файл для каждого сокета.
Этот файл может быть заблокирован тогда и только тогда, когда в данный момент запущен другой экземпляр сервера. Если блокировку удалось захватить, можно безопасно вызывать unlink() для файла сокета.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | const char *socket_path = "/tmp/server-socket"; int server = socket(PF_LOCAL, SOCK_STREAM, 0); assert(server != -1); struct sockaddr_un server_addr; bzero(&server_addr, sizeof(server_addr)); server_addr.sun_family = AF_LOCAL; strcpy(server_addr.sun_path, socket_path); char lock_path[256]; sprintf(lock_path, "%s.lock", socket_path); // // Open lock file // int lock_fd = open(lock_path, O_RDONLY | O_CREAT, 0600); if (lock_fd == -1) { printf("can't open lock file\n"); exit(1); } // // Acquire lock // int ret = flock(lock_fd, LOCK_EX | LOCK_NB); if (ret != 0) { printf("address already in use!\n"); exit(1); } // // Remove socket file // unlink(socket_path); // // Create socket file // ret = bind(server, (struct sockaddr *)&server_addr, sizeof(server_addr)); assert(ret == 0); |
Abstract namespace sockets
Еще более простое решение - использовать abstract namespace (поддерживается только в Linux).
Если в структуре sockaddr_un в поле sun_path в первый байт имени сокета записать '\0', сокет с этим именем будет создан не в файловой системе, а абстрактном пространстве имен.
Такой сокет будет автоматически освобожден при завершении приложения.
Комментариев нет:
Отправить комментарий