Автоматическое присвоение имен локальным датаграммам AF_UNIX? - PullRequest
5 голосов
/ 18 сентября 2008

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

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

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

Ответы [ 4 ]

5 голосов
/ 15 декабря 2011

Справочная страница unix (7) , на которую я ссылался, содержала эту информацию о сокетах UNIX с автосвязыванием:

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

Вот почему ядро ​​Linux проверяет, что длина адреса равна sizeof (short), потому что sa_family_t - это short. На другой справочной странице по unix (7), на которую ссылается замечательный ответ Роба, говорится, что клиентские сокеты всегда автоматически связываются при подключении, но поскольку сокеты SOCK_DGRAM не устанавливают соединение (несмотря на вызов соединения), я считаю, что это применимо только к сокетам 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).

4 голосов
/ 20 сентября 2008

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

Сначала ответ: после сокета () и перед connect () или первым 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)". Таким образом, решение выше.

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

Rob

1 голос
/ 16 августа 2009

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

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

0 голосов
/ 18 сентября 2008

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

Вот код

Сначала сервер (слушатель)

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
...