回調函數的實現
實現的功能:
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;
}