分享

Socket connect阻塞导致连接超时过长

 补丁牛仔裤 2024-05-21 发布于广东

问题描述:

在项目中,机械臂作为服务端,软件程序编写一个客户端进行连接,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 处。

分析连接异常的几种情况:

  1. 服务端出现异常时,connect连接会立刻返回-1,表示连接失败;
  2. 客户端出现异常时,有以下两种情况:
    ● 物理网络未连接(网线连接异常),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

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多