[C++]C/C++ Socket设置非阻塞模式接收超时时间的多种方法

网络编程中经常需要处理的一个问题就是如何正确地处理Socket超时,对于C/C++,有几种常用的技术可以用来设置Socket接收超时时间,在这篇文章中,我们将详细介绍如何在C/C++中设置Socket的非阻塞模式以及如何配置接收超时时间,需要的朋友可以参考下
C/C++ Socket设置非阻塞模式接收超时时间的多种方法
非阻塞模式(fcntl)
设置非阻塞模式
非阻塞模式下的接收超时
使用select函数
使用select设置接收超时
setsockopt方法设置Socket超时
setsockopt函数概述
使用setsockopt设置接收超时
完整示例代码
小结
总结

C/C++ Socket设置非阻塞模式接收超时时间的多种方法
网络编程中经常需要处理的一个问题就是如何正确地处理Socket超时。对于C/C++,有几种常用的技术可以用来设置Socket接收超时时间。在这篇文章中,我们将详细介绍如何在C/C++中设置Socket的非阻塞模式以及如何配置接收超时时间。


非阻塞模式(fcntl)
默认情况下,Socket操作都是阻塞的。这意味着当调用某个Socket函数时(例如recv),如果数据还未就绪,函数会阻塞等待,直到有数据可用为止。然而,在许多情况下,让函数阻塞并不是最佳解决方案(容易造成卡死)。这时,就需要使用非阻塞模式。


设置非阻塞模式
要将Socket设置为非阻塞模式,可以使用fcntl函数。以下是一段示例代码:
int flags = fcntl(sock_fd, F_GETFL, 0);
fcntl(sock_fd, F_SETFL, flags | O_NONBLOCK);
上述代码首先获取了Socket当前的文件状态标志,然后将O_NONBLOCK标志位添加到文件状态标志中,最后使用F_SETFL命令将新的文件状态标志设置回Socket。此时,Socket已经处于非阻塞模式。


非阻塞模式下的接收超时
在非阻塞模式下,如果没有数据可用,recv函数会立即返回一个错误,并设置errno为EWOULDBLOCK或EAGAIN。因此,可以通过检查errno来确定是否超时。以下是一段示例代码:
#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);
}
在上述代码中,我们在一个循环中不断地尝试接收数据。如果recv返回了错误,并且errno被设置为EWOULDBLOCK或EAGAIN,我们就让进程睡眠一段时间,然后重试。如果尝试了指定的次数还未能成功接收到数据,那么我们就认为已经超时。

这种方法的优点是简单直观。但缺点是可能会占用大量的CPU资源,因为在超时期间,程序会不断地在循环中运行。


使用select函数
另一种处理Socket超时的方法是使用select函数。select函数可以监听一组文件描述符,等待它们中的任何一个进入就绪状态(例如,数据可读),或者直到超时。这种方法的优点是可以同时监听多个Socket,并且不会占用过多的CPU资源。


使用select设置接收超时
以下是一段使用select设置接收超时的示例代码:
#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;
}
在上述代码中,我们首先初始化了一个文件描述符集合和一个时间间隔结构体。然后,我们将目标Socket添加到文件描述符集合中,并设置了超时时间。最后,我们调用select函数并检查其返回值。如果select返回0,表示已经超时。如果select返回负数,表示发生了错误。如果select返回正数,表示有文件描述符已经就绪,此时我们就可以调用recv来接收数据了。


setsockopt方法设置Socket超时
除了上述介绍的非阻塞模式和select函数,还有一种常用的方法是使用setsockopt函数来直接设置Socket的超时时间。


setsockopt函数概述
setsockopt函数用于设置指定的Socket选项。它的原型如下:

1
2
int setsockopt(int sockfd, int level, int optname,
               const void *optval, socklen_t optlen);
这个函数接收五个参数:sockfd是要设置的Socket的文件描述符;level指定选项所在的协议层;optname是需要设置的选项的名称;optval指向包含新选项值的缓冲区;optlen是optval缓冲区的大小。


使用setsockopt设置接收超时
在Socket编程中,SO_RCVTIMEO和SO_SNDTIMEO选项可以分别用来设置接收和发送超时。这两个选项都位于套接字层,所以在调用setsockopt函数时,level参数应设为SOL_SOCKET。

以下是一段示例代码,展示如何使用setsockopt设置接收超时:

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
}
在上述代码中,我们首先创建了一个timeval结构体,并设置了超时时间。然后,我们调用setsockopt函数,将SO_RCVTIMEO选项的值设置为指向timeout结构体的指针。如果setsockopt返回负数,表示发生了错误。

需要注意的是,SO_RCVTIMEO和SO_SNDTIMEO选项设置的超时时间是一个总时间,而不是在Socket函数阻塞时每次等待的时间。这意味着,如果你在一个循环中多次调用recv函数,那么这些函数调用的总时间将不会超过你设置的超时时间。


完整示例代码
下面是一个unix domain socket使用setsockopt函数设置接收超时的示例代码(用文件套接字通信),其中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;
    }
}

小结
使用setsockopt函数设置SO_RCVTIMEO选项是一种直接且有效的方法来设置Socket接收超时。这种方法的优点是简单直观,只需要一行代码就可以完成设置。然而,它的缺点是灵活性较差,因为它只能设置一个固定的超时时间,而不能动态地根据网络状况调整超时时间。


总结
在C/C++中,有多种方法可以用来设置Socket接收超时时间。非阻塞模式和select函数亦或setsockopt函数都是处理这个问题的有效工具。需要注意的是,选择哪种方法取决于具体的应用场景。例如,如果你需要同时处理多个Socket,那么select函数可能是更好的选择。如果想要方便,setsockopt函数可以考虑

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/706677.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode 刷题 [C++] 第141题.环形链表

题目描述 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&a…

STC-ISP原厂代码研究之 V3.7d汇编版本

最近在研究STC的ISP程序,用来做一个上位机烧录软件,逆向了上位机软件,有些地方始终没看明白,因此尝试读取它的ISP代码,但是没有读取成功。应该是目前的芯片架构已经将引导代码放入在了单独的存储块中,而这存储块有硬件级的使能线,在面包板社区-宏晶STC单片机的ISP的BIN文…

uniapp的动态表单实现

目录 1.说明 2.示例 3.总结 1.说明 ①在 formData 中定义个数组变量用来接受同一个字段的多个结果。 dynamicFormData: {email: ,// domains 字段下会有多个结果domains: [] }②使用 uni-forms-item 的 rules 属性定义单个表单域的校验规则。 <uni-forms-item :label&qu…

matlab绘制雷达图和二维FFT变换图

1、内容简介 略 49-可以交流、咨询、答疑 matlab绘制雷达图和二维FFT变换图 NMO组及NORMAL组 RNFL层、GCL层、IPL层、GCC层、ORL层做雷达图&#xff08;共10张&#xff09; 2、内容说明 略 NMO组及NORMAL组 RNFL层、GCL层、IPL层、GCC层、ORL层请分别做雷达图&#xff08…

每日OJ题_分治快排③_力扣215. 数组中的第K个最大元素

目录 力扣215. 数组中的第K个最大元素 解析代码 力扣215. 数组中的第K个最大元素 215. 数组中的第K个最大元素 难度 中等 给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xf…

【学习笔记】SOA服务设计原则(二)举例说明

以银行系统为例 背景 在线银行系统允许用户进行各种银行业务,如查看账户余额、转账、支付账单等。为了支持这些功能,系统需要与账户管理、交易处理、账单支付等多个不同的服务进行交互。 应用SOA设计基本原则 标准化服务合约(Standardized Service Contract) 例子:所有服…

VAST Data步步攀升,整合AI堆栈,打造一体化AI平台:从存储根基走向全方位数据处理与分析

VAST Data公司正逐步从其商品化存储基础出发&#xff0c;构建一套统一的AI堆栈系统&#xff0c;致力于提供一站式AI解决方案。最近&#xff0c;VAST公司的现场CTO安迪珀斯坦纳在一次简报中传达了这一战略方向。 VAST提供了分散式QLC单层闪存架构的并行、横向扩展文件型存储系统…

kotlin与java的相互转换

Kotlin转java 将kotlin代码反编译成java Tools -> Kotlin -> Show Kotlin Bytecode 然后点击 【Decompile】 生成java代码 java转kotlin Code -> Convert Java File To Kotlin File

Docker数据管理及网络通信

目录 ------------------Docker 的数据管理--------------------- 1&#xff0e;数据卷 2&#xff0e;数据卷容器 -----------------端口映射----------------------------------- ------------------容器互联&#xff08;使用centos镜像&#xff09;---------------------…

科技论文编写思路

科技论文编写思路 1.基本框架2.课题可行性评估1.研究目标和意义2.研究方法和技术3.可行性和可操作性4.风险和不确定性5.经济性和资源投入6.成果预期和评估 3.写作思路4.利用AI读论文5.实验流程 1.基本框架 IntroductionRelated worksMethodExperiment and analysisDiscussionC…

ElasticSearch之Search Template和Index Alias

写在前面 本文看下es的search template和index alias。 1&#xff1a;search template 用来定义模板查询语句&#xff0c;运行时只需要将要查询的内容作为参数传进来即可&#xff0c;如下&#xff1a; 接着来测试下&#xff0c;首先来定义数据&#xff1a; DELETE tmdb/ P…

服务器防火墙的应用技术有哪些

服务器防火墙的应用技术有哪些 1.数据包过滤技术 数据包过滤是最基本的服务器防火墙技术之一&#xff0c;它根据一系列预定义规则过滤进出网络的数据包。数据包过滤器通常基于IP地址、端口号和协议类型等信息来判断数据包是否合法&#xff0c;如果不符合规则&#xff0c;则将被…

解决i18n国际化可读性问题,傻瓜式webpack中文支持国际化插件开发

先来看最后的效果 问题 用过国际化i18n的朋友都知道&#xff0c;天下苦国际化久矣&#xff0c;尤其是中文为母语的开发者&#xff0c;在面对代码中一堆的$t(abc.def)这种一点也不直观毫无可读性的代码&#xff0c;根本不知道自己写了啥 &#xff08;如上图&#xff0c;你看得出…

R语言在数据分析中的应用案例

一、引言 随着大数据时代的来临&#xff0c;数据分析已经成为了各行各业不可或缺的一部分。R语言&#xff0c;作为一款开源的统计计算和图形展示软件&#xff0c;因其强大的统计计算功能、丰富的包资源和灵活的编程特性&#xff0c;在数据分析领域得到了广泛的应用。本案例将通…

大语言模型LLM分布式框架:PyTorch Lightning框架(LLM系列14)

文章目录 大语言模型LLM分布式框架&#xff1a;PyTorch Lightning框架&#xff08;LLM系列14&#xff09;引言PyTorch Lightning分布式计算基础PyTorch Lightning核心架构概览LightningModule与分布式训练的兼容性LightningDataModule在分布式数据加载与预处理中的作用Trainer类…

Swagger3 使用详解

Swagger3 使用详解 一、简介1 引入依赖2 开启注解3 增加一个测试接口4 启动服务报错1.5 重新启动6 打开地址&#xff1a;http://localhost:8093/swagger-ui/index.html 二、Swagger的注解1.注解Api和ApiOperation2.注解ApiModel和ApiModelProperty3.注解ApiImplicitParams和Api…

大数据职业技术培训包含哪些

技能提升认证考试&#xff0c;旨在通过优化整合涵盖学历教育、职业资格、技术水平和高新技术培训等各种教育培训资源&#xff0c;通过大数据行业政府引导&#xff0c;推进教育培训的社会化&#xff0c;开辟教育培训新途径&#xff0c;围绕大数据技术人才创新能力建设&#xff0…

【华为面试基础题】求字符串中所有整数的最小和

描述 字符串s&#xff0c;只包含 a-z A-Z - &#xff1b; 合法的整数包括 1&#xff09; 正整数 一个或者多个0-9组成&#xff0c;如 0 2 3 002 102 2&#xff09;负整数 负号 - 开头&#xff0c;数字部分由一个或者多个0-9组成&#xff0c;如 -0 -012 -23 -00023 输入描述&…

targetSdkVersion > 30 如何将下载的网络视频 保存到手机相册里更新

在 targetSdkVersion 31 中&#xff0c;将下载的网络视频保存到手机相册中涉及几个关键步骤。由于 Android 12&#xff08;API 级别 31&#xff09;引入了更多的隐私和安全限制&#xff0c;特别是作用域存储&#xff08;Scoped Storage&#xff09;&#xff0c;因此你需要遵循这…

java特殊文件、日志技术、多线程

一、属性文件 1.1 特殊文件概述 像这种普通的文本文件&#xff0c;没有任何规律可言&#xff0c;不方便程序对文件中的数据信息处理。 在以后的Java开发过程中还会遇到一些特殊的文本文件&#xff0c;这些文件是有一些格式要求的&#xff0c;方便程序对文件中的数据进行处理。…