之前为了方便测试部门保存一些测试数据,需要临时开发一个简单的nanomsg server,与板子端的client在局域网内进行通信,接收client数据并保存。关于nanomsg的简单使用介绍可查看往期文章:一个实用通信库的简单使用分享

 

作为server端,需要绑定电脑的IP,不同的电脑IP是不一样的,所以使用不同的电脑时需要修改IP才能正常使用这个server程序。

 

在不需要重新编译程序的情况下,有如下两种方法可以满足这个需求:

 

  • 把IP写在配置文件里,比如ini格式的配置文件里,然后server程序读取配置文件里的IP,再进行绑定。server程序自动获取IP地址并绑定。

 

下面分别使用这两种方法:

 

从配置文件中读取IP地址

配置文件的格式有很多,如JSON、INI等。这里我选用的是INI格式的配置文件,.ini 文件是 Initialization File 的缩写,即初始化文件。INI文件由节、键、值组成,注解使用分号表示(;)。例如:

 

  [Section1 Name]  KeyName1_1=value1_1  ;这是注释  KeyName1_2=value1_2  [Section2 Name]  KeyName2_1=value2_1  KeyName2_2=value2_2  

 

这里我们使用inih解析器来对INI文件进行解析。

 

inih:一个C 语言编写的 INI 文件解析器。

 

inih解析器的地址:

 

https://github.com/benhoyt/inih

 

 

同时,inih解析器也已经被收录于大杂烩资源汇总贴中:

 

https://gitee.com/zhengnianli/EmbedSummary

 

inih的使用很简单,下面一起来看一下。

 

下载得到的inih内容如图所示:

 

 

ini.cini.h 放到我们的工程下即可。这里我们使用一个测试工程:

 

 

同时,新建一个 ip.ini 文件存放于工程目录下。ip.ini 文件的内容如:

 

  [ip]                ;Section1  ip_addr = 192.168.1.103    [test]              ;Section2  name = ZhengN         num  = 66  

 

下面我们编写代码test.c来解析这个文件:

 

  // 微信公众号:嵌入式大杂烩  #include   #include   #include   #include "ini.h"    typedef struct  {      const char* ip_addr;      const char* name;      int num;  } configuration;    static int handler(void* user, const char* section, const char* name,                     const char* value)  {      configuration* pconfig = (configuration*)user;        #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0      if (MATCH("ip", "ip_addr"))       {          pconfig->ip_addr = strdup(value);      }       else if (MATCH("test", "name"))       {          pconfig->name = strdup(value);      }       else if (MATCH("test", "num"))       {          pconfig->num = atoi(value);      }       else       {          return 0;  /* unknown section/name, error */      }        return 1;  }    int main(int argc, char* argv[])  {      configuration config;      config.ip_addr = NULL;  /* set defaults */      config.name = NULL;      config.num = 0;        if (ini_parse("ip.ini", handler, &config) < 0)       {          printf("Can't load 'ip.ini'\n");          return 1;      }      printf("Config loaded from 'ip.ini': ip_addr = %s, name = %s, num = %d\n",          config.ip_addr, config.name, config.num);        if (config.ip_addr)          free((void*)config.ip_addr);      if (config.name)          free((void*)config.name);        return 0;  }  

 

解析方法很简单(可参考 inih/examples 下的demo,我们这里也是模仿这个demo来做解析的):

 

构造一个配置结构体 configuration ,定义一个configuration结构体用于保存我们解析的数据,结构体里面的成员就是我们需要解析的INI文件里的各个键。例如,我们的ip.ini文件里有ip_addr、name、num这三个键,结构体里的成员表示的就是这三个键。

 

定义一个handler回调函数,用于处理解析过程。解析过程也很简单,匹配Section Name及Key Name,然后取出值即可。

 

调用 ini_parse 函数对INI文件进行解析。

 

其中,handler函数里调用了一个 strdup() 函数及 atoi() 函数。

 

  • strdup()函数是c语言中常用的一种字符串拷贝库函数,一般和free()函数成对出现,,因为strdup()在内部调用了malloc()函数为变量分配内存。atoi()函数(ascii to integer)是把字符串转换成整型数的一个函数。

 

编译、运行:

 

自动获取IP地址

我们可以使用 getifaddrs() 函数来获取。getifaddrs()函数用于获取网卡信息,包括IP、掩码、广播地址等信息。

 

getifaddrs()函数原型:

 

  int getifaddrs (struct ifaddrs **__ifap);  

 

  • __ifap为获取得到的网卡信息。

 

用getifaddrs()函数获取得到的IP格式为 数值格式(numeric),需要转成 表达格式(presentation)inet_ntop() 函数可以满足这个需求。

 

inet_ntop()函数是随IPv6出现的函数,对于IPv4地址和IPv6地址都适用,函数中p和n分别代表 表达格式(presentation)数值格式(numeric) 。IP地址的表达格式是ASCII字符串,数值格式则是存放到套接字地址结构的二进制值。我们这里需要的就是ASCII字符串形式的IP地址。

inet_ntop()函数原型:

 

 

  const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len);  

 

family参数:既可以是AF_INET(ipv4)也可以是AF_INET6(ipv6)。

 

addrptr参数:addrptr指针指向数值格式的IP。

 

strptr参数:strptr指向表达格式的IP,调用者必须为目标存储单元分配内存并指定其大小。

 

len参数:容纳表达格式的长度。

 

返回值:若成功则为指向表达格式的指针,若出错则为NULL。

 

下面看看实例代码:

 

  // 微信公众号:嵌入式大杂烩  #include   #include   #include   #include   #include     char *get_ip_addr(void)  {   static char ip_str[256] = {0};   struct ifaddrs *ifaddrs_struct = NULL;   void *addrptr = NULL;      if (getifaddrs(&ifaddrs_struct) != 0)   {    printf("getifaddrs error!\n");    return NULL;   }      struct ifaddrs *tmp_ifaddrs = ifaddrs_struct;   while (tmp_ifaddrs != NULL)   {          // IPv4    if (tmp_ifaddrs->ifa_addr->sa_family == AF_INET)    {              #define INET_ADDR_STR_LEN  256     char addr_buf[INET_ADDR_STR_LEN];     addrptr = &((struct sockaddr_in*)tmp_ifaddrs->ifa_addr)->sin_addr;     if (inet_ntop(AF_INET, addrptr, addr_buf, INET_ADDR_STR_LEN) == NULL)     {      printf("inet_ntop error!\n");      return NULL;     }              if (strlen(addr_buf) < sizeof(ip_str) - strlen(ip_str))              {                  strncat(ip_str, addr_buf, strlen(addr_buf));                  strncat(ip_str, ";", strlen(";"));              }         }          // IPv6          else if (tmp_ifaddrs->ifa_addr->sa_family == AF_INET6)          {                        }        tmp_ifaddrs = tmp_ifaddrs->ifa_next;   }      freeifaddrs(ifaddrs_struct);        return ip_str;  }    int main(int argc, char** argv)  {      printf("hello world!\n");      char *ip_addr = get_ip_addr();      printf("ip addr : %s\n", ip_addr);        return 0;  }  

 

编译、运行: