之前為了方便測(cè)試部門保存一些測(cè)試數(shù)據(jù),需要臨時(shí)開發(fā)一個(gè)簡(jiǎn)單的nanomsg server,與板子端的client在局域網(wǎng)內(nèi)進(jìn)行通信,接收client數(shù)據(jù)并保存。關(guān)于nanomsg的簡(jiǎn)單使用介紹可查看往期文章:一個(gè)實(shí)用通信庫的簡(jiǎn)單使用分享
作為server端,需要綁定電腦的IP,不同的電腦IP是不一樣的,所以使用不同的電腦時(shí)需要修改IP才能正常使用這個(gè)server程序。
在不需要重新編譯程序的情況下,有如下兩種方法可以滿足這個(gè)需求:
- 把IP寫在配置文件里,比如ini格式的配置文件里,然后server程序讀取配置文件里的IP,再進(jìn)行綁定。server程序自動(dòng)獲取IP地址并綁定。
下面分別使用這兩種方法:
從配置文件中讀取IP地址
配置文件的格式有很多,如JSON、INI等。這里我選用的是INI格式的配置文件,.ini 文件是 Initialization File
的縮寫,即初始化文件。INI文件由節(jié)、鍵、值組成,注解使用分號(hào)表示(;)。例如:
[Section1 Name]
KeyName1_1=value1_1 ;這是注釋
KeyName1_2=value1_2
[Section2 Name]
KeyName2_1=value2_1
KeyName2_2=value2_2
這里我們使用inih解析器來對(duì)INI文件進(jìn)行解析。
inih:一個(gè)C 語言編寫的 INI 文件解析器。
inih解析器的地址:
https://github.com/benhoyt/inih
同時(shí),inih解析器也已經(jīng)被收錄于大雜燴資源匯總貼中:
https://gitee.com/zhengnianli/EmbedSummary
inih的使用很簡(jiǎn)單,下面一起來看一下。
下載得到的inih內(nèi)容如圖所示:
把 ini.c
與 ini.h
放到我們的工程下即可。這里我們使用一個(gè)測(cè)試工程:
同時(shí),新建一個(gè) ip.ini
文件存放于工程目錄下。ip.ini
文件的內(nèi)容如:
[ip] ;Section1
ip_addr = 192.168.1.103
[test] ;Section2
name = ZhengN
num = 66
下面我們編寫代碼test.c來解析這個(gè)文件:
// 微信公眾號(hào):嵌入式大雜燴
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 = %dn",
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;
}
解析方法很簡(jiǎn)單(可參考 inih/examples
下的demo,我們這里也是模仿這個(gè)demo來做解析的):
構(gòu)造一個(gè)配置結(jié)構(gòu)體 configuration
,定義一個(gè)configuration結(jié)構(gòu)體用于保存我們解析的數(shù)據(jù),結(jié)構(gòu)體里面的成員就是我們需要解析的INI文件里的各個(gè)鍵。例如,我們的ip.ini文件里有ip_addr、name、num這三個(gè)鍵,結(jié)構(gòu)體里的成員表示的就是這三個(gè)鍵。
定義一個(gè)handler回調(diào)函數(shù),用于處理解析過程。解析過程也很簡(jiǎn)單,匹配Section Name及Key Name,然后取出值即可。
調(diào)用 ini_parse
函數(shù)對(duì)INI文件進(jìn)行解析。
其中,handler函數(shù)里調(diào)用了一個(gè) strdup()
函數(shù)及 atoi()
函數(shù)。
- strdup()函數(shù)是c語言中常用的一種字符串拷貝庫函數(shù),一般和free()函數(shù)成對(duì)出現(xiàn),,因?yàn)閟trdup()在內(nèi)部調(diào)用了malloc()函數(shù)為變量分配內(nèi)存。atoi()函數(shù)(ascii to integer)是把字符串轉(zhuǎn)換成整型數(shù)的一個(gè)函數(shù)。
編譯、運(yùn)行:
自動(dòng)獲取IP地址
我們可以使用 getifaddrs()
函數(shù)來獲取。getifaddrs()函數(shù)用于獲取網(wǎng)卡信息,包括IP、掩碼、廣播地址等信息。
getifaddrs()函數(shù)原型:
int getifaddrs (struct ifaddrs **__ifap);
- __ifap為獲取得到的網(wǎng)卡信息。
用getifaddrs()函數(shù)獲取得到的IP格式為 數(shù)值格式(numeric)
,需要轉(zhuǎn)成 表達(dá)格式(presentation)
。inet_ntop()
函數(shù)可以滿足這個(gè)需求。
inet_ntop()函數(shù)是隨IPv6出現(xiàn)的函數(shù),對(duì)于IPv4地址和IPv6地址都適用,函數(shù)中p和n分別代表 表達(dá)格式(presentation)
和 數(shù)值格式(numeric)
。IP地址的表達(dá)格式是ASCII字符串,數(shù)值格式則是存放到套接字地址結(jié)構(gòu)的二進(jìn)制值。我們這里需要的就是ASCII字符串形式的IP地址。
inet_ntop()函數(shù)原型:
const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
family參數(shù):既可以是AF_INET(ipv4)也可以是AF_INET6(ipv6)。
addrptr參數(shù):addrptr指針指向數(shù)值格式的IP。
strptr參數(shù):strptr指向表達(dá)格式的IP,調(diào)用者必須為目標(biāo)存儲(chǔ)單元分配內(nèi)存并指定其大小。
len參數(shù):容納表達(dá)格式的長(zhǎng)度。
返回值:若成功則為指向表達(dá)格式的指針,若出錯(cuò)則為NULL。
下面看看實(shí)例代碼:
// 微信公眾號(hào):嵌入式大雜燴
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ifaddrs.h>
#include <arpa/inet.h>
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 : %sn", ip_addr);
return 0;
}
編譯、運(yùn)行: