套接字超时处理的三种方法

本文介绍了处理套接字超时的三种方法:一是利用SIGALRM信号结合connect设置超时,当alarm信号触发时中断connect;二是使用select为accept设置超时,确保等待连接不会无限期阻塞;三是通过setsockopt设置SO_RCVTIMEO,为recvfrom提供超时功能,避免在接收数据时阻塞。这些方法有助于优化网络通信的效率和稳定性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、使用SIGALRM为connect 设置超时

signal  捕捉到alarm信号之后会中断connect函数,导致返回值为EINTR,因此可提前设置connect超时返回,而不用等到最长 75s的connect原始超时时长

1、Signal 返回值 为  typede void Sigfunc(int);    -- 可以保存和恢复旧的信号处理函数

2、alarm(); 可覆盖之前的定时,返回值为 之前定时的剩余时间,返回0表示之前未设置定时

3、connect()  返回 < 0,设置为失败,超时; 若成功, == 0;connect失败后需关闭close(sockfd);并重新 socket() 获取套接字

/* include connect_timeo */
#include    "unp.h"

static void connect_alarm(int);

int
connect_timeo(int sockfd, const SA *saptr, socklen_t salen, int nsec)
{
    Sigfunc *sigfunc;
    int     n;  

    sigfunc = Signal(SIGALRM, connect_alarm);
    if (alarm(nsec) != 0)
        err_msg("connect_timeo: alarm was already set");

    if ( (n = connect(sockfd, saptr, salen)) < 0) {
        close(sockfd);
        if (errno == EINTR)
            errno = ETIMEDOUT;
    }   
    alarm(0);                   /* turn off the alarm */
    Signal(SIGALRM, sigfunc);   /* restore previous signal handler */

    return(n);
}

static void
connect_alarm(int signo)
{
    return;     /* just interrupt the connect() */
}
/* end connect_timeo */

void
Connect_timeo(int fd, const SA *sa, socklen_t salen, int sec)
{
    if (connect_timeo(fd, sa, salen, sec) < 0)
        err_sys("connect_timeo error");             // 出错或者超时 都 < 0

    // 连接成功 connect_timeo 返回 0
}

二、使用select为accept 设置超时

fd_set fdset;
struct timeval tv;

FD_ZERO(&fdset);
FD_SET(info->sockfd, &fdset);
    
tv.tv_sec = 9;
tv.tv_usec = 0;

iRet = select(info->sockfd + 1, &fdset, NULL, NULL, &tv);
if (iRet > 0)
{
    /*成功则为非负描述符*/
    connfd = accept(info->sockfd, (struct sockaddr *)&cliAddr, &iAddrLen);
    if (connfd < 0)
    {
        printf("[%s %d] accept failed. \r\n", __FILE__, __LINE__);
        return -1;
    }
    else
    {
        info->connfd = connfd;
        printf("[%s %d] accept succ. connfd = %d\r\n", __FILE__, __LINE__, connfd);
        return 0;
    }
}
else
{
    printf("[%s %d] select timeout or Failed\r\n", __FILE__, __LINE__);
    return -1;
}

 

 

三、使用setsockopt设置SO_RCVTIMEO为recvfrom设置超时

errno == EWOULDBLOCK

#include    "unp.h"

void
dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
    int             n;  
    char            sendline[MAXLINE], recvline[MAXLINE + 1]; 
    struct timeval  tv; 

    tv.tv_sec = 5;
    tv.tv_usec = 0;
    Setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

    while (Fgets(sendline, MAXLINE, fp) != NULL) {

        Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);

        n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
        if (n < 0) {
            if (errno == EWOULDBLOCK) {
                fprintf(stderr, "socket timeout\n");
                continue;
            } else
                err_sys("recvfrom error");
        }   

        recvline[n] = 0;    /* null terminate */
        Fputs(recvline, stdout);
    }   
}

 

 

 

 

 

 

 

### Windows 套接字连接超时处理方法 在Windows环境下,套接字编程中常见的问题是设置连接超时时间。为了实现这一功能,可以通过`setsockopt`函数配置套接字选项,并结合非阻塞模式或异步操作完成。 #### 设置连接超时方法 一种常见的方式是通过将套接字设为非阻塞模式并手动控制超时逻辑。以下是具体实现方式: 1. **创建套接字** 使用`WSASocket`或其他API创建一个套接字实例。 2. **切换至非阻塞模式** 调用`ioctlsocket`函数将套接字设置为非阻塞状态。此函数允许应用程序更改文件句柄的行为[^1]。 3. **尝试建立连接** 调用`connect`函数发起连接请求。如果处于非阻塞模式,则该调用会立即返回错误码`WSAEWOULDBLOCK`表示正在处理连接。 4. **轮询连接状态** 利用`select`函数检测套接字的状态变化,或者直接查询错误代码判断是否成功建立了连接。 5. **恢复阻塞模式(可选)** 如果后续不需要非阻塞行为,可以在确认连接完成后重新启用阻塞模式。 #### 示例代码 (C语言) 以下是一个完整的示例代码展示如何在Windows环境中设置套接字连接超时: ```c #include <winsock2.h> #include <stdio.h> #pragma comment(lib, "ws2_32.lib") int main() { WSADATA wsaData; SOCKET ConnectSocket = INVALID_SOCKET; struct addrinfo *result = NULL, hints; // 初始化 WinSock if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("WSAStartup failed with error: %ld\n", WSAGetLastError()); return 1; } ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6 hints.ai_socktype = SOCK_STREAM; // TCP socket hints.ai_protocol = IPPROTO_TCP; // Resolve the server address and port const char* serverAddress = "192.168.1.1"; const char* portNumber = "80"; if (getaddrinfo(serverAddress, portNumber, &hints, &result) != 0) { printf("getaddrinfo failed with error: %d\n", WSAGetLastError()); WSACleanup(); return 1; } // Create a SOCKET for connecting to server ConnectSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); if (ConnectSocket == INVALID_SOCKET) { printf("Error at socket(): %ld\n", WSAGetLastError()); freeaddrinfo(result); WSACleanup(); return 1; } u_long iMode = 1; // 非阻塞模式 ioctlsocket(ConnectSocket, FIONBIO, &iMode); // 尝试连接 int connectResult = connect(ConnectSocket, result->ai_addr, (int)result->ai_addrlen); if (connectResult == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK) { fd_set writefds; FD_ZERO(&writefds); FD_SET(ConnectSocket, &writefds); timeval timeout; timeout.tv_sec = 5; // 设置超时时间为5秒 timeout.tv_usec = 0; int selectRet = select(0, NULL, &writefds, NULL, &timeout); if (selectRet > 0) { // 成功连接 printf("Connection established.\n"); } else if (selectRet == 0) { // 超时 printf("Timeout occurred on connection attempt.\n"); closesocket(ConnectSocket); ConnectSocket = INVALID_SOCKET; } else { // 错误发生 printf("Select function failed with error: %d\n", WSAGetLastError()); closesocket(ConnectSocket); ConnectSocket = INVALID_SOCKET; } } else if (connectResult == SOCKET_ERROR) { printf("Failed to initiate connection: %d\n", WSAGetLastError()); closesocket(ConnectSocket); ConnectSocket = INVALID_SOCKET; } freeaddrinfo(result); if (ConnectSocket != INVALID_SOCKET) { // 可在此处执行其他网络操作... closesocket(ConnectSocket); } WSACleanup(); return 0; } ``` 以上代码实现了基于非阻塞模式下的连接超时机制。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值