У меня проблема при написании графического интерфейса GTK + для управления другими процессами в Linux. Я определенно не эксперт в GTK +, и я не могу решить эту проблему.
Я пытаюсь написать приложение GTK +, которое должно запускать другие процессы (в частности, iPerf - программа измерения сети - клиент и сервер iPerf, управление которыми осуществляется с помощью system()
и popen()
/ pclose()
, в зависимости от кнопок, которые нажимают пользователи.
Есть несколько кнопок, связанных с запуском клиента, и две кнопки для запуска и остановки сервера, которые вызывают их соответствующие обратные вызовы.
Кнопка запуска сервера, в частности, вызывает обратный вызов, который отвечает за запуск потока, который должен считывать данные с сервера (довольно асинхронно) и соответствующим образом обновлять часть графического интерфейса, в то время как графический интерфейс должен реагировать на выполнение. другие операции (например, запуск клиента).
В частности, iPerf настроен на вывод новых данных каждые 1 с, и каждая информация находится в каждой строке, возвращаемой iPerf, каждую секунду.
Я попытался прочитать данные с сервера, используя popen()
.
Если я запускаю функцию serverParserIdle()
(об этом сообщается ниже) из-за обратного вызова GTK +, используя gdk_threads_add_idle()
, она работает, но с двумя большими проблемами, мешающими нормальной работе программы:
1) Выход iPerf буферизируется с помощью popen()
, и данные не анализируются в режиме реального времени, как должна делать программа
2) Поток serverParserIdle()
блокирует графический интерфейс, и я не могу одновременно выполнять другие операции, такие как запуск клиента, что мне нужно сделать
Пытаясь решить (2), я попытался изменить с gdk_threads_add_idle()
на gdk_threads_add_timeout(1000,...)
. В этом случае графический интерфейс больше не заблокирован, но popen
возвращает 0
, а сервер не запускается. Ты знаешь почему?
Что я могу сделать, чтобы решить все проблемы, перечисленные выше?
Это функция serverParserIdle()
, упомянутая ранее:
static gboolean serverParserIdle(gpointer data) {
FILE *iperfFp;
char linebuf[STRSIZE_LINEBUF];
double goodput, final_goodput;
char unit_letter;
int total_datagrams, prev_total_datagrams=-1;
struct parser_data *parser_data_struct=data;
gchar *gput_label_str=NULL, *final_gput_label_str=NULL;
char first_char;
iperfFp=popen(parser_data_struct->cmd,"r"); //parser_data_struct->cmd contains a string containing the command to launch the iperf server "iperf -s -u -i 1 ..."
if(!iperfFp) {
// We enter here if gdk_threads_add_timeout(1000,...) is used to call serverParserIdle()
return FALSE;
}
while(fgets(linebuf,sizeof(linebuf),iperfFp)!=NULL) {
sscanf(linebuf,"%c %*s %*s %*f %*s %*f %*s %lf %c%*s %*f %*s %*s %d %*s",&first_char,&goodput,&unit_letter,&total_datagrams); // Parse useful data on this line
if(first_char!='[' || (unit_letter!='K' && unit_letter!='M')) {
// This is just to discrimate the useful lines
continue;
}
if(unit_letter=='K') {
goodput=goodput/1000;
}
// This is again a way to distinguish the last line of a client-server session from all the other lines
if(prev_total_datagrams!=-1 && total_datagrams>prev_total_datagrams*2) {
if(final_gput_label_str) {
g_free(final_gput_label_str);
}
// Update final goodput value in the GUI
final_goodput=goodput;
prev_total_datagrams=-1;
final_gput_label_str=g_strdup_printf("<b><span font=\"70\" foreground=\"blue\">%.2f</span></b>",goodput);
gtk_label_set_text(GTK_LABEL(parser_data_struct->gput_labels.final_gput_info_label),final_gput_label_str);
} else {
if(gput_label_str) {
g_free(gput_label_str);
}
prev_total_datagrams=total_datagrams;
// Update current goodput value in the GUI (every 1s only when a client is being connected to the server)
gput_label_str=g_strdup_printf("<b><span font=\"70\" foreground=\"#018729\">%.2f</span></b>",goodput);
gtk_label_set_text(GTK_LABEL(parser_data_struct->gput_labels.gput_info_label),gput_label_str);
}
//fflush(iperfFp); <- tried flushing, but it does not work
}
pclose(iperfFp);
g_free(gput_label_str);
g_free(final_gput_label_str);
return FALSE;
}
gdk_threads_add_idle()
или gdk_threads_add_timeout()
фактически вызываются из обратного вызова (start_server()
), который назначается кнопке в main()
с использованием:
g_signal_connect(button,"clicked",G_CALLBACK(start_server),&(data));
Заранее большое спасибо.