问题描述:
在项目中,机械臂作为服务端,软件程序编写一个客户端进行连接,connect连接出现有异常时客户端线程阻塞时间过长;
分析阻塞的原因:
connect()函数在三次握手的第二次返回; 当网络连接失败时,即客户端执行connect,发送SYN包给服务端,然后一直等待服务端回应的ACK包才会返回,若超时时间到达限制,则返回-1;(1、Linux 内核中对 connect 的超时时间限制是 75s。2、套接字默认为阻塞模式)
1、套接字执行 I/O 操作有阻塞和非阻塞两种模式。在阻塞模式下,在 I/O 操作完成前,执行操作的函数一直等候而不会立即返回,该函数所在的线程会阻塞在这里。相反,在非阻塞模式下,套接字函数会立即返回,而不管 I/O 是否完成,该函数所在的线程会继续运行。 2、客户端调用 connect() 发起对服务端的 socket 连接,如果客户端的 socket 描述符为阻塞模式,则connect() 会阻塞到连接建立成功或连接建立超时(Linux 内核中对 connect 的超时时间限制是 75s)。如果为非阻塞模式,则调用 connect() 后函数立即返回,如果连接不能马上建立成功(返回 -1),则 errno 设置为 EINPROGRESS,此时 TCP 三次握手仍在继续。此时可以调用 select() 检测非阻塞 connect 是否完成。select 指定的超时时间可以比 connect 的超时时间短,因此可以防止连接线程长时间阻塞在 connect 处。
分析连接异常的几种情况:
- 服务端出现异常时,connect连接会立刻返回-1,表示连接失败;
- 客户端出现异常时,有以下两种情况:
● 物理网络未连接(网线连接异常),connect连接会立刻返回-1,表示连接失败; ● 物理网络连接正常,即能获取IP地址,但网络连接失败,connect连接可能发生阻塞。
解决connect阻塞的办法:
1、信号处理函数设置阻塞超时控制
核心代码:
sigset(SIGALRM, u_alarm_handle);
alarm(30);//设置闹钟,30s后将发送SIGALRM信号,执行信号处理函数u_alarm_handle
//尝试连接控制器
int res1 = connect(_socketfd1,(struct sockaddr *)&tcp_client1,sizeof(tcp_client1));//IO socket
std::cout << res1 << std::endl;
alarm(0);//取消闹钟,不再发送SIGALRM信号
sigrelse(SIGALRM);
#include "sys/socket.h"
#include "sys/types.h"
#include "netinet/in.h"
#include "arpa/inet.h"
#include "fcntl.h"
using namespace std::chrono_literals;
_controller_ip = controller_ip;//"192.168.58.2";//控制器默认ip地址
std::cout << "开始创建状态反馈TCP socket" << std::endl;
_socketfd1 = socket(AF_INET,SOCK_STREAM,0);//状态获取端口只有TCP
if(_socketfd1 == -1){
std::cout << "错误: 创建socket失败!" << std::endl;
exit(0);//创建套字失败,丢出错误
}else{
std::cout << "创建状态反馈socket成功,开始连接控制器..." << std::endl;
struct sockaddr_in tcp_client1;
tcp_client1.sin_family = AF_INET;
tcp_client1.sin_port = htons(port1);//8083端口
tcp_client1.sin_addr.s_addr = inet_addr(_controller_ip.c_str());
sigset(SIGALRM, u_alarm_handle);
alarm(30);//设置闹钟,30s后将发送SIGALRM信号,执行信号处理函数u_alarm_handle
//尝试连接控制器
int res1 = connect(_socketfd1,(struct sockaddr *)&tcp_client1,sizeof(tcp_client1));//IO socket
std::cout << res1 << std::endl;
alarm(0);//取消闹钟,不再发送SIGALRM信号
sigrelse(SIGALRM);
}
void u_alarm_handle(int x)
{
std::cout << "错误:连接over time,程序退出!" << std::endl;
}
验证结果:connect发生阻塞后在设置的时间后执行信号处理函数u_alarm_handle
|