Автоматическое присвоение имен локальным сокетам дейтаграмм AF_UNIX?

StackOverflow https://stackoverflow.com/questions/94594

Вопрос

Я реализую простой сервис, использующий дейтаграммы через локальные сокеты unix (семейство адресов AF_UNIX, т.е. не UDP).Сервер привязан к общедоступному адресу, и он отлично принимает запросы.К сожалению, когда дело доходит до ответа, sendto завершается неудачей, если только клиент тоже не привязан.(распространенной ошибкой является Transport endpoint is not connected).

Работает привязка к какому-нибудь случайному имени (основанному на файловой системе или абстрактному).Но я бы хотел избежать этого:кто я такой, чтобы гарантировать, что выбранные мной имена не будут пересекаться?

Документация по потоковому режиму сокетов unix сообщает нам, что абстрактное имя будет присвоено им в connect время, если у них его еще нет.Доступна ли такая функция для сокетов, ориентированных на дейтаграммы?

Это было полезно?

Решение

Я предполагаю, что вы используете Linux;Я не знаю, применим ли этот совет к SunOS или любому другому UNIX.

Во-первых, ответ:после socket() и перед connect() или first sendto() попробуйте добавить этот код:

struct sockaddr_un me;
me.sun_family = AF_UNIX;
int result = bind(fd, (void*)&me, sizeof(short));

Теперь объяснение:тот самый unix(7) на странице руководства написано следующее:

Когда сокет подключен и у него еще нет локального адреса, уникальный адрес в абстрактном пространстве имен будет сгенерирован автоматически.

К сожалению, справочная страница лжет.

Изучая Исходный код Linux, мы видим, что unix_dgram_connect() вызывает unix_autobind() только в том случае, если во флагах сокета установлен SOCK_PASSCRED.Поскольку я не знаю, что такое SOCK_PASSCRED, а сейчас 1:00 ночи, мне нужно поискать другое решение.

Исследуя unix_bind (привязка), я замечаю, что unix_bind вызывает unix_autobind, если переданный размер равен "sizeof(short)".Таким образом, решение приведено выше.

Удачи и доброго утра.

Роб

Другие советы

Тот Самый unix(7) справочная страница, на которую я ссылался, содержала следующую информацию об autobind UNIX-сокетах:

Если вызов bind(2) указывает addrlen как sizeof(sa_family_t), или параметр сокета SO_PASSCRED был указан для сокета, который явно не был привязан к адресу, то сокет автоматически привязывается к абстрактному адресу.

Вот почему ядро Linux проверяет, что длина адреса равна sizeof(short), потому что sa_family_t - это short .На другой справочной странице unix (7), на которую ссылается отличный ответ Роба, говорится, что клиентские сокеты всегда автоматически привязаны к connect, но поскольку сокеты SOCK_DGRAM не имеют соединения (несмотря на вызов connect на них) Я считаю, что это применимо только к сокетам SOCK_STREAM.

Также обратите внимание, что при указании ваших собственных абстрактных имен сокетов пространства имен адрес сокета в этом пространстве имен задается дополнительными байтами в sun_path, которые покрываются указанной длиной структуры адресов.

struct sockaddr_un me;
const char name[] = "\0myabstractsocket";
me.sun_family = AF_UNIX;
// size-1 because abstract socket names are not null terminated
memcpy(me.sun_path, name, sizeof(name) - 1);
int result = bind(fd, (void*)&me, sizeof(me.sun_family) + sizeof(name) - 1);

функция sendto() также должна ограничивать длину адреса и не передавать sizeof(sockaddr_un).

Немного запоздалый ответ, но для тех, кто найдет это с помощью Google, как это сделал я.Ответ Роба Адама помог мне получить "реальный" ответ на этот вопрос:просто используйте set (уровень SO_SOCKET, видеть man 7 unix) установить SO_PASSCRED до 1.Нет необходимости в глупой привязке.

Я использовал это в PHP, но в нем нет SO_PASSCRED определенный (дурацкий PHP).Однако это все еще работает, если вы определяете это сами.На моем компьютере он имеет значение 16, и я считаю, что он будет работать вполне переносимо.

Я не уверен, что полностью понял ваш вопрос, но вот реализация дейтаграммы эхо-сервера, которую я только что написал.Вы можете видеть, что сервер отвечает клиенту по тому же IP / ПОРТУ, с которого он был отправлен.

Вот код

Во-первых, сервер (прослушиватель)

from socket import *
import time
class Listener:
    def __init__(self, port):
        self.port = port
        self.buffer = 102400

    def listen(self):

        sock = socket(AF_INET, SOCK_DGRAM)
        sock.bind(('', self.port))

        while 1:
            data, addr = sock.recvfrom(self.buffer)
            print "Received: " + data
            print "sending to %s" % addr[0]
            print "sending data %s" % data
            time.sleep(0.25)
            #print addr # will tell you what IP address the request came from and port
            sock.sendto(data, (addr[0], addr[1]))
            print "sent"
        sock.close()

if __name__ == "__main__":
    l = Listener(1975)
    l.listen()

И теперь Клиент (отправитель), который получает ответ от Слушателя

from socket import *
from time import sleep
class Sender:
    def __init__(self, server):
       self.port = 1975
       self.server = server
       self.buffer = 102400

    def sendPacket(self, packet):
        sock = socket(AF_INET, SOCK_DGRAM)
        sock.settimeout(10.75)


        sock.sendto(packet, (self.server, int(self.port)))

        while 1:
            print "waiting for response"
            data, addr = sock.recvfrom(self.buffer)
            sock.close()
            return data



if __name__ == "__main__":
        s = Sender("127.0.0.1")
        response = s.sendPacket("Hello, world!")
        print response
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top