Use Avahi as a library for Discovering mDNS Service

来源:互联网 发布:js高级程序设计最新 编辑:程序博客网 时间:2024/05/20 08:43

     Avahi(http://www.avahi.org/) is an implement ofmulticast Domain Name System (mDNS) in Linux/Android. mDNS is a (local) network service broadcasting/discovering mechanism: for example,  the IP printers or IP cameras,  could be found via mDNS functions.

    Avahi libraries is easy to be use relatively.  There are  adequate examples to illuminate how to use those API. In here I would like to make them be ample. I would explain about discovering (the serivces) part in this article.


Note : avahi brower would depend on avahi-daemon, this post's goal is to remove the dependency.




  零. 
      Download the avahi, for me, I use version 0.6.31. Then uncompress the tar ball.

 一.     Go into the avahi folder, configure the avahi withgtk2 ,qt4 anddbus

./configure --prefix=$PWD/built --disable-qt3 --disable-gtk3 --disable-mono --enable-tests --enable-core-docs --disable-python --disable-pygtk

     After the configuration has be done,type make and make install.


二. 
    Go to the built//bin folder, execute the avahi-browse:

You would get the similar result with mine:


./avahi-browse -a+  wlan0 IPv6 i5-3210m [60:67:20:a5:44:ae]                  _workstation._tcp    local+  wlan0 IPv4 i5-3210m [60:67:20:a5:44:ae]                  _workstation._tcp    local+  wlan0 IPv6 i5-3210m                                      _udisks-ssh._tcp     local+  wlan0 IPv4 i5-3210m                                      _udisks-ssh._tcp     local+  wlan0 IPv4 GAIGER NETEYE CS2230                          _http._tcp           local




But if you stop the avahi-daemon and re-execute the avahi-browe:

sudo service avahi-daemon stopavahi-daemon stop/waiting./avahi-browse -aFailed to create client object: 幕後程式沒有在執行中 

the Chinese words read "the daemon programming is not on running"

That is, the avahi-browe depends on avahi-daemon, which be theprovider ofservice of discover.

But, If you run the avahi-discover-standalone, there is a window for reporting discovered service:



The GAIGER NETEYE CS2230 be a IP camera in the same lan domain.

     The other services discovered by avahi-browe are local services provided byavahi-daemon, those would vanish whenavahi-daemon stopped.

    You couldstudywhat is the different betweenavahi-browe andavahi-discover-standalone,   the source codes be in folder examples/client-browse-services.c and  avahi-discover-standalone/main.c respectively.

   For most useful and portable for libraries usage, the avahi-discover-standalone/main.c should be removed from UI, and be independent of the others libraries (dbus and so forth).


三.
   Re-configure or uncompress the same tar ball again , use the configure arguments:


./configure --prefix=$PWD/built --disable-dbus --disable-mono --disable-monodoc --disable-qt4 --disable-qt3 --disable-glib --disable-gobject --disable-gtk --disable-gtk3 --disable-gdbm --disable-dbm --disable-python --disable-pygtk

And make -j2 && make install.

That is for minimization the dependence.


四.
   Create a folder be under avahi source root , the folder name be sandyboxcode for me.
   my standalone code be inside the folder:

sandbox-discover-standalone.c:


#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <time.h>#include <net/if.h>#include "avahi-core/lookup.h"#include <avahi-common/simple-watch.h>#include <avahi-common/malloc.h>#include <avahi-common/error.h>#ifndef FALSE #define FALSE       (0)#endif#ifndef TRUE #define TRUE       (1)#endif#define MAX_STR_LEN        (256)typedef struct ServiceInfo {  char *pServiceType; char *pServiceName; char *pDomainName; char *pInterface; char *pProtocolNumber; char *pAddr; unsigned short port; char *pHostName; char *pText;  struct ServiceInfo *pNext;}ServiceInfo, ServiceInfoList;const char unUsedServiceInfoMemHead[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff , 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };//the impossible memory location valuestatic AvahiServer *pServer;static AvahiSimplePoll *pSimplePoll;ServiceInfoList *CreateServiceInfoList(void){ ServiceInfo *pServiceInfo;     pServiceInfo = (ServiceInfo*)malloc(sizeof(ServiceInfo)); memset(pServiceInfo, (unsigned char)0xff, sizeof(ServiceInfo));     return pServiceInfo;}/*CreateServiceInfoList*/#define SAFE_FREE(PTR)     if(NULL != (PTR))\         free(PTR);\        PTR = NULLvoid DestroyServiceInfo(ServiceInfo *pServiceInfo){ if(NULL == pServiceInfo)  return; SAFE_FREE(pServiceInfo->pServiceType); SAFE_FREE(pServiceInfo->pServiceName); SAFE_FREE(pServiceInfo->pDomainName); SAFE_FREE(pServiceInfo->pInterface);SAFE_FREE(pServiceInfo->pProtocolNumber); SAFE_FREE(pServiceInfo->pAddr);SAFE_FREE(pServiceInfo->pHostName); if(NULL != pServiceInfo->pText)  avahi_free(pServiceInfo->pText);   pServiceInfo->pText = NULL; SAFE_FREE(pServiceInfo);}/*DestoryServiceInfo*/void DestroyServiceInfoList(ServiceInfoList *pServiceInfoList){ ServiceInfo *pCurrent; pCurrent = pServiceInfoList; if( 0 == memcmp(pServiceInfoList, &unUsedServiceInfoMemHead[0], sizeof(unUsedServiceInfoMemHead)) ) {  SAFE_FREE(pCurrent);   return; }/*if unused*/ while(NULL != pCurrent) {  ServiceInfo *pNext;  pNext = pCurrent->pNext;  DestroyServiceInfo(pCurrent);  pCurrent = pNext; }/*while*/    }/*CleanServiceInfoList*/void ServiceResolverCallback( AvahiSServiceResolver *pResolver, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *pServiceName, const char *pType, const char *pDomain, const char *pHostName, const AvahiAddress *pAvahiAddress, uint16_t port, AvahiStringList *pText, AvahiLookupResultFlags flags, void* pUserdata){ char addrStr[AVAHI_ADDRESS_STR_MAX]; ServiceInfo *pServiceInfo; //printf("__FUNCTION__ = %s\n", __FUNCTION__); if (AVAHI_RESOLVER_FOUND != event) {  printf("NOT AVAHI_RESOLVER_FOUND\n");  return; }/* AVAHI_RESOLVER_FOUND != event */  pServiceInfo = (ServiceInfo*)pUserdata;  if( 0 == memcmp(pServiceInfo, &unUsedServiceInfoMemHead[0], sizeof(unUsedServiceInfoMemHead)) ) {  memset(pServiceInfo, 0, sizeof(ServiceInfo));  } else {  while(NULL != pServiceInfo->pNext)     pServiceInfo = pServiceInfo->pNext;    pServiceInfo->pNext = (ServiceInfo*)malloc(sizeof(ServiceInfo));  memset(pServiceInfo->pNext, 0, sizeof(ServiceInfo));    pServiceInfo = pServiceInfo->pNext; }/*if zero*/   avahi_address_snprint(&addrStr[0], AVAHI_ADDRESS_STR_MAX, pAvahiAddress);  pServiceInfo->pServiceType = strndup(pType, MAX_STR_LEN); pServiceInfo->pServiceName = strndup(pServiceName, MAX_STR_LEN); pServiceInfo->pDomainName = strndup(pDomain, MAX_STR_LEN); pServiceInfo->pInterface = strndup(if_indextoname(interface, (char*)pServiceName), MAX_STR_LEN); pServiceInfo->pProtocolNumber = strndup(avahi_proto_to_string(protocol), MAX_STR_LEN); pServiceInfo->pAddr = strndup(&addrStr[0], MAX_STR_LEN); pServiceInfo->pHostName = strndup(pHostName, MAX_STR_LEN); pServiceInfo->port = port; if(NULL != pText) {  char *pAvahiText = avahi_string_list_to_string(pText) ;  pServiceInfo->pText = strndup(pAvahiText, MAX_STR_LEN);  avahi_free(pAvahiText);  }/*if NULL != pText*/ #if(0) printf(    "Service Type: %s\n"   "Service Name: %s\n"   "Domain Name: %s\n"   "Interface: %s %s\n"   "Address: %s/%s:%u\n"   "TEXT Data: %s\n"   ,   pServiceInfo->pServiceType,   pServiceInfo->pServiceName,    pServiceInfo->pDomainName,    pServiceInfo->pInterface, pServiceInfo->pProtocolNumber,   pServiceInfo->pAddr, pServiceInfo->pHostName, pServiceInfo->port,   NULL == pServiceInfo->pText ? "\x1B[1;36m" "NULL" "\x1B[0m": pServiceInfo->pText          );   #endif     if (NULL != pResolver)  avahi_s_service_resolver_free(pResolver); }/*ServiceResolverCallback*/  void ServiceBrowserCallback( AvahiSServiceBrowser *pBrower, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *pServiceName, const char *pServiceType, const char *pDomainName, AvahiLookupResultFlags flags, void *pUserdata) { AvahiSServiceResolver *pResolver; //printf("__FUNCTION__ = %s\n", __FUNCTION__); if(NULL == pServiceName || NULL == pServiceType  || NULL == pDomainName)  {  return; }/*if NULL*/ /*  TODO:   a service filter should be put in here.  */  pResolver = avahi_s_service_resolver_new(pServer, interface, protocol, pServiceName,  pServiceType, pDomainName, AVAHI_PROTO_UNSPEC, 0, ServiceResolverCallback,  (void*)pUserdata);}/*ServiceBrowserCallback*/void ServiceTypeBrowserCallback(  AvahiSServiceTypeBrowser *pTypeBrower, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *pType, const char *pDomain, AvahiLookupResultFlags flags,  void *pUserdata) {  assert(pTypeBrower); switch (event)  { case AVAHI_BROWSER_FAILURE: printf("AVAHI_BROWSER_FAILURE\n"); avahi_simple_poll_quit(pSimplePoll); break; case AVAHI_BROWSER_NEW: //printf("AVAHI_BROWSER_NEW\n"); {    /* We ignore the returned resolver object. In the callback     function we free it. If the server is terminated before     the callback function is called the server will free     the resolver for us. */               if( NULL == avahi_s_service_browser_new(pServer, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,    pType, pDomain, 0, ServiceBrowserCallback, pUserdata))  {   fprintf(stderr, "error in avahi_s_service_browser_new\n");  }/*if NULL*/ }/*local Variables*/ break; case AVAHI_BROWSER_REMOVE: //printf("AVAHI_BROWSER_REMOVE\n"); break; case AVAHI_BROWSER_ALL_FOR_NOW: //printf("AVAHI_BROWSER_ALL_FOR_NOW\n"); avahi_simple_poll_quit(pSimplePoll); break;    case AVAHI_BROWSER_CACHE_EXHAUSTED: //printf("AVAHI_BROWSER_CACHE_EXHAUSTED\n");  break; }/*switch event*/}/*service_type_browser_callback*/void TimeoutCallback(AvahiTimeout *pTimeout, void *pUserdata) { printf("__FUNCTION__ = %s\n", __FUNCTION__);  avahi_simple_poll_quit(pSimplePoll);}/*TimeoutCallback*/int MulticastDomainNameSystemServiceDiscover(int *pNumber, ServiceInfoList *pServiceInfoList,  int maxTimeoutInMilliSec){ AvahiServerConfig config; AvahiSServiceTypeBrowser *pServiceTypeBrowser; ServiceInfo *pServiceInfoHead; int error; int i; int ret; struct timeval tv;   pSimplePoll = NULL; pServiceTypeBrowser = NULL; *pNumber = 0; pServiceInfoHead = pServiceInfoList; if(NULL == pServiceInfoHead) {  fprintf(stderr, "ServiceInfo should be initialized \n");  ret = -1;  goto flag_Fail; }/*if */ avahi_set_allocator(NULL); pSimplePoll = avahi_simple_poll_new(); if (NULL == pSimplePoll)  {  fprintf(stderr, "Failed to create simple poll object.\n");  ret = -2;  goto flag_Fail; }/*if*/ avahi_server_config_init(&config); config.publish_hinfo = config.publish_addresses = FALSE; config.publish_domain = config.publish_workstation = FALSE;  pServer = avahi_server_new(avahi_simple_poll_get (pSimplePoll), &config, NULL, NULL, &error); avahi_server_config_free(&config);   if (NULL == pServer)  {  fprintf(stderr, "Failed to create server : %s\n", avahi_strerror(error));  ret = -3;  goto flag_Fail; }/*if NULL == pServer*/ pServiceTypeBrowser = avahi_s_service_type_browser_new(pServer,   AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, 0,   ServiceTypeBrowserCallback, pServiceInfoHead); if(NULL == pServiceTypeBrowser) {  fprintf(stderr, "Failed to create brower \n");  ret = -4;  goto flag_Fail; }/*if NULL == pServiceTypeBrowser*/ avahi_simple_poll_get(pSimplePoll)->timeout_new(  avahi_simple_poll_get(pSimplePoll),  avahi_elapse_time(&tv, maxTimeoutInMilliSec, 0),  TimeoutCallback,  NULL);            /* Run the main loop */ avahi_simple_poll_loop(pSimplePoll);  if( 0 == memcmp(pServiceInfoHead, &unUsedServiceInfoMemHead[0],   sizeof(unUsedServiceInfoMemHead)) ) {  goto flag_Fail ;  }/*if no service found*/ {/*count  the list length*/    ServiceInfo *pServiceInfo;  pServiceInfo = pServiceInfoHead;  *pNumber = 0;    while(NULL != pServiceInfo)  {   pServiceInfo = pServiceInfo->pNext;   (*pNumber)++;  }/*NULL != pServiceInfo*/ }/*local variable */     ret = *pNumber; flag_Fail: if(NULL != pServer)  avahi_server_free(pServer); pServer = NULL; if(NULL != pSimplePoll)    avahi_simple_poll_free(pSimplePoll); pSimplePoll = NULL; return ret;}/*MulticastDomainNameSystemServiceDiscover*/#define TIMEOUT_VALUE      (5000)int main(int argc, char *argv[]){  int n, i; ServiceInfoList *pServiceInfoList; struct timespec timeStart, timeEnd;  pServiceInfoList = CreateServiceInfoList(); clock_gettime(CLOCK_REALTIME, &timeStart); MulticastDomainNameSystemServiceDiscover(&n, pServiceInfoList, TIMEOUT_VALUE);  clock_gettime(CLOCK_REALTIME, &timeEnd); printf("%d services have been found\n", n); if(0 != n ) {  ServiceInfo *pServiceInfo;  pServiceInfo = pServiceInfoList;    while(NULL != pServiceInfo)  {     printf("\n");   printf(      "Service Type: %s\n"     "Service Name: %s\n"     "Domain Name: %s\n"     "Interface: %s %s\n"     "Address: %s/%s:%u\n"     "TEXT Data: %s\n"     ,     pServiceInfo->pServiceType,     pServiceInfo->pServiceName,      pServiceInfo->pDomainName,      pServiceInfo->pInterface, pServiceInfo->pProtocolNumber,     pServiceInfo->pAddr, pServiceInfo->pHostName, pServiceInfo->port,     NULL == pServiceInfo->pText ? "\x1B[1;36m" "NULL" "\x1B[0m": pServiceInfo->pText            );           pServiceInfo = pServiceInfo->pNext;    }/*while*/  printf("\n"); }/*if 0 != n*/ {  int  elaspeTimeMicroSec;   elaspeTimeMicroSec = (timeEnd.tv_sec - timeStart.tv_sec) *1e6 +      (timeEnd.tv_nsec - timeStart.tv_nsec) /1e3;  printf("discover cast %u msec\n", elaspeTimeMicroSec/1000);   }      DestroyServiceInfoList(pServiceInfoList);  return 0;}/*main*/

 


The corresponding Makefile be :


CC := gccCXX := g++INC := -I../built/include LIB_PATH := ../built/libLIB += $(LIB_PATH)/libavahi-core.aLIB += $(LIB_PATH)/libavahi-common.aCFLAGS := -gall : $(CC) $(CFLAGS) $(INC)  sandbox-discover-standalone.c  $(LIB)  -o sandbox-discover-standalone clean : rm sandbox-discover-standalone -rf


 
The code is very easy to be  modified as library form.

五. 
   Discover result:


./sandbox-discover-standaloneJoining mDNS multicast group on interface wlan0.IPv6 with address fe80::6267:20ff:fea5:44ae.New relevant interface wlan0.IPv6 for mDNS.Joining mDNS multicast group on interface wlan0.IPv4 with address 192.168.13.7.New relevant interface wlan0.IPv4 for mDNS.Network interface enumeration completed.Leaving mDNS multicast group on interface wlan0.IPv6 with address fe80::6267:20ff:fea5:44ae.Leaving mDNS multicast group on interface wlan0.IPv4 with address 192.168.13.7.n = 1Service Type: _http._tcpService Name: GAIGER NETEYE CS2230Domain Name: localInterface: wlan0 IPv4Address: 192.168.13.107/NETEYECS2230-001A97018882.local:80TEXT Data: NULL


Of course, the avahi-daemon be in stop status.


0 0