第一,如果使用空闲函数来处理socket程序,可能会造成数据接收不及时,特别是针对一些对数据及时性要求很高的程序。因为空闲函数只会在用户不对界面进行操作的时候(即系统空闲时)才进行。如果当前用户正在界面上执行某项耗时的操作,而此时有数据通过socket传送,那么该数据会等到用户的界面操作完成后才到达,造成了数据延时。那么应该怎么处理了,此时我们可以向GTK的主循环添加一个需要监视的socket描述符,然后让主循环对该socket进行扫描。这样做的效率在我看来是比使用空闲函数高的。具体使用方法:
1.完成socket连接。
2.使用g_io_channel_unix_new()生成一个全新的GChannel指针。注意:windows平台请使用g_io_channel_win32_new_socket()。
3.通过g_io_add_watch()将刚才的GChannel指针添加到需要检测的队列中。
#include <glib.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#define ADDR "192.169.18.252"
#define PORT 5038
int new_message(GIOChannel *channel,GIOCondition condition,gpointer data);
int main()
{
GMainLoop *loop;
GIOChannel *channel;
int sockfd;
struct sockaddr_in addr;
/*Create socket*/
sockfd=socket(AF_INET,SOCK_STREAM,0);
/*Init address*/
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port=htons(PORT);
addr.sin_addr.s_addr=inet_addr(ADDR);
/*connect to the server*/
if(connect(sockfd,(struct sockaddr *)&addr,sizeof(addr))==-1)
{
perror("connect");
return 1;
}
/*Get channel from socket*/
channel=g_io_channel_unix_new(sockfd);
/*add the channel to main loop*/
g_io_add_watch(channel,G_IO_IN|G_IO_HUP|G_IO_NVAL|G_IO_ERR,(GIOFunc)new_message,NULL);
/*Enter the main loop*/
loop=g_main_loop_new(NULL,FALSE);
g_main_loop_run(loop);
return 0;
}
int new_message(GIOChannel *channel,GIOCondition condition,gpointer data)
{
if(condition==G_IO_IN)
{
/*Something to read*/
char buffer[BUFSIZ];
int sockfd;
memset(buffer,0,sizeof(buffer));
sockfd=g_io_channel_unix_get_fd(channel);
recv(sockfd,buffer,BUFSIZ,0);
g_print("%s",buffer);
return TRUE;
}
else
{
/*Unable to read ,so remove the watch*/
g_print("closed");
return FALSE;
}
}
第二,在使用空闲函数时,如果空闲函数可能存在阻塞的情况,例如,连接超时。当空闲函数超时使,程序会一直阻塞,直到空闲函数完成。为了避免这种情况,必须使用多线程。如果直接按照网上的一些例子在线程中通过使用gdk_threads_enter()和gdk_threads_leave()来更新界面,这回导致程序移植问题(在我的测试中,linux系统上是不存在问题,但是如果使用mingw则界面会出现卡钝现象)。为了解决这个问题,我的建议是只在线程中处理数据(例如treeview的model数据等),在需要处理界面时,通过gdk_threads_idle_add()函数添加界面处理。这样就解决了程序的移植和后台更新数据等问题。