2008-09-18 60 views
4

我在unix本地套接字(AF_UNIX地址系列,即而不是UDP)上使用數據報實現一項簡單服務。服務器綁定到一個公共地址,它接收請求就好了。不幸的是,當回答回答時,sendto失敗,除非客戶端也被綁定。 (常見的錯誤是Transport endpoint is not connected)。AF_UNIX本地數據報套接字的自動命名?

綁定到一些隨機名稱(基於文件系統或抽象)的作品。但我想避免這種情況:我是誰來保證我選擇的姓名不會相互衝突?

unix套接字的流模式文檔告訴我們,如果他們還沒有一個抽象名稱,他們將在connect時間給他們分配一個抽象名稱。這種功能是否適用於面向數據報的套接字?

回答

4

我假設響應你正在運行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 source code,如果SOCK_PASSCRED在套接字標誌中設置,我們看到unix_dgram_connect()只調用unix_autobind()。由於我不知道SOCK_PASSCRED是什麼,現在是凌晨1點,我需要尋找另一種解決方案。

檢查unix_bind,我注意到,如果傳入的大小等於「sizeof(short)」,unix_bind將調用unix_autobind。因此,上面的解決方案。

祝你好運,早上好。

羅布

+0

偉大的分析,羅布!非常感謝! 似乎有不同來源的manpages:在工作(一些舊的SuSE),他們明確地限制自動綁定連接效果流套接字。在家裏,我有和你一樣的句子。 從長遠來看,我會記住這是綁定到一個空路徑。 – 2008-09-20 13:52:25

-1

我不太確定我完全理解你的問題,但這裏是我剛寫的一個echo服務器的數據報實現。您可以看到服務器正在響應客戶端發送的相同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

一個反應遲緩的位,但對於任何人發現這個使用谷歌和我一樣。羅布亞當的答案幫助我得到了'真正'的答案:只需使用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是短的。 Rob很好的回答引用了另一個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)。