今天在解决产品BUG时碰到这样一个问题,在设备启动的时候不断的按屏幕开关按键,由于屏幕开关按键是通过按键发送消息到QT底层来实现响应按键信息,当按键按下,而此时接收消息的程序还未启动,此时则一直停在这里等待返回,故导致整个系统无法正常启动。小小设置引出的大问题啊。
一、在recv接收数据时使用MSG_DONTWAIT标志,这将使某个单次接收操作为非阻塞方式,如下所示:
recv(sockfd, buffer, MAXBUF, MSG_DONTWAIT);
代码示例:
int byte = recv(sockfd, send_buffer, BUFSIZE, MSG_DONTWAIT);
if (byte > 0) {
printf("get %d message: %s\n", byte, char_send);
byte = 0;
} else if (byte < 0) {
if (errno == EAGAIN) {
errno = 0;
continue;
} else {
perror("recv failed");
exit(0);
}
}
errno是linux系统下保存当前错误状态的一个公共变量,在以非阻塞方式调用recv()函数返回时,如果没有数据可读,将修改errno变量的值为“EAGAIN”,表示recv读数据时,对方没有发送数据过来。即可以根据这一特殊情况,对无数据时做特殊处理。
二、设置socket文件描述符的属性为非阻塞,这将导致所有针对该文件描述符的操作都为非阻塞方式,分为以下三种:
(1)选用setsockopt
在send(),recv()过程中有时由于网络状况等原因,收发不能预期进行,可以设置收发时限:
int nNetTimeout = 1000; //1秒
发送时限
setsockopt( socket, SOL_S0CKET, SO_SNDTIMEO, ( char * )&nNetTimeout, sizeof( int ) );
接收时限
setsockopt( socket, SOL_S0CKET, SO_RCVTIMEO, ( char * )&nNetTimeout, sizeof( int ) );
(2)选用fcntl
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
(3)选用ioctl
struct ifreq ifr; //该结构体专门用于ioctl函数获取接口信息,具体声明请看<net/if.h>
ioctl(sockfd, FIONBIO, &ifr);
三、select多路复用技术,不只能解决非阻塞接收消息,还可以解决终端的阻塞问题。
select提供轮循等待的方式从多个文件描述小获取状态变化后的消息,声明如下:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
参数介绍:
参数nfds:指定要检测的文件描述符的范围,测试范围在0到nfds-1之间的文件描述符(nfds为这些文件描述符中的最大值加1)。
参数readfds:包含所有因状态变成可读而触发select()函数返回的文件描述符。
参数writefds:包含所有因状态变成可写而触发select()函数返回的文件描述符。
参数exceptfds:包含所有因状态发生特殊异常而触发select()函数返回的文件描述符。
参数timeout:表示超时时限。
针对文件描述符集合的操作如下:
#define FD_SET(fd, fdsetp) //把fd添加到fdsetp
#define FD_CLR(fd, fdsetp) //从fdsetp中删除fd
#define FD_ISSET(fd, fdsetp) //检测fdsetp中的fd是否为异常
#define FD_ZERO(fdsetp) //初始化fdsetp为空
返回值:
(1)、如果函数执行错误,将返回-1.
(2)、如果因超时而返回,即在timeout所描述的时间范围内没有任何描述符出现异常,则返回0.
(3)、如果因一个或多个文件描述符异常而返回,其返回值为产生异常的文件描述符数,并在相应的文件描述符集合中清除未产生异常的文件描述符记录。因此返回后可以根据文件描述符集合中的记录查找出是哪一个文件描述符返回。
基本示例:
如果想检测某个socket是否有数据可读,可以使用如下代码:
fd_set rdfds;
struct timeval timeout;
int ret;
FD_ZEOR(rdfds);
FD_SET(socket, &rdfds);
timeout.sec = 1;
timeout.usec = 500;
ret = select(socket+1, &rdfds, NULL, NULL, &tv);
if (ret < 0)
perror("select failed");
if (ret == 0)
printf("timeout!!\n);
else {
printf("ret = %d\n", ret);
if (FD_ISSET(socket, &rdfds))
recv(.,.....);
}
还在不断的完善中。。。