欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C/C++ Socket設(shè)置接收超時(shí)時(shí)間的多種方法

 更新時(shí)間:2024年01月24日 10:29:56   作者:Dontla  
網(wǎng)絡(luò)編程中經(jīng)常需要處理的一個(gè)問(wèn)題就是如何正確地處理Socket超時(shí),對(duì)于C/C++,有幾種常用的技術(shù)可以用來(lái)設(shè)置Socket接收超時(shí)時(shí)間,在這篇文章中,我們將詳細(xì)介紹如何在C/C++中設(shè)置Socket的非阻塞模式以及如何配置接收超時(shí)時(shí)間,需要的朋友可以參考下

C/C++ Socket設(shè)置非阻塞模式接收超時(shí)時(shí)間的多種方法

網(wǎng)絡(luò)編程中經(jīng)常需要處理的一個(gè)問(wèn)題就是如何正確地處理Socket超時(shí)。對(duì)于C/C++,有幾種常用的技術(shù)可以用來(lái)設(shè)置Socket接收超時(shí)時(shí)間。在這篇文章中,我們將詳細(xì)介紹如何在C/C++中設(shè)置Socket的非阻塞模式以及如何配置接收超時(shí)時(shí)間。

非阻塞模式(fcntl)

默認(rèn)情況下,Socket操作都是阻塞的。這意味著當(dāng)調(diào)用某個(gè)Socket函數(shù)時(shí)(例如recv),如果數(shù)據(jù)還未就緒,函數(shù)會(huì)阻塞等待,直到有數(shù)據(jù)可用為止。然而,在許多情況下,讓函數(shù)阻塞并不是最佳解決方案(容易造成卡死)。這時(shí),就需要使用非阻塞模式。

設(shè)置非阻塞模式

要將Socket設(shè)置為非阻塞模式,可以使用fcntl函數(shù)。以下是一段示例代碼:

int flags = fcntl(sock_fd, F_GETFL, 0);
fcntl(sock_fd, F_SETFL, flags | O_NONBLOCK);

上述代碼首先獲取了Socket當(dāng)前的文件狀態(tài)標(biāo)志,然后將O_NONBLOCK標(biāo)志位添加到文件狀態(tài)標(biāo)志中,最后使用F_SETFL命令將新的文件狀態(tài)標(biāo)志設(shè)置回Socket。此時(shí),Socket已經(jīng)處于非阻塞模式。

非阻塞模式下的接收超時(shí)

在非阻塞模式下,如果沒(méi)有數(shù)據(jù)可用,recv函數(shù)會(huì)立即返回一個(gè)錯(cuò)誤,并設(shè)置errno為EWOULDBLOCKEAGAIN。因此,可以通過(guò)檢查errno來(lái)確定是否超時(shí)。以下是一段示例代碼:

#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

#define MAX_RETRIES 5
#define SLEEP_DURATION 1000000 // One second
#define BUFFER_SIZE 1024

int retries = 0;
char buffer[BUFFER_SIZE];

while(retries < MAX_RETRIES) {
    memset(buffer, 0, sizeof(buffer)); // Clear the buffer
    ssize_t recv_status = recv(sock_fd, buffer, BUFFER_SIZE - 1, 0);

    if(recv_status < 0) {
        if(errno == EWOULDBLOCK || errno == EAGAIN) {
            usleep(SLEEP_DURATION);
            retries++;
        } else {
            perror("Error in recv"); // Print error message
            break;
        }
    } else if(recv_status == 0) { // Socket is closed
        printf("Socket is closed by the peer\n");
        break;
    } else { 
        // Handle received data
        printf("Received data: %s\n", buffer);
        break;
    }
}

if(retries >= MAX_RETRIES) {
    printf("Failed to receive data after %d retries\n", MAX_RETRIES);
}

在上述代碼中,我們?cè)谝粋€(gè)循環(huán)中不斷地嘗試接收數(shù)據(jù)。如果recv返回了錯(cuò)誤,并且errno被設(shè)置為EWOULDBLOCK或EAGAIN,我們就讓進(jìn)程睡眠一段時(shí)間,然后重試。如果嘗試了指定的次數(shù)還未能成功接收到數(shù)據(jù),那么我們就認(rèn)為已經(jīng)超時(shí)。

這種方法的優(yōu)點(diǎn)是簡(jiǎn)單直觀(guān)。但缺點(diǎn)是可能會(huì)占用大量的CPU資源,因?yàn)樵诔瑫r(shí)期間,程序會(huì)不斷地在循環(huán)中運(yùn)行。

使用select函數(shù)

另一種處理Socket超時(shí)的方法是使用select函數(shù)。select函數(shù)可以監(jiān)聽(tīng)一組文件描述符,等待它們中的任何一個(gè)進(jìn)入就緒狀態(tài)(例如,數(shù)據(jù)可讀),或者直到超時(shí)。這種方法的優(yōu)點(diǎn)是可以同時(shí)監(jiān)聽(tīng)多個(gè)Socket,并且不會(huì)占用過(guò)多的CPU資源。

使用select設(shè)置接收超時(shí)

以下是一段使用select設(shè)置接收超時(shí)的示例代碼:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define TIMEOUT_SECONDS 5
#define BUFFER_SIZE 1024

int main() {
    fd_set set;
    struct timeval timeout;
    char buffer[BUFFER_SIZE];
    int sock_fd;

    // TODO: Initialize the socket here. You need to write your own logic to do this.

    FD_ZERO(&set);
    FD_SET(sock_fd, &set);

    timeout.tv_sec = TIMEOUT_SECONDS;
    timeout.tv_usec = 0;

    int rv = select(sock_fd + 1, &set, NULL, NULL, &timeout);
    if(rv == 0) {
        // Timeout
        printf("Timeout occurred! No data after %d seconds.\n", TIMEOUT_SECONDS);
    } else if(rv < 0) {
        // Error occurred
        perror("Error occurred in select");
    } else {
        // Socket ready, can receive data now
        ssize_t bytes_received = recv(sock_fd, buffer, BUFFER_SIZE - 1, 0); // leave space for '\0'
        if(bytes_received < 0) {
            // Error occurred in recv
            perror("Error occurred in recv");
        } else {
            // Null-terminate the received data
            buffer[bytes_received] = '\0';
            printf("Received data: %s\n", buffer);
        }
    }

    // Clean up and close the socket
    if(close(sock_fd) < 0) {
        perror("Error occurred while closing the socket");
    }

    return 0;
}

在上述代碼中,我們首先初始化了一個(gè)文件描述符集合和一個(gè)時(shí)間間隔結(jié)構(gòu)體。然后,我們將目標(biāo)Socket添加到文件描述符集合中,并設(shè)置了超時(shí)時(shí)間。最后,我們調(diào)用select函數(shù)并檢查其返回值。如果select返回0,表示已經(jīng)超時(shí)。如果select返回負(fù)數(shù),表示發(fā)生了錯(cuò)誤。如果select返回正數(shù),表示有文件描述符已經(jīng)就緒,此時(shí)我們就可以調(diào)用recv來(lái)接收數(shù)據(jù)了。

setsockopt方法設(shè)置Socket超時(shí)

除了上述介紹的非阻塞模式和select函數(shù),還有一種常用的方法是使用setsockopt函數(shù)來(lái)直接設(shè)置Socket的超時(shí)時(shí)間。

setsockopt函數(shù)概述

setsockopt函數(shù)用于設(shè)置指定的Socket選項(xiàng)。它的原型如下:

int setsockopt(int sockfd, int level, int optname,
               const void *optval, socklen_t optlen);

這個(gè)函數(shù)接收五個(gè)參數(shù):sockfd是要設(shè)置的Socket的文件描述符;level指定選項(xiàng)所在的協(xié)議層;optname是需要設(shè)置的選項(xiàng)的名稱(chēng);optval指向包含新選項(xiàng)值的緩沖區(qū);optlenoptval緩沖區(qū)的大小。

使用setsockopt設(shè)置接收超時(shí)

在Socket編程中,SO_RCVTIMEOSO_SNDTIMEO選項(xiàng)可以分別用來(lái)設(shè)置接收和發(fā)送超時(shí)。這兩個(gè)選項(xiàng)都位于套接字層,所以在調(diào)用setsockopt函數(shù)時(shí),level參數(shù)應(yīng)設(shè)為SOL_SOCKET。

以下是一段示例代碼,展示如何使用setsockopt設(shè)置接收超時(shí):

struct timeval timeout;
timeout.tv_sec = TIMEOUT_SECONDS;
timeout.tv_usec = 0;

if (setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
    // Error occurred
}

在上述代碼中,我們首先創(chuàng)建了一個(gè)timeval結(jié)構(gòu)體,并設(shè)置了超時(shí)時(shí)間。然后,我們調(diào)用setsockopt函數(shù),將SO_RCVTIMEO選項(xiàng)的值設(shè)置為指向timeout結(jié)構(gòu)體的指針。如果setsockopt返回負(fù)數(shù),表示發(fā)生了錯(cuò)誤。

需要注意的是,SO_RCVTIMEO和SO_SNDTIMEO選項(xiàng)設(shè)置的超時(shí)時(shí)間是一個(gè)總時(shí)間,而不是在Socket函數(shù)阻塞時(shí)每次等待的時(shí)間。這意味著,如果你在一個(gè)循環(huán)中多次調(diào)用recv函數(shù),那么這些函數(shù)調(diào)用的總時(shí)間將不會(huì)超過(guò)你設(shè)置的超時(shí)時(shí)間。

完整示例代碼

下面是一個(gè)unix domain socket使用setsockopt函數(shù)設(shè)置接收超時(shí)的示例代碼(用文件套接字通信),其中FILE_PATH是文件路徑。

bool nonBlockingRecv()
{
    struct sockaddr_un addr;
    int sock_fd;
    char buffer[BUFFER_SIZE] = "REQ";
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, FILE_PATH.c_str());
    sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock_fd < 0)
    {
        std::cout << "Request socket failed\n";
        return false;
    }

    if (connect(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
    {
        std::cout << "Connect socket failed\n";
        close(sock_fd);
        return false;
    }

    //1.send command
    SEND_INFO(COMMAND);

    // Set recv timeout to 100ms
    struct timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 100000; // 100 ms
    if (setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) 
    {
        std::cout << "Setting socket timeout failed\n";
        close(sock_fd);
        return false;
    }

    //2.receive response of register req
    memset(buffer, 0, BUFFER_SIZE);
    int recv_status = recv(sock_fd, buffer, BUFFER_SIZE, 0);
    if (recv_status < 0) 
    {
        if (errno == EWOULDBLOCK || errno == EAGAIN) 
        {
            std::cout << "Receive timeout\n";
        } 
        else 
        {
            std::cout << "Receive error\n";
        }
        close(sock_fd);
        return false;
    }

    std::cout << "Received [" << buffer << "] from manager" << std::endl;

    //3.check result
    if (NULL != strstr(buffer, SUCCESS.c_str()))//receive success.
    {
        std::cout << "Received success\n";
        close(sock_fd);
        return true;
    }
    else
    {
        std::cout << "Received fail\n";
        close(sock_fd);
        return false;
    }
}

小結(jié)

使用setsockopt函數(shù)設(shè)置SO_RCVTIMEO選項(xiàng)是一種直接且有效的方法來(lái)設(shè)置Socket接收超時(shí)。這種方法的優(yōu)點(diǎn)是簡(jiǎn)單直觀(guān),只需要一行代碼就可以完成設(shè)置。然而,它的缺點(diǎn)是靈活性較差,因?yàn)樗荒茉O(shè)置一個(gè)固定的超時(shí)時(shí)間,而不能動(dòng)態(tài)地根據(jù)網(wǎng)絡(luò)狀況調(diào)整超時(shí)時(shí)間。

總結(jié)

在C/C++中,有多種方法可以用來(lái)設(shè)置Socket接收超時(shí)時(shí)間。非阻塞模式和select函數(shù)亦或setsockopt函數(shù)都是處理這個(gè)問(wèn)題的有效工具。需要注意的是,選擇哪種方法取決于具體的應(yīng)用場(chǎng)景。例如,如果你需要同時(shí)處理多個(gè)Socket,那么select函數(shù)可能是更好的選擇。如果想要方便,setsockopt函數(shù)可以考慮

以上就是C/C++ Socket設(shè)置接收超時(shí)時(shí)間的多種方法的詳細(xì)內(nèi)容,更多關(guān)于C/C++ Socket接收超時(shí)時(shí)間的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論