2008-09-18 1 views
4

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

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

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

ответ

4

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

Во-первых, ответ: после того, как гнездо() и до подключения() или первого SendTo(), попробуйте добавить этот код:

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

Теперь объяснение: страница unix(7) человек говорит, что это :

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

К сожалению, страница руководства лежит.

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

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

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

Роб

+0

Отличный анализ, Роб! Большое спасибо! Кажется, что есть разные источники для manpages: на работе (какой-то старый SuSE) они явно ограничивают эффект autobind-on-connect для потоковых сокетов. Дома у меня такое же предложение, как и вы. В конечном счете, я запомню это как связывание с пустым путем. – 2008-09-20 13:52:25

-1

Я не уверен, что полностью понимаю ваш вопрос, но вот реализация дейтаграммы эхо-сервера, который я только что написал. Вы можете видеть, что сервер отвечает клиенту на том же 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 
+0

Вопрос был о разъемах unix, а не в Интернете. (т. е. семейство адресов AF_UNIX, а не AF_INET). Спасибо за ваш ответ, я отредактирую вопрос и сделаю это явным. – 2008-09-18 19:17:16

+0

Извините, что, спасибо за разъяснение. – 2008-09-26 07:00:08

0

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

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

3

unix(7) человек страница я ссылка была эта информация о autobind UNIX сокеты:

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

Именно поэтому ядро ​​Linux проверяет, что длина адреса равна sizeof (short), потому что sa_family_t является коротким. Другая справочная страница 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).

Смежные вопросы