Use Avahi as a library for Discovering mDNS Service

55469 ワード

Avahi(http://www.avahi.org/)is an implement of
multicast Domain Name System(mDNS)in Linux/Android.mDNS is a(local)network service broadcasting/discovering mechaism:for example,  the IP press or IP cameraas、  could be found via mDNS functions.
    Avahi libries is easury to be use relatively.  The re are  adequate examples to illuminate how to use those API.In here I would like to make them be ample.I would explin about discovering part in this artic.
Note:avah i brower would depend on avah-daemen、this post's goal is to remove the dependency.
  ゼロ. 
      Download the avah,for me,I use version 0.69.31.The n uncomppress the tar ball.
 1つ
..。
     Go into the avah folder、configre the avah i withgtk 2、qt 4 anddbus: 
./configure --prefix=$PWD/built --disable-qt3 --disable-gtk3 --disable-mono --enable-tests --enable-core-docs --disable-python --disable-pygtk
     After the configration has be done、type make and make install.
二. 
    Go to the built/bin folder、execute the avah-browse:
You would get the simillar 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 avah-daemen and re-execute the avah-brow:
sudo service avahi-daemon stop
avahi-daemon stop/waiting

./avahi-browse -a
Failed to create client object:            
the Chinese words read「the daement programming is not on on running」
That is、the
avah-brow e depends on
avah-daemen,which be the
provider of service of discover.
But、If you run the avah-discover-standalne、there is a window for reporting discovered service:
The GAIGER NETEYE CS 2230 be a IP camera in the same lan domain.
     The other services discovered by
avah-browe are local services provided by
avah-daemen,those would vanish when
avah-daemen stopped.
    You could
studywhat is the different between
avah-browe and avah-discover-standowne、  the source codes
be in folder examples/client-browservices.cand  avah-discover-standowne/main.cs respively.
   For most useful and potable for libries usage,the
avah-discovere-standowne/main.cn shound be removed from UI,and be independent of the others libries.
三.
   Re-configure or uncomppress the same tar ball again、use the configure argments:
./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-j 2&make install.
That is for
minimization the dependence.
四.
   Create a folder be under avah i source root、the folder name be sadybox code for me.
   my standowne code be inside the folder:
サンダーボックス-discover-standowne.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 value


static 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 = NULL


void 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
", __FUNCTION__);
if (AVAHI_RESOLVER_FOUND != event) { printf("NOT AVAHI_RESOLVER_FOUND
"); 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
" "Service Name: %s
" "Domain Name: %s
" "Interface: %s %s
" "Address: %s/%s:%u
" "TEXT Data: %s
" , 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
", __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
"); avahi_simple_poll_quit(pSimplePoll); break; case AVAHI_BROWSER_NEW: //printf("AVAHI_BROWSER_NEW
");
{ /* 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
"); }/*if NULL*/ }/*local Variables*/ break; case AVAHI_BROWSER_REMOVE: //printf("AVAHI_BROWSER_REMOVE
");
break; case AVAHI_BROWSER_ALL_FOR_NOW: //printf("AVAHI_BROWSER_ALL_FOR_NOW
");
avahi_simple_poll_quit(pSimplePoll); break; case AVAHI_BROWSER_CACHE_EXHAUSTED: //printf("AVAHI_BROWSER_CACHE_EXHAUSTED
");
break; }/*switch event*/ }/*service_type_browser_callback*/ void TimeoutCallback(AvahiTimeout *pTimeout, void *pUserdata) { printf("__FUNCTION__ = %s
", __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
"); 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.
"); 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
", 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
"); 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); if(0 != n ) { ServiceInfo *pServiceInfo; pServiceInfo = pServiceInfoList; while(NULL != pServiceInfo) { printf("
"); printf( "Service Type: %s
" "Service Name: %s
" "Domain Name: %s
" "Interface: %s %s
" "Address: %s/%s:%u
" "TEXT Data: %s
" , 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("
"); }/*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
", elaspeTimeMicroSec/1000); } DestroyServiceInfoList(pServiceInfoList); return 0; }/*main*/
 
The cores ponding Makefile be:
CC := gcc CXX := g++ INC := -I../built/include LIB_PATH := ../built/lib LIB += $(LIB_PATH)/libavahi-core.a LIB += $(LIB_PATH)/libavahi-common.a CFLAGS := -g all : $(CC) $(CFLAGS) $(INC) sandbox-discover-standalone.c $(LIB) -o sandbox-discover-standalone clean : rm sandbox-discover-standalone -rf
  The code is very easury to be  modified as library form.5.     Discover result:
./sandbox-discover-standalone Joining 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 = 1 Service Type: _http._tcp Service Name: GAIGER NETEYE CS2230 Domain Name: local Interface: wlan0 IPv4 Address: 192.168.13.107/NETEYECS2230-001A97018882.local:80 TEXT Data: NULL
Of course,the avah-daemen be in stop status.