كيفية استخدام getaddrinfo_a للقيام بحل غير متزامن مع glibc

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

  •  09-06-2019
  •  | 
  •  

سؤال

وظيفة غالبًا ما يتم تجاهلها ولا تتطلب مكتبة خارجية، ولكنها في الأساس لا تحتوي على أي وثائق على الإطلاق.

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

المحلول

تحديث (2010/10/11):تحتوي صفحات دليل Linux الآن على وثائق getaddrinfo_a، ويمكنك العثور عليها هنا: http://www.kernel.org/doc/man-pages/online/pages/man3/getaddrinfo_a.3.html

كإخلاء للمسؤولية، يجب أن أضيف أنني جديد تمامًا على لغة C ولكني لست مبتدئًا تمامًا، لذلك قد تكون هناك أخطاء أو ممارسات ترميز سيئة، يرجى تصحيحي (وقواعدي النحوية سيئة أيضًا).

أنا شخصياً لم أعلم بالأمر إلا بعد أن عرفته هذا المشنور بقلم آدم لانجلي، سأقدم بعض مقتطفات التعليمات البرمجية لتوضيح استخدامه وتوضيح بعض الأشياء التي قد لا تكون واضحة عند الاستخدام الأول.تتمثل فوائد استخدام هذا في استعادة البيانات التي يمكن استخدامها بسهولة قابس كهرباء(), يستمع() وغيرها من الوظائف، وإذا تم القيام بها بشكل صحيح، فلن تقلق بشأن IPv4/v6 أيضًا.
لذا، للبدء بالأساسيات، كما مأخوذة من الرابط أعلاه (ستحتاج إلى الارتباط ضد libanl (-lanl)) :
هنا هو النموذج الأولي للوظيفة:

int getaddrinfo_a(int mode, struct gaicb *list[], int ent, 
                  struct sigevent *);
  1. ال وضع هو إما GAI_WAIT (وهو على الأرجح ليس ما تريده) وGAI_NOWAIT لعمليات البحث غير المتزامنة
  2. ال com.gaicb تقبل الوسيطة مجموعة من المضيفين للبحث باستخدام ملف الأنف والحنجرة وسيطة تحدد عدد العناصر الموجودة في المصفوفة
  3. ال sigevent سيكون مسؤولاً عن إخبار الوظيفة بكيفية إعلامنا، المزيد عن هذا في لحظة

تبدو بنية gaicb كما يلي:

struct gaicb {
    const char *ar_name;
    const char *ar_service;
    const struct addrinfo *ar_request;
    struct addrinfo *ar_result;
};

إذا كنت معتادًا على getaddrinfo، فإن هذه الحقول تتوافق معها كما يلي:

int getaddrinfo(const char *node, const char *service,
                const struct addrinfo *hints,
                struct addrinfo **res);

العقدة هي حقل ar_name، والخدمة هي المنفذ، ووسيطة التلميحات تتوافق مع عضو ar_request ويتم تخزين النتيجة في الباقي.
يمكنك الآن تحديد الطريقة التي تريد أن يتم إعلامك بها من خلال بنية sigevent:

struct sigevent {
    sigval_t sigev_value;
    int sigev_signo;
    int sigev_notify;
    void (*sigev_notify_function) (sigval_t);
    pthread_addr_t *sigev_notify_attributes;
};
  1. يمكنك تجاهل الإشعار عبر ضبط _sigev_notify_ على SIGEV_NONE
  2. يمكنك تشغيل إشارة عبر ضبط sigev_notify على SIGEV_SIGNAL وsigev_signo على الإشارة المطلوبة.لاحظ أنه عند استخدام إشارة في الوقت الحقيقي (سيجرتمين-سيجرتماكس, استخدمه دائمًا عبر وحدات الماكرو والإضافة سيجرتمين+2 وما إلى ذلك) يمكنك تمرير مؤشر أو قيمة في عضو sigev_value.sival_ptr أو sigev_value.sival_int على التوالي
  3. يمكنك طلب رد اتصال في موضوع جديد عبر تعيين sigev_notify على SIGEV_NONE

لذا، إذا كنت تريد البحث عن اسم مضيف، فقم بتعيين ar_name على المضيف وتعيين كل شيء آخر عليه باطل, ، إذا كنت تريد الاتصال بمضيف، فقم بتعيين ar_name و ar_service، وإذا كنت تريد إنشاء خادم، فقم بتحديد ar_service وحقل ar_result.يمكنك بالطبع تخصيص عضو ar_request ليناسب محتوى قلبك، انظر إليه man getaddrinfo لمزيد من المعلومات.

إذا كان لديك حلقة حدث مع تحديد/استطلاع/epoll/kqueue فقد ترغب في استخدامها com.signalfd للراحة.يقوم Signalfd بإنشاء واصف ملف يمكنك من خلاله استخدام آليات استقصاء الأحداث المعتادة مثل:

#define _GNU_SOURCE //yes this will not be so standardish
#include <netdb.h>
#include <signal.h>
#include <sys/signalfd.h>

void signalfd_setup(void) {
    int sfd;
    sigset_t mask;

    sigemptyset(&mask);
    sigaddset(&mask, SIGRTMIN);
    sigprocmask(SIG_BLOCK, &mask, NULL); //we block the signal
    sfd = signalfd(-1, &mask, 0);
    //add it to the event queue
}
void signalfd_read(int fd) {
    ssize_t s;
    struct signalfd_siginfo fdsi;
    struct gaicb *host;

    while((s = read(fd, &fdsi, sizeof(struct signalfd_siginfo))) > 0){
    if (s != sizeof(struct signalfd_siginfo)) return; //thats bad
    host = fdsi.ssi_ptr; //the pointer passed to the sigevent structure
            //the result is in the host->ar_result member
            create_server(host);
    }
}
void create_server(struct gaicb *host) {
    struct addrinfo *rp, *result;
    int fd;

    result = host->ar_result;
    for(rp = result; rp != NULL; rp = rp->ai_next) {
        fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
        bind(fd, rp->ai_addr, rp->ai_addrlen);
        listen(fd, SOMAXCONN);
        //error checks are missing!

        freeaddrinfo(host->ar_request);
        freeaddrinfo(result);
        //you should free everything you put into the gaicb
    }
}
int main(int argc, char *argv[]) {
    struct gaicb *host;
    struct addrinfo *hints;
    struct sigevent sig;

    host = calloc(1, sizeof(struct gaicb));
    hints = calloc(1, sizeof(struct addrinfo));

    hints->ai_family = AF_UNSPEC; //we dont care if its v4 or v6
    hints->ai_socktype = SOCK_STREAM;
    hints->ai_flags = AI_PASSIVE;
    //every other field is NULL-d by calloc

    host->ar_service = "8888"; //the port we will listen on
    host->ar_request = hints;

    sig.sigev_notify = SIGEV_SIGNAL;
    sig.sigev_value.sival_ptr = host;
    sig.sigev_signo = SIGRTMIN;

    getaddrinfo_a(GAI_NOWAIT, &host, 1, &sig);

    signalfd_setup();

    //start your event loop
    return 0;
}

يمكنك بالطبع استخدام معالج إشارة بسيط لهذه المهمة أيضًا، انظر رجل كبير لمزيد من المعلومات.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top