Avahi dns_sd 兼容层无法运行浏览回调

发布于 2024-12-04 13:36:07 字数 1901 浏览 2 评论 0原文

背景

我正在为 Haskell 开发一个跨平台 Zeroconf/Bonjour/DNS-SD 库,并认为我最好的选择是针对 dns_sd.h API。在Linux下,该接口的实现由Avahi提供,它声称支持Bonjour API的子集。

问题

作为对我的库的完整性检查,我用 C 语言编写了一个小型测试程序,该程序仅使用 API 的基本框架。它会浏览 _http._tcp 类型的网络上的任何服务,一看到服务就打印一条消息,然后终止:

#include <dns_sd.h>
#include <stdio.h>
#include <stdlib.h>

void cb(DNSServiceRef sdRef,
        DNSServiceFlags flags,
        uint32_t interfaceIndex,
        DNSServiceErrorType errorCode,
        const char *serviceName,
        const char *regtype,
        const char *replyDomain,
        void *context) {
  printf("called!\n");
}

int main() {
  DNSServiceRef sd = malloc(sizeof(DNSServiceRef));
  const char *regtype = "_http._tcp";
  DNSServiceErrorType err1 = DNSServiceBrowse(&sd, 0, 0, regtype, NULL, &cb, NULL);
  printf("err1=%d\n", err1);
  DNSServiceErrorType err2 = DNSServiceProcessResult(sd);
  printf("err2=%d\n", err2);
  return 0;
}

在我的 Mac 上,这个测试程序在 C 语言和等效语言中都可以正常工作Haskell(它找到了我的打印机;令人兴奋!):

$ gcc test.c -o test
$ ./test
err1=0
called!
err2=0

但是在我的 Linux 机器上,程序在退出之前斥责我而不调用回调:

$ gcc test.c -o test -ldns_sd
$ ./test
*** WARNING *** The program 'test' uses the Apple Bonjour compatibility layer of Avahi.
*** WARNING *** Please fix your application to use the native API of Avahi!
*** WARNING *** For more information see <http://0pointer.de/avahi-compat?s=libdns_sd&e=test>
err1=0
err2=0

问题

  1. Avahi dns_sd 兼容性层仍然是一个合适的目标吗?跨平台绑定?或者,关于使用本机 Avahi API 的警告消息是否足够严重,我应该考虑重新定位?
  2. C 语言的跨平台 Zeroconf 的最新技术是什么?

Background

I'm working on a cross-platform Zeroconf/Bonjour/DNS-SD library for Haskell, and figured my best bet would bet would be to target the dns_sd.h API. Under Linux, the implementation of this interface is provided by Avahi, which claims to support a subset of the Bonjour API.

Problem

As a sanity check for my library, I've written a small test program in C that just uses the bare bones of the API. It browses for any service on the network of type _http._tcp, prints a message as soon as it sees one, and then dies:

#include <dns_sd.h>
#include <stdio.h>
#include <stdlib.h>

void cb(DNSServiceRef sdRef,
        DNSServiceFlags flags,
        uint32_t interfaceIndex,
        DNSServiceErrorType errorCode,
        const char *serviceName,
        const char *regtype,
        const char *replyDomain,
        void *context) {
  printf("called!\n");
}

int main() {
  DNSServiceRef sd = malloc(sizeof(DNSServiceRef));
  const char *regtype = "_http._tcp";
  DNSServiceErrorType err1 = DNSServiceBrowse(&sd, 0, 0, regtype, NULL, &cb, NULL);
  printf("err1=%d\n", err1);
  DNSServiceErrorType err2 = DNSServiceProcessResult(sd);
  printf("err2=%d\n", err2);
  return 0;
}

On my Mac, this test program works fine in both C and the equivalent Haskell (it finds my printer; exciting!):

$ gcc test.c -o test
$ ./test
err1=0
called!
err2=0

But on my Linux machine, the program berates me before exiting without invoking the callback:

$ gcc test.c -o test -ldns_sd
$ ./test
*** WARNING *** The program 'test' uses the Apple Bonjour compatibility layer of Avahi.
*** WARNING *** Please fix your application to use the native API of Avahi!
*** WARNING *** For more information see <http://0pointer.de/avahi-compat?s=libdns_sd&e=test>
err1=0
err2=0

Questions

  1. Is the Avahi dns_sd compatibility layer still a suitable target for a cross-platform binding? Or is that warning message serious enough about using the native Avahi API that I should consider retargeting?
  2. What is the state of the art for cross-platform Zeroconf in C?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

清晰传感 2024-12-11 13:36:07

由于我不知道的原因,它仅适用于非阻塞调用。下面是改进后的代码。 Avahi 的 Socket 设置为非阻塞模式,然后使用 select (3) 等待可用数据。每次套接字上有数据时都必须调用 DNSServiceProcessResult(sd) ,因此您的示例在其他平台上运行可能纯属运气。

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dns_sd.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

static int set_nonblocking(int fd)
{
    int flags;
    /* If they have O_NONBLOCK, use the Posix way to do it */
#if defined(O_NONBLOCK)
    /* Fixme: O_NONBLOCK is defined but broken on SunOS 4.1.x and AIX 3.2.5. */
    if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
        flags = 0;
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
#else
    /* Otherwise, use the old way of doing it */
    flags = 1;
    return ioctl(fd, FIOBIO, &flags);
#endif
}     

void cb(DNSServiceRef sdRef,
        DNSServiceFlags flags,
        uint32_t interfaceIndex,
        DNSServiceErrorType errorCode,
        const char *serviceName,
        const char *regtype,
        const char *replyDomain,
        void *context) {
    printf("called %s %s!\n", serviceName, regtype);
}

int main() {
    DNSServiceRef sd = malloc(sizeof(DNSServiceRef));
    const char *regtype = "_http._tcp";
    DNSServiceErrorType err1 = DNSServiceBrowse(&sd, 0, 0, regtype, NULL, &cb, NULL);
    printf("err1=%d\n", err1);
    int socket = DNSServiceRefSockFD(sd);
    set_nonblocking(socket);

    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(socket, &read_fds);

    while(1) {
        if(select(socket+1, &read_fds, NULL, NULL, NULL)  < 0) {
            perror("select");  
        }
        DNSServiceErrorType err2 = DNSServiceProcessResult(sd);
        printf("err2=%d\n", err2);
        if(err2 != 0)
            return 2;
    }
    return 0;
}

By reason unknown to me, it works only with non-blocking calls. Below is the improved code. Socket from Avahi is set to a non-blocking mode and then select (3) is used to wait for available data. DNSServiceProcessResult(sd) has to be called each time there is data on the socket so it may have been pure luck that your example worked on other platforms.

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dns_sd.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

static int set_nonblocking(int fd)
{
    int flags;
    /* If they have O_NONBLOCK, use the Posix way to do it */
#if defined(O_NONBLOCK)
    /* Fixme: O_NONBLOCK is defined but broken on SunOS 4.1.x and AIX 3.2.5. */
    if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
        flags = 0;
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
#else
    /* Otherwise, use the old way of doing it */
    flags = 1;
    return ioctl(fd, FIOBIO, &flags);
#endif
}     

void cb(DNSServiceRef sdRef,
        DNSServiceFlags flags,
        uint32_t interfaceIndex,
        DNSServiceErrorType errorCode,
        const char *serviceName,
        const char *regtype,
        const char *replyDomain,
        void *context) {
    printf("called %s %s!\n", serviceName, regtype);
}

int main() {
    DNSServiceRef sd = malloc(sizeof(DNSServiceRef));
    const char *regtype = "_http._tcp";
    DNSServiceErrorType err1 = DNSServiceBrowse(&sd, 0, 0, regtype, NULL, &cb, NULL);
    printf("err1=%d\n", err1);
    int socket = DNSServiceRefSockFD(sd);
    set_nonblocking(socket);

    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(socket, &read_fds);

    while(1) {
        if(select(socket+1, &read_fds, NULL, NULL, NULL)  < 0) {
            perror("select");  
        }
        DNSServiceErrorType err2 = DNSServiceProcessResult(sd);
        printf("err2=%d\n", err2);
        if(err2 != 0)
            return 2;
    }
    return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文