[IO复用] recv()和send()的阻塞和非阻塞、返回值、超时

文章目录

  • 前言
    • 阻塞
      • 超时设置
    • 非阻塞
      • 创建socket时,直接用SOCK_NOBLOCK指定为非阻塞
        • server部分
        • client 部分
      • 使用fcntl()把socket设置为非阻塞
      • socket阻塞,单独把recv或者send设置为非阻塞
    • recv和send的返回值
    • 参考文章

前言

记录一下recv和send函数的相关信息。

阻塞

头文件

#include <sys/socket.h> //socket()
#include <unistd.h>  //close()
#include<netinet/in.h> //sockaddr_in

#include <stdio.h> //perror()
#include <errno.h>

#include <string.h> //memset
#include <iostream>

#include <netinet/in.h> //inet_ntoa
#include <arpa/inet.h>

阻塞模式server代码,进行recv

#if 1 //阻塞模式
int main()
{
    int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(listen_fd == -1) {
        perror("socket");
        return -1;
    }

    timeval timev;
    timev.tv_sec = 3;
    timev.tv_usec = 0;
    int ret = setsockopt(listen_fd, SOL_SOCKET, SO_RCVTIMEO, (void*)&timev, sizeof(timev));
    if(ret != 0)
    {
        perror("setsockopt SO_RCVTIMEO");
        close(listen_fd);
        return -1;
    }

    sockaddr_in local_addr;
    local_addr.sin_family = AF_INET;
    local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    local_addr.sin_port = htons(9100);
    ret = bind(listen_fd, (sockaddr*)&local_addr, sizeof(local_addr));
    if(ret == -1)
    {
        perror("bind");
        close(listen_fd);
        return -1;
    }

    ret = listen(listen_fd,5);
    if (ret == -1)
    {
        perror("listen");
        close(listen_fd);
        return -1;
    }

    sockaddr_in cnn_addr;
    memset(&cnn_addr, 0x00, sizeof(cnn_addr));
    socklen_t len = sizeof(cnn_addr);
    int conn_fd = accept(listen_fd, (sockaddr*)&cnn_addr, &len);
    if(conn_fd == -1)
    {
        perror("accept");
        close(listen_fd);
        return -1;
    }
    else
    {
        std::cout << "accept addr:" << inet_ntoa(cnn_addr.sin_addr)
                  << " port:" << ntohs(cnn_addr.sin_port) << std::endl;
        int loop_count = 0;

        char buff[512] = {0x00};
        while (1)
        {
            std::cout << "loop:" << ++loop_count << " times" << std::endl;
            int count = recv(conn_fd, buff, 512, 0);
            if(count == 0) 
            {
                //无论阻塞模式还是非阻塞模式,
                //recv 返回0,都表示链接断开
                std::cout << "close end" << std::endl;
                break;
            }
            if(count == -1)
            {
                 //无论阻塞模式还是非阻塞模式,
                //recv 返回-1,都表示错误,返回一下三个错误,被视为正常
                if (errno == EINTR || 
                    errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    std::cout << "EWOULDBLOCK.continue" << std::endl;
                    continue;
                }
                else
                {
                    std::cout << "ERROR.break" << errno <<std::endl;
                    break;
                }
            }
            else 
                std::cout << "recv (" << count << " bytes):" << buff << std::endl;
        }
    }

    std::cout << "close connection" << std::endl;
    close(listen_fd);
    return 0;
}
#endif //阻塞模式

阻塞模式client代码,进行send

#if 1 //阻塞模式
int main()
{
    int local_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(local_fd == -1) {
        perror("socket");
        return -1;
    }

    timeval timev;
    timev.tv_sec = 3;
    timev.tv_usec = 0;
    int ret = setsockopt(local_fd, SOL_SOCKET, SO_SNDTIMEO, (void*)&timev, sizeof(timev));

    sockaddr_in local_addr;
    local_addr.sin_family = AF_INET;
    local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    local_addr.sin_port = htons(9100);
    ret = connect(local_fd, (sockaddr*)&local_addr, sizeof(local_addr));
    if(ret == -1)
    {
        perror("accept");
        close(local_fd);
        return -1;
    }
    else
    {
        std::cout << "connect addr:" << inet_ntoa(local_addr.sin_addr)
                  << " port:" << ntohs(local_addr.sin_port) << std::endl;
        int loop_count = 0;

        char buff[512] = {0x00};
        sprintf(buff, "%s", "hello");
        while (1)
        {
            std::cout << "loop:" << ++loop_count << " times" << std::endl;

            getchar();
            int count = send(local_fd, buff, strlen(buff), 0);
            if(count == 0) 
            {
                //无论阻塞模式还是非阻塞模式,
                //send 返回0,都表示链接断开
                std::cout << "close end" << std::endl;
                break;
            }
            if(count == -1)
            {
                 //无论阻塞模式还是非阻塞模式,
                //send 返回-1,都表示错误,返回一下三个错误,被视为正常
                if (errno == EINTR || 
                    errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    std::cout << "EWOULDBLOCK.continue" << std::endl;
                    continue;
                }
                else
                {
                    std::cout << "ERROR.break" << errno <<std::endl;
                    break;
                }
            }
            else
                std::cout << "send (" << count << " bytes):" << buff << std::endl;
        }
    }

    std::cout << "close connection" << std::endl;
    close(local_fd);
    return 0;
}
#endif //阻塞模式

超时设置

上面代码中均用setsockopt进行了recv和send的超时设置。

int ret = setsockopt(listen_fd, SOL_SOCKET, SO_RCVTIMEO, (void*)&timev, sizeof(timev));
 int ret = setsockopt(local_fd, SOL_SOCKET, SO_SNDTIMEO, (void*)&timev, sizeof(timev));

这个超时设置不但包括recv和send,还包括accept。
accept
当accept超时的时候,会返回-1,errno==11(“Resource temporarily unavailable”)。

recv
当recv超时的时候,会返回-1, errno==EAGAIN(11)。
EWOULDBLOCK 就是EAGAIN

#define	EWOULDBLOCK	EAGAIN	/* Operation would block */

非阻塞

创建socket时,直接用SOCK_NOBLOCK指定为非阻塞

server部分

设置如下,因为socket()默认是阻塞的,所以设置了SOCK_NONBLOCK就会是非阻塞socket了。

- int listen_fd = socket(AF_INET, SOCK_STREAM, 0); //block socket
+ int listen_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); //non-block socket

和上面的超时设置一样,如果在sock()函数中,设置了SOCK_NONBLOCK参数,accept会变成非阻塞,
如果执行时没有连接可以返回,accept就会直接返回-1,errno:11。
需要用一个while循环, 当accept返回 -1 并且errno 为EAGAIN 时,重复accept。

但是此时recv和send还是阻塞的
这是因为,上面的diff代码,创建的是用于listen的socket,它被设置为非阻塞后,会影响跟他有关的accept。
但是recv和send,关联的socket是accept返回的连接的socket,跟上面listen的fd的设置无关了。

 int conn_fd = accept(listen_fd, (sockaddr*)&cnn_addr, &len);
 ...
 int count = recv(conn_fd, buff, 512, 0);

此时可以用accept4()把accept返回的socket,设置为非阻塞的:

- int conn_fd = accept(listen_fd, (sockaddr *)&cnn_addr, &len); //return block socket
+ int conn_fd = accept4(listen_fd, (sockaddr *)&cnn_addr, &len, SOCK_NONBLOCK); //return non-block socket

这样后续的recv 就是非阻塞了。

client 部分

向上面一样,在socket()中添加SOCK_NONBLOCK参数,可以把socket设置为非阻塞。
和socket关联的connect也是非阻塞的了。当无法立即建立连接,connect会返回EINPROGRESS。
可以直接不启动server,直接启动client进行connect来测试:
阻塞connect的执行结果:

~/share/myproj/2.1.1-multiIO/20240121$ ./client
connect: Connection refused ← perror输出

非阻塞connect的执行结果:

~/share/myproj/2.1.1-multiIO/20240121$ ./client
connect: Operation now in progress ← perror输出
connect EINPROGRESS continue ← 循环处的log,证明continue了
connect: Connection refused ← perror输出

跟上面同理,connect后面的send,也变成非阻塞的了。
此时让server不调用recv(), client的send会一直发送,直到server的接收缓冲区满了,send将会立刻返回一个EWOULDBLOCK错误。
而如果是阻塞的socket,send将会阻塞住。

使用fcntl()把socket设置为非阻塞

这里只是设置方式的差别。
实际效果是和上面一样的。
上面是在socket()函数中,设置SOCK_NONBLOCK来进行非阻塞设置。
还可以用以下方法:
代码来自于:使用fcntl()函数设置socket为阻塞态或非阻塞态
设置为非阻塞

#include <fcntl.h> //fcntl
int func()
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    int flag = fcntl(fd,F_GETFL,0);//获取文件fd当前的状态
    //int flag = fcntl(fd,F_GETFL);//不用第3个参数也可以
    if(flag<0)
    {
        perror("fcntl F_GETFL fail"); 
        close(fd); 
        return -1; 
    }
    if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0)//设置为非阻塞态
     {  
        perror("fcntl F_SETFL fail"); 
        close(fd);
        return -1; 
    }
}

恢复阻塞

int func()
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    int flag = fcntl(fd,F_GETFL,0);//获取文件fd当前的状态
    if(flag<0)
    {
        perror("fcntl F_GETFL fail"); 
        close(fd); 
        return -1; 
    }
    
    flag = flag&~O_NONBLOCK;
    //flag &= ~O_NONBLOCK;//等同于上行
    if (fcntl(fd, F_SETFL, flag) < 0)//设置为阻塞态
     {  
        perror("fcntl F_SETFL fail"); 
        close(fd);
        return -1; 
    }
}

socket阻塞,单独把recv或者send设置为非阻塞

也可以单独把recv和send函数设置为非阻塞。
设置方法如下:

- int count = recv(conn_fd, buff, 512, 0);
+  count = recv(conn_fd, buff, 512, MSG_DONTWAIT);

这样就是socket是阻塞的,但是设置了MSG_DONTWAIT的recv是非阻塞的(除了这个recv函数,别的accept等函数都还是阻塞的)。

recv和send的返回值

不论阻塞还是非阻塞,
recv的返回值,都是
大于0,表示接受的字节数
等于0,表示断开连接
等于-1,如果是errno == (EAGAIN || EWOULDBLOCK || EINTR) ,则表示正常,继续recv;
其余errno 表示发生错误。

send也是如此。

参考文章

socket阻塞与非阻塞情况下的recv、send、read、write返回值

使用fcntl()函数设置socket为阻塞态或非阻塞态

send与recv函数详解

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

写Shell以交互方式变更Ubuntu的主机名

以下是一个简单的 Bash 脚本&#xff0c;用于以交互方式更改 Ubuntu 20 系统的主机名&#xff1a; 1#!/bin/bash 2 3# 提示用户输入新的主机名 4read -p "请输入新的系统名称&#xff08;主机名&#xff09;: " new_hostname 5 6# 检查是否输入了新的主机名 7if [ -…

猫用空气净化器哪些好?五款宠物空气净化推荐!

如今&#xff0c;养宠物的家庭越来越多了&#xff01;家里因此变得更加温馨&#xff0c;但同时也会带来一些问题&#xff0c;比如异味和空气中的毛发可能会对健康造成困扰。 为了避免家中弥漫着异味&#xff0c;特别是来自宠物便便的味道&#xff0c;一款能够处理家里异味的宠…

python基础学习-03 安装

python3 可应用于多平台包括 Windows、Linux 和 Mac OS X。 Unix (Solaris, Linux, FreeBSD, AIX, HP/UX, SunOS, IRIX, 等等。)Win 9x/NT/2000Macintosh (Intel, PPC, 68K)OS/2DOS (多个DOS版本)PalmOSNokia 移动手机Windows CEAcorn/RISC OSBeOSAmigaVMS/OpenVMSQNXVxWorksP…

npm install运行报错npm ERR! gyp ERR! not ok问题解决

执行npm install的时候报错&#xff1a; npm ERR! path D:..\node_modules\\**node-sass** npm ERR! command failed ...npm ERR! gyp ERR! node -v v20.11.0 npm ERR! gyp ERR! node-gyp -v v3.8.0 npm ERR! gyp ERR! not ok根据报错信息&#xff0c;看出时node-sass运行出现…

图像处理算法:白平衡、除法器、乘法器~笔记

参考&#xff1a; 基于FPGA的自动白平衡算法的实现 白平衡初探 (qq.com) FPGA自动白平衡实现步骤详解-CSDN博客 xilinx 除法ip核&#xff08;divider&#xff09; 不同模式结果和资源对比&#xff08;VHDL&ISE&#xff09;_ise除法器ip核-CSDN博客 数…

绝地求生:本周三停机维护更新4小时: RASH悲喜套装即将下线!

本周三将迎来停机维护更新四小时~&#xff0c;同时游戏商城内RASH悲喜联名套装即将下线&#xff0c;同时空投签到任务和荣都地图翻牌任务即将下线~ 预计维护时间: 2024年1月24日08:00~12:00 本周地图轮换情况 (1月24日 ~ 1月31日) 可自主选择地图的地区:艾伦格、泰戈、帝斯顿、…

前沿重器[41] | 综述-面向大模型的检索增强生成(RAG)

前沿重器 栏目主要给大家分享各种大厂、顶会的论文和分享&#xff0c;从中抽取关键精华的部分和大家分享&#xff0c;和大家一起把握前沿技术。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。&#xff08;算起来&#xff0c;专项启动已经…

JavaScript入门分享

文章目录 一、JavaScript简介二、第一个JavaScript案例三、在浏览器中执行JavaScript代码四、JavaScript的输出方法五、JavaScript的语法六、JavaScript的数据类型七、JavaScript的定义变量/函数八、热门文章 一、JavaScript简介 JavaScript是一种高级编程语言&#xff0c;用于…

假期刷题打卡--Day12

1、MT1128骰子的反面 小码哥抛出一个六面骰子。每个面上都印有一个数字&#xff0c;数字在1到6之间。输入正面的数字&#xff0c;输出对面的数字。 其他情况输出-1。 格式 输入格式&#xff1a; 输入为整型 输出格式&#xff1a; 输出为整型 样例 1 输入&#xff1a; …

【CANoe使用大全】——工程新建

文章目录 1、硬件连接2、通道配置2.1通道协议选择2.2映射通道配置2.3.波特率采样点配置 1、硬件连接 前提条件&#xff1a;软件、驱动均已经安装完成 硬件通过UBS接入电脑&#xff0c;Status状态灯为黄色闪烁状态说明硬件设备与电脑连接正常 2、通道配置 2.1通道协议选择 …

npm i 报一堆版本问题

1&#xff0c;先npm cache clean --force 再下载 插件后缀加上 --legacy-peer-deps 2&#xff0c; npm ERR! code CERT_HAS_EXPIRED npm ERR! errno CERT_HAS_EXPIRED npm ERR! request to https://registry.npm.taobao.org/yorkie/download/yorkie-2.0.0.tgz failed, reason…

Linux之安装配置VCentOS7+换源

目录 一、安装 二、配置 三、安装工具XSHELL 3.1 使用XSHELL连接Linux 四、换源 前言 首先需要安装VMware虚拟机&#xff0c;在虚拟机里进行安装Linux 简介 Linux&#xff0c;一般指GNU/Linux&#xff08;单独的Linux内核并不可直接使用&#xff0c;一般搭配GNU套件&#…

SQL注入示例

例一、基础SQL注入&#xff1a;load_file读文件 CISP-PTE 认证考试 首先是有单引号和括号的&#xff0c;首要是要闭合&#xff0c;然后回显点是在-1的位置&#xff0c;读取文件上面的key的话使用的是load_file(/tmp/360/key) id-1)%09ununionion%09select%091,2,3,load_file…

Open3D 最小二乘拟合二维直线(拉格朗日乘子法)

目录 一、算法原理二、代码实现三、结果展示Open3D 最小二乘拟合二维直线(拉格朗日乘子法)由CSDN点云侠原创。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、算法原理 平面直线的表达式为: y = k x

【BIAI】Lecture 8 - EEG data analysis hands on

Lecture 8 - EEG data analysis hands on 分析流程 Recap: 频域上的分析 Recap: ICA降噪 ICA: 用于从混合信号中分离出独立的成分或因素。它可以将观测到的多个信号分解为相互独立的成分&#xff0c;这些成分在原始信号中可能是混合在一起的。 假设采集EEG信号的帽子上有n个电…

C语言快速排序(非递归)图文详解

前言&#xff1a; 上一期分析了快速排序的三种写法&#xff0c;这三种写法有一个相同点&#xff0c;都是采用递归形式来实现的&#xff0c;那么有没有非递归的方法实现呢&#xff1f;答案是当然有&#xff0c;用非递归的方法实现快速排序&#xff0c;其实可以借助数据结构中的栈…

docker 部署及命令

一、容器概述 1、为什么要用到容器&#xff1f; ①容器可以屏蔽底层操作系统的差异性&#xff0c;让业务应用不管在哪里都是使用容器的环境运行&#xff0c;从而保证开发测试环境与生产环境的一致性 ②容器部署起来非常便捷和迅速&#xff0c;缩短开发测试部署的周期时间 2…

关于在Ubuntu20.04(ROS1 noetic)中使用catkin_make编译时发生的与pyhton版本不兼容的问题解决办法

今天在另外一台电脑上操作复现【ROS建模&#xff1a;一起从零手写URDF模型】这个博客时&#xff0c;发生了一些问题&#xff0c;特此记录下来 【ROS建模&#xff1a;一起从零手写URDF模型】链接&#xff1a;https://blog.csdn.net/qq_54900679/article/details/135726348?spm…

C++高精度问题

高精度前言 C中int不能超过2^31-1&#xff0c;最长的long long也不能超过2^63-1,所以我们在题目中如果碰到了很长很长的数&#xff0c;并且需要进行大数运算时&#xff0c;就需要高精度存储。 高精度总体思路 由于int和long long的限制&#xff0c;我们要想存放很长的数就需…

数据分析的理念、流程、方法、工具(下)

四、用户分群 1、用户分群 用户分群是精细化运营的基础要求&#xff0c;也是数据分析的最基础方式。对用户进行分群&#xff0c;能帮助我们了解每个细分群体用户的变化情况&#xff0c;进而了解用户的整体现状及发展趋势。同时&#xff0c;由于运营资源本身有限&#xff0c;不…