7

unix ローカル ソケット (AF_UNIX アドレス ファミリ、つまりUDP ではない) を介してデータグラムを使用する単純なサービスを実装しています。サーバーはパブリックアドレスにバインドされており、リクエストを問題なく受け取ります。残念ながら、応答に関してはsendto、クライアントもバインドされていない限り失敗します。(一般的なエラーは ですTransport endpoint is not connected)。

ランダムな名前 (ファイルシステムベースまたは抽象) へのバインドは機能します。しかし、私はそれを避けたいと思います.私が選んだ名前が衝突しないことを保証するのは誰ですか?

connectUnix ソケットのストリーム モードのドキュメントによると、抽象名がまだない場合は、その時点で抽象名が割り当てられるとのことです。そのような機能はデータグラム指向のソケットで利用できますか?

4

4 に答える 4

6

私が参照したunix(7) man ページには、UNIX ソケットの自動バインドに関する次の情報がありました。

bind(2) 呼び出しで addrlen を sizeof(sa_family_t) として指定するか、明示的にアドレスにバインドされていないソケットに SO_PASSCRED ソケット オプションが指定された場合、ソケットは抽象アドレスに自動バインドされます。

これが、sa_family_t が short であるため、Linux カーネルがアドレス長が sizeof(short) と等しいことをチェックする理由です。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) を渡さないようにする必要があります。

于 2011-12-15T16:58:38.433 に答える
4

Linux を実行していると仮定します。このアドバイスが SunOS に当てはまるのか、UNIX に当てはまるのかはわかりません。

まず、答え: socket() の後、connect() または最初の sendto() の前に、次のコードを追加してみてください。

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

ここで、説明: unix(7)の man ページには次のように書かれています。

ソケットが接続され、まだローカル アドレスを持っていない場合、抽象名前空間内の一意のアドレスが自動的に生成されます。

悲しいことに、man ページには嘘があります。

Linux ソース コードを調べると、ソケット フラグに SOCK_PASSCRED が設定されている場合、unix_dgram_connect() は unix_autobind() のみを呼び出すことがわかります。SOCK_PASSCRED が何であるかがわからず、現在は午前 1 時なので、別の解決策を探す必要があります。

unix_bindを調べると、渡されたサイズが「sizeof(short)」と等しい場合、 unix_bind が unix_autobind を呼び出すことに気付きました。したがって、上記の解決策。

おはようございます。

ロブ

于 2008-09-20T06:15:00.623 に答える
1

少し遅い応答ですが、私がしたようにグーグルを使ってこれを見つけた人のために。ロブ・アダムの答えは、これに対する「本当の」答えを得るのに役立ちました。単にset(level SO_SOCKET、see man 7 unix)を使用して1に設定SO_PASSCREDします。ばかげたバインドは必要ありません。

これをPHPで使用しましたが、定義されていませんSO_PASSCRED(愚かなPHP)。ただし、自分で定義すれば、それでも機能します。私のコンピューターでは16の値があり、非常に移植性が高いと思います。

于 2009-08-16T12:35:03.267 に答える
-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
于 2008-09-18T18:07:36.473 に答える