回调函数的实现
实现的功能:
ui界面通过回调函数取到ping任务所返回的数据。
1、需要创建一个定义函数指针的方式:
回调类型确定:typedef void (*VCD_PING_CALLBACK)(StVcdPingVar* ctx);
StVcdPingVar代表函数指针指向的函数(回调函数)所需要的参数,做完这一步,就可以用VCD_PING_CALLBACK去定义函数指针了。下面是回调函数参数类型:确定取回三个数据,延迟、丢包以及额外自定义数据。
typedef struct st_vcd_ping{
float delay;
int pkt_loss_rate;
gpointer priv;
}StVcdPingVar;
回调函数实现:很简单,取回的数据在此处打印即可,要是有界面程序可以将数据传入界面以显示,M_DBEUG是自己实现的printf,大家可以用printf代替。
static void vcd_ping_callback(StVcdPingVar *data) {
StVcdPingVar *ctx = (StVcdPingVar *)data;
M_DEBUG("vcd_ping_callback delay:%d,loss:%d\n", ctx->delay, ctx->pkt_loss_rate);
}
2、实现ping任务
总览:一般来说我们采取一个c++中的单例模式,启动一个ping的任务,用一个源文件和一个头文件实现,对外只公开ping任务开启和关闭的接口,并且在开启任务的时候将实现好的回调函数传入这个任务,待任务完成时自动调用这个函数以更新数据。
数据结构:一个单例所用的到的所有数据我们可以存放在一个静态全局的结构体中,这个g_ping_ctx只供这个文件使用(对外不显示),结构里面存放了线程句柄,线程结束标志位,回调函数指针,以及用户自定义数据等等。
typedef struct st_vcd_ping_linux{
VCD_PING_CALLBACK func;
GThread *thd;
gpointer priv;
gboolean bStop;
}VcdPingLinuxCtx;
static VcdPingLinuxCtx* g_ping_ctx;
开启ping任务: 下面是开启ping任务的入口,可以看到参数用VCD_PING_CALLBACK来接入。调用的时候只要将上面实现的vcd_ping_callback函数名传入即可。
static gboolean vcd_ping_linux_start(VCD_PING_CALLBACK func,gpointer priv){
if (g_ping_ctx)
vcd_ping_linux_stop();
M_DEBUG("vcd_ping_linux_start");
g_ping_ctx = g_malloc0(sizeof(VcdPingLinuxCtx));
g_ping_ctx->priv = priv;
g_ping_ctx->func = func;
g_ping_ctx->thd = g_thread_new("vcd_ping_linux", (GThreadFunc)GThreadFunc_ping, NULL);
return TRUE;
}
下面就是ping线程的实现方式:取回数据之后用g_ping_ctx->func(&ctx);调用回调函数即会在之前的M_DEBUG中显示了。
static gpointer GThreadFunc_ping(gpointer arg){
while(g_ping_ctx && !g_ping_ctx->bStop){
char* host = g_vcd_conf->ip;
if(!host) goto fail;
char cmd[100] = {0};
snprintf(cmd, sizeof(cmd), "ping -c 4 -W 1 %s", host);
FILE* pf = popen(cmd, "r");
if(!pf) goto fail;
const int len = 1024;
char data[1024] = {0};
int loss = -1;
float delay = -1;
while(fgets((char*)data, len, pf)){
//M_DEBUG("fgets:%s",data);
if(is_packet_loss_str(data))
loss = find_packet_loss(data);
if(is_avg_delay_str(data))
delay = find_avg_delay(data);
}
if(loss != -1 && delay != -1 && g_ping_ctx && g_ping_ctx->func){
StVcdPingVar ctx = {0};
ctx.delay = delay;
ctx.pkt_loss_rate = loss;
ctx.priv = g_ping_ctx->priv;
g_ping_ctx->func(&ctx);
}
pclose(pf);
continue;
fail:
sleep(1);
continue;
}
return FALSE;
}
最后给上任务结束函数:
static void vcd_ping_linux_stop(){
g_return_if_fail(g_ping_ctx != 0);
M_DEBUG("vcd_ping_linux_stop");
g_ping_ctx->bStop = TRUE;
system("killall ping");
g_thread_join(g_ping_ctx->thd);
g_free(g_ping_ctx);
g_ping_ctx = 0;
}
取数据细节函数:
static gboolean is_packet_loss_str(const char* data){
if(!data) return FALSE;
const char* sub = "packet loss";
if(strstr(data,sub)){
return TRUE;
}
return FALSE;
}
// rtt min/avg/max/mdev = 0.578/0.598/0.625/0.019 ms
static gboolean is_avg_delay_str(const char* data){
if(!data) return FALSE;
const char* sub = "rtt min/avg/max/mdev = ";
if(strstr(data,sub)){
return TRUE;
}
return FALSE;
}
static int find_packet_loss(const char* data){
if(!is_packet_loss_str(data)){
goto fail;
}
const char* sub = "received, ";
char* pStar = strstr(data,sub);
if(!pStar) goto fail;
pStar += strlen(sub);
char* pEnd = strstr(pStar,"%");
if(!pEnd) goto fail;
char tmp[10] = {0};
strncpy(tmp,pStar,pEnd-pStar);
int loss = atoi(tmp);
return loss;
fail:
return -1;
}
// rtt min/avg/max/mdev = 0.578/0.598/0.625/0.019 ms
static float find_avg_delay(const char* data){
if(!is_avg_delay_str(data)){
goto fail;
}
const char* sub = "rtt min/avg/max/mdev = ";
char* pStar = strstr(data,sub);
if(!pStar) goto fail;
// 0.578/0.598/0.625/0.019 ms
pStar += strlen(sub);
pStar = strstr(pStar,"/");
if(!pStar) goto fail;
// 0.598/0.625/0.019 ms
pStar = pStar + 1;
char* pEnd = strstr(pStar,"/");
if(!pEnd) goto fail;
char tmp[10] = {0};
strncpy(tmp,pStar,pEnd-pStar);
float delay = atof(tmp);
return delay;
fail:
return -1;
}