1 背景
写一个c++的TCP通信程序,使用了recv
接口。因为该接口默认为阻塞,因此除非检测到连接断开,否则线程会一直阻塞在那里。
现在需要将recv
接口增加一个超时,即在n秒内未能recv
任何数据,则跳出阻塞。
2 解决方法
解决方法可以有两种:
- 设置
socket
接受超时法:recv
本身可以设置超时(推荐)。 - 信号法:
recv
可以通过信号的方式解除阻塞。
2.1 设置socket超时
在建立连接前增加超时设置:
auto fd = socket(PF_INET, SOCKET_STREAM, 0);
if(-1 == fd){
return "error";
}
// 设置超时
struct timeval timeout = {3, 0}; // 这里设置3秒超时时间
setsocketopt(fd, SOL_SOCKET, SO_RCVTIMEO, (void*)&timeout, sizeof(struct timeval));
if (0 > connect(fd, (struct sockaddr*)&addr_in, sizeof(struct sockaddr))){
return "error";
}
如此就可以在接收不到任何数据后的3秒时间断开阻塞。
2.2 信号法
我们可以利用信号可以解除阻塞的机制进行,就比如我们利用SIGALRM
信号,可以使用alarm
函数在n
秒后触发信号:
#include <signal.h>
void handleSigAlarm(int sign_no){ }
int main(){
struct sigaction alarm_act;
bzero(&alarm_act,sizeof(sigaction));
alarm_act.sa_handler = handleSigAlarm;
alarm_act.sa_flags = SA_NOMASK; // 这里一定是NOMASK
sigaction(SIGALRM,&alarm_act,NULL);
//...
while(true){
alarm(3); // 这里设置3秒超时
auto ret = recv(....)
alarm(0); // 收到后复位
}
}
需要注意的是其中的sa_flags
:
- 如果其值为
SA_NOMASK
,则会中断已阻塞的函数,使程序继续往下执行,即略过阻塞。 - 如果其值为
SA_RESTART
,则会重启函数,也就是会复位继续在recv
上阻塞。