التسمية التلقائية لمآخذ توصيل مخطط البيانات المحلية AF_UNIX؟

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

سؤال

أقوم بتنفيذ خدمة بسيطة باستخدام مخططات البيانات عبر مآخذ توصيل يونكس المحلية (عائلة عناوين AF_UNIX، أي. ليس UDP).يرتبط الخادم بعنوان عام، ويتلقى الطلبات بشكل جيد.لسوء الحظ، عندما يتعلق الأمر بالرد، sendto يفشل إلا إذا كان العميل ملزمًا أيضًا.(الخطأ الشائع هو Transport endpoint is not connected).

يعمل الارتباط ببعض الأسماء العشوائية (المعتمدة على نظام الملفات أو الملخص).لكني أود تجنب ذلك:من أنا حتى أضمن أن الأسماء التي اخترتها لن تتصادم؟

تخبرنا وثائق وضع الدفق الخاصة بمآخذ توصيل يونكس أنه سيتم تعيين اسم مجرد لها في connect الوقت إذا لم يكن لديهم واحدة بالفعل.هل هذه الميزة متاحة للمآخذ الموجهة نحو مخطط البيانات؟

هل كانت مفيدة؟

المحلول

أفترض أنك تستخدم نظام التشغيل Linux؛لا أعرف ما إذا كانت هذه النصيحة تنطبق على SunOS أو أي نظام UNIX.

أولا الجواب :بعد المقبس () وقبل الاتصال () أو الإرسال الأول () حاول إضافة هذا الكود:

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

والآن الشرح:ولل يونيكس(7) صفحة الرجل تقول هذا:

عند توصيل المقبس ولا يحتوي بالفعل على عنوان محلي ، سيتم إنشاء عنوان فريد في مساحة الاسم المجردة تلقائيًا.

للأسف، صفحة الرجل تكمن.

فحص كود مصدر لينكس, ، نرى أن unix_dgram_connect() يستدعي unix_autobind() فقط إذا تم تعيين SOCK_PASSCRED في إشارات المقبس.نظرًا لأنني لا أعرف ما هو SOCK_PASSCRED، والساعة الآن 1:00 صباحًا، أحتاج إلى البحث عن حل آخر.

فحص unix_bind, ، لاحظت أن unix_bind يستدعي unix_autobind إذا كان الحجم الذي تم تمريره يساوي "sizeof(short)".وهكذا الحل أعلاه.

حظا سعيدا، وصباح الخير.

روب

نصائح أخرى

ال يونيكس(7) تحتوي صفحة الدليل التي أشرت إليها على هذه المعلومات حول مآخذ توصيل UNIX التلقائية:

إذا حدد استدعاء bind(2) addrlen كـ sizeof(sa_family_t)، أو تم تحديد خيار مأخذ التوصيل SO_PASSCRED لمأخذ توصيل لم يكن مرتبطًا بشكل صريح بعنوان، فسيتم ربط المقبس تلقائيًا بعنوان مجرد.

ولهذا السبب تتحقق نواة Linux من أن طول العنوان يساوي sizeof(short) لأن sa_family_t قصير.تقول صفحة دليل unix(7) الأخرى التي تمت الإشارة إليها بواسطة إجابة Rob الرائعة أن مآخذ توصيل العميل تكون دائمًا مرتبطة تلقائيًا عند الاتصال، ولكن نظرًا لأن مآخذ توصيل 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).

رد متأخر بعض الشيء، ولكن لمن يجد هذا باستخدام جوجل كما فعلت.ساعدتني إجابة Rob Adam في الحصول على الإجابة "الحقيقية" لهذا:ببساطة استخدم set (level 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