网络编程(学习)2024.8.29

news2024/11/16 5:34:49

目录

阻塞式IO(BIO)

特点

阻塞原因与阻塞反应

TCP流式套接字缓冲区

非阻塞式IO(NIO)     

特点

设置非阻塞

1.通过对参数的修改实现

2.通过对文件描述符的属性进行设置 fcntl

信号驱动IO (异步IO模型)

IO多路复用  select、poll、epoll

IO多路复用机制 

1.select

2.fd_set表的结构与机制

3.流程

阻塞式IO(BIO)

特点

最简单、最常用;但是相对于进程来说效率低

阻塞原因与阻塞反应

1.当程序调用某些接口时,如果期望的动作无法触发,那么进程会进入阻塞态(等待状态,让出CPU的调度),当期望动作可以被触发了,那么会被唤醒,然后处理事务。

2.阻塞I/O模式是最普遍使用的I/O 模式,大部分程序使用的都是阻塞模式的I/O,缺省情况下(及系统默认状态),套接字建立后所处于的模式就是阻塞I/O模式。
3.之前学习的很多读写函数在调用过程中会发生阻塞。

例如:
(1)读操作中的read、recv、recvfrom
        读阻塞——>需要读缓冲区中有数据可读,读阻塞解除
(2)写操作中的write、send
        写阻塞——>阻塞情况比较少,主要发生在写入的缓冲区的大小小于要写入的数据量的情况下,写操作不进行任何拷贝工作,将发生阻塞,一旦缓冲区有足够的空间,内核将唤醒进程,将数据从用户缓冲区拷贝到相应的发送数据缓冲区。 


注意:sendto没有写阻塞
无sendto函数的原因:
        sendto不是阻塞函数,本身udp通信不是面向连接的,udp无发送缓冲区,即sendto没有发送缓冲区,send是有发送缓存区的,即sendto不是阻塞函数。
        UDP不用等待确认,没有实际的发送缓冲区,所以UDP协议中不存在缓冲区满的情况,在UDP套接字上进行写操作永远不会阻塞。
其他阻塞函数操作:accept、connect

TCP流式套接字缓冲区

da1d35b3ba4143f48f29237ad1c218b4.png

非阻塞式IO(NIO)     

特点

可以处理多路IO;但是需要轮询,浪费CPU资源

当一个应用程序使用了非阻塞模式的套接字,它需要使用一个循环来不停地测试一个文件描述符是否有数据可读,这步操作叫做轮询(polling)。
应用程序不停的polling 内核来检查是否I/O操作已经就绪。这将是一个极浪费CPU 资源的操作。
这种模式使用中不普遍。

设置非阻塞

1.通过对参数的修改实现

例如recv()最后的参数:

 char buf[N];
        while (1)
        {
            memset(buf, 0, N);
            int ret = recv(acceptfd, buf, N, 0);//当是0的时候阻塞,当是MSG_DONTWAIT的时候非阻塞
            if (ret < 0)
            {
                if (errno == 11)
                {
                    printf("读缓存区内没数据\n");
                    continue;
                }
                else
                {
                    perror("recv失败\n");
                    return -1;
                }
            }
            else if (ret == 0)
            {
                printf("客户端ip:%s退出\n", inet_ntoa(caddr.sin_addr));
                close(acceptfd);
                break;
            }
            else
            {
                printf("客户端%s\n", buf);
            }
        }

2.通过对文件描述符的属性进行设置 fcntl

由于阻塞基本都是对有缓冲区的文件进行操作导致的,修改文件描述符的属性,就可以使read等的时候实现非阻塞。
#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ...);
功能:
        获取/改变文件属性(linux中一切皆文件)
参数:
        fd:文件描述符
        cmd:设置的命令
                F_GETFL  //获取文件的属性
                F_SETFL  //设置文件的属性
        第三个参数:由第二个参数决定,set时候写需要设置的值,get时候写0

返回值:
        成功:文件状态标志(文件的属性)  
        失败: -1 

57a4b800b9fd4991a26aae6c9a9f5e0b.png

置0:目标位和0相与,其他位和1相与
置1:目标位和1相或,其他位和0相或

设置阻塞还是非阻塞

int flag;        //文件状态的标志 
flag = fcntl(fd, F_GETFL);         //读        fd是文件描述符
flag |= O_NONBLOCK;        //改        O_NONBLOCK = 0x00004000
fcntl(fd, F_SETFL, flag);        //写

例如:标准输入非阻塞设置

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

#define N 32
int main(int argc, char const *argv[])
{
    int flag;                
    flag = fcntl(0, F_GETFL); 
    flag |= O_NONBLOCK;    
    fcntl(0, F_SETFL, flag); 
    char buf[N];
    while (1)
    {
        memset(buf, 0, N);
        gets(buf);
        printf("buf:%s\n", buf);
        if (strlen(buf) > 0)
        {
            sleep(5);
        }
    }

    return 0;
}

信号驱动IO (异步IO模型)

特点:异步通知模式,需要底层驱动的支持

IO多路复用  select、poll、epoll

案例分析:键盘鼠标事件

同时对键盘和鼠标进行监听,当敲击键盘按下回车,就打印键盘输入的东西,动鼠标就要打印鼠标写入的内容。

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>

#define N 64
int main(int argc, char const *argv[])
{
    int mouse = open("/dev/input/mouse0", O_RDONLY);
    if (mouse < 0)
    {
        perror("open失败");
        return -1;
    }
    char buf[N];
    while (1)
    {
        memset(buf, 0, N);
        gets(buf);
        printf("buf:%s\n", buf);
        int ret = read(mouse, buf, N);
        if (ret < 0)
        {
            perror("read失败");
            return -1;
        }
        else
        {
            printf("mouse:%s\n", buf);
        }
    }
}

IO多路复用机制 

使用I/O多路复用技术。其基本思想是:

1.先构造一张有关描述符的表,然后调用一个函数。
2.当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。
3.函数返回时告诉进程哪个描述符已就绪,可以进行I/O操作。

1.select

#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

 int select(int nfds(要轮询的文件描述符个数), fd_set *readfds(阻塞的读文件描述符), fd_set *writefds(阻塞的写文件描述符),fd_set *exceptfds(阻塞的异常文件描述符), struct timeval *timeout(超时时长));

一般写:select(int nfds,fd_set *readfds,NULL,NULL,NULL);

void FD_CLR(int fd, fd_set *set);        //将某一文件描述符在表里去除
int  FD_ISSET(int fd, fd_set *set);        //判断某一文件描述符是否在表里
void FD_SET(int fd, fd_set *set);        //将某一文件描述符放入表中
void FD_ZERO(fd_set *set);        //将表置零

2.fd_set表的结构与机制

e26c46e3e64c4269ba46d4f5e45ce5d6.png

3.流程

第一步:建表初始化
第二步:填表
第三步:监听表
第四步:判断,操作

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>

#define N 64
int main(int argc, char const *argv[])
{
    int mouse = open("/dev/input/mouse0", O_RDONLY);
    if (mouse < 0)
    {
        perror("open失败");
        return -1;
    }
    char buf[N];

    // 第一步:建表初始化
    fd_set readfds, tempfds;
    FD_ZERO(&readfds);
    // 第二步:填表
    FD_SET(0, &readfds);
    FD_SET(mouse, &readfds);
    // 第三步:监听表
    while (1)
    {
        memset(buf, 0, N);
        temphfds = readfds;
        int ret = select(4, &tempfds, NULL, NULL, NULL);
        // 第四步:判断,操作
        if (ret == -1)
        {
            perror("select失败");
            return -1;
        }
        if (FD_ISSET(0, &tempfds))
        {
            gets(buf);
            printf("buf:%s\n", buf);
        }
        if (FD_ISSET(mouse, &tempfds))
        {
            int n = read(mouse, buf, N);
            if (n < 0)
            {
                perror("read失败");
                return -1;
            }
            else
            {
                printf("mouse:%s\n", buf);
            }
        }
    }

    return 0;
}

例题:创建全双工客户端(既能接收也能发送)

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <errno.h>

int main(int argc, char const *argv[])
{
    // 1.创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    printf("sorkfd:%d\n", sockfd);

    // 2.连接
    unsigned short post = 0;
    char ip[15];
    printf("请输入ip地址");
    scanf("%s", ip);
    getchar();
    printf("请输入端口号");
    scanf("%hd", &post);
    getchar();
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(post);
    saddr.sin_addr.s_addr = inet_addr(ip);
    socklen_t addrlen = sizeof(saddr);
    if (connect(sockfd, (struct sockaddr *)&saddr, addrlen) < 0)
    {
        perror("connect失败\n");
        return -1;
    }

    // 3.接收
#define N 64
    char buf[N];
    while (1)
    {
        // 第一步:建表初始化
        fd_set readfds, tempfds;
        FD_ZERO(&readfds);
        // 第二步:填表
        FD_SET(0, &readfds);
        FD_SET(sockfd, &readfds);
        // 第三步:监听表
        while (1)
        {
            memset(buf, 0, N);
            tempfds = readfds;
            int ret = select(4, &tempfds, NULL, NULL, NULL);
            // 第四步:判断,操作
            if (ret == -1)
            {
                perror("select失败");
                return -1;
            }
            if (FD_ISSET(0, &tempfds))
            {
                scanf("%s", buf);
                send(sockfd, buf, N, 0);
            }
            if (FD_ISSET(sockfd, &tempfds))
            {
                int ret = recv(sockfd, buf, N, 0);
                printf("服务端:%s\n", buf);
            }
        }
    }
    close(sockfd);
    return 0;
}

例题:创建全双工服务端(既能接收也能发送)

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

#define ERR_MSG(msg)                           \
    do                                         \
    {                                          \
        fprintf(stderr, "line:%d ", __LINE__); \
        perror(msg);                           \
    } while (0)

int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        printf("用法:<port>\n");
        return -1;
    }
    // 1.创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    printf("sorkfd:%d\n", sockfd);
    // 2.bind绑定IP和Port端口号
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[1]));
    // saddr.sin_addr.s_addr = inet_addr("192.168.50.213");
    socklen_t addrlen = sizeof(saddr);
#if 0
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
#else
    saddr.sin_addr.s_addr = INADDR_ANY;
#endif

    if (bind(sockfd, (struct sockaddr *)&saddr, addrlen) < 0)
    {
        ERR_MSG("bind失败");
        return -1;
    }
    printf("bind成功\n");

    // 3.监听listen将主动套接字变为被动套接字
    if (listen(sockfd, 7) < 0)
    {
        ERR_MSG("lisren失败");
        return -1;
    }
    printf("listen成功\n");
    while (1)
    {
        // 4.accept阻塞等待链接
        int acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &addrlen);
        if (acceptfd < 0)
        {
            ERR_MSG("accept失败\n");
            return -1;
        }
        printf("acceptfd:%d\n", acceptfd);
        printf("客户端ip:%s\t 端口号:%d\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));

        // 5.发送
#define N 64
        char buf[N];
        while (1)
        {
            // 第一步:建表初始化
            fd_set readfds, tempfds;
            FD_ZERO(&readfds);
            // 第二步:填表
            FD_SET(0, &readfds);
            FD_SET(acceptfd, &readfds);
            // 第三步:监听表
            while (1)
            {
                memset(buf, 0, N);
                tempfds = readfds;
                int ret = select(5, &tempfds, NULL, NULL, NULL);
                // 第四步:判断,操作
                if (ret == -1)
                {
                    perror("select失败");
                    return -1;
                }
                if (FD_ISSET(0, &tempfds))
                {
                    scanf("%s", buf);
                    send(acceptfd, buf, N, 0);
                }
                if (FD_ISSET(acceptfd, &tempfds))
                {
                    int ret = recv(acceptfd, buf, N, 0);
                    printf("客户端:%s\n", buf);
                }
            }
        }

        close(sockfd);
        return 0;
    }
}

 

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

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

相关文章

深度学习实战2--MNIST 手写数字分类(代码在末尾)

1.本节目标&#xff1a; (1)了解什么是MNIST 数据集&#xff1b; (2)了解卷积神经网络对图片处理的流程&#xff1b; (3)能够看懂Python 编写的对图片分类任务的代码&#xff1b; (4)在一定程度上掌握处理类似任务的编程能力。 注意&#xff1a;本章节使用开源机器学习库P…

docker 部署 kkFileView 并 使用Nginx代理

拉取镜像 docker pull keking/kkfileview 运行容器 docker run -it -d -p 8012:8012 keking/kkfileview --restart always 配置nginx location /preview {# 本地运行的kkFileView的地址proxy_pass http://127.0.0.1:8012;proxy_set_header Host $host;proxy_set_header X-…

Sang.UAParser一个简单的.NET用户代理解析器

本文主要介绍了 Sang.UAParser 这个简单的.NET用户代理解析器&#xff0c;可以用来解析用户代理字符串&#xff0c;提取出其中的浏览器、操作系统等信息。这个库的使用非常简单&#xff0c;只需要引用 NuGet 包&#xff0c;然后调用相应的方法即可。 1. 简介 Sang.UAParser 是…

YOLO | YOLO目标检测算法(基础入门)

github&#xff1a;https://github.com/MichaelBeechan CSDN&#xff1a;https://blog.csdn.net/u011344545 YOLO目标检测算法 深度学习经典检测方法1、两阶段&#xff08;Two-stage&#xff09;2、单阶段&#xff08;One-stage&#xff09; 深度学习经典检测方法 1、两阶段&a…

blender修改材质时出现颜色丢失的问题

对于建立的三维模型&#xff0c;我们一般是直接使用gazebo时不会有材质的颜色信息&#xff0c;这一点还是比较烦的&#xff0c;所以这里通过blender来重新给模型上色 首先需要去安装blender&#xff1a; sudo apt install blender对于导入的模型,修改了材质后依然表现为没有颜…

如何在uni-app中使用echarts(泪的教训,保证可用,不能用来打我,保姆级教程)

线上最终实现图&#xff1a; 项目背景&#xff1a;uni-appvue2uv-uiecharts 每步都有坑&#xff0c;跟着看完 实现过程 使用了uniapp插件市场的 echarts-for-wx插件&#xff0c;以下两种方式均可 下载后将以下文件拷贝到项目的components下 如果是zip下载&#xff0c;解压后…

leetcode60.不同路径

题目描述 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 问总共有多少条不同的路径? 示例 1: 输入:m = 3, n = 7 输出:28 示例 2:…

OpenCV绘图函数(10)根据指定像素计算字体大小的函数getFontScaleFromHeight()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 计算特定字体的大小以达到给定的像素高度。 函数原型 double cv::getFontScaleFromHeight (const int fontFace,const int pixelHeight,const…

C_03_函数学习

函数 优点&#xff1a; 降低代码耦合度降低代码冗余度提高代码复用率提高代码可读性 思想&#xff1a; 封装【包装】 声明&#xff1a; 语法&#xff1a; extern 函数名(形参列表)&#xff1b;// 注意&#xff1a;此时 形参列表中变量名可以忽略不写&#xff1b;定义&#xff1…

风控领域特征工程

在金融行业&#xff0c;风险控制&#xff08;风控&#xff09;是核心环节&#xff0c;它关乎资产安全、合规性以及机构的长期稳健发展。随着大数据时代的到来&#xff0c;金融机构面临着前所未有的数据量和复杂性。在这样的背景下&#xff0c;风控领域特征工程应运而生&#xf…

构建高效公正的会议抽奖系统:提升活动互动性与参与度

在各类会议、庆典及企业活动中&#xff0c;抽奖环节往往是吸引参与者兴趣、增强活动氛围的关键一环。一个高效、公正且充满趣味性的会议抽奖系统&#xff0c;不仅能够极大地提升活动的互动性与参与度&#xff0c;还能加深品牌印象&#xff0c;促进与会者之间的交流与合作。本文…

数据结构线性表(1)顺序表

&#x1f30f;个人博客主页&#xff1a;意疏-CSDN博客 希望文章能够给到初学的你一些启发&#xff5e; 如果觉得文章对你有帮助的话&#xff0c;点赞 关注 收藏支持一下笔者吧&#xff5e; 阅读指南&#xff1a; 开篇说明线性表的定义线性表的顺序存储结构&#xff08;顺序表…

全程云OA UploadEditorFile接口存在任意文件上传漏洞 附POC

@[toc] 全程云OA UploadEditorFile接口存在任意文件上传漏洞 附POC 免责声明:请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学…

中标麒麟v10 sp3 部署cuda cudnn tensorrt deepstream

1.驱动安装 ./NVIDIA-Linux-x86_64-530.41.03.run 更改,不检测 ./NVIDIA-Linux-x86_64-530.41.03.run -no-x-check 禁用nouveau 创建文件/etc/modprobe.d/blacklist-nouveau.conf,添加如下文本: blacklist nouveau options nouveau modeset=0 重新生成initramfs $ su…

压测工具大比武!谁是市场主流?

阿里云PTS 性能测试PTS&#xff08;Performance Testing Service&#xff09;是阿里云一款商业化的性能测试工具。支持按需发起压测任务&#xff0c;可支持百万并发、千万TPS流量发起能力&#xff0c;100%兼容JMeter。PTS支持的场景编排、API调试、流量定制、流量录制等功能&am…

【HarmonyOS】模仿个人中心头像图片,调用系统相机拍照,从系统相册选择图片和圆形裁剪显示 (一)

【HarmonyOS】头像图片&#xff0c;调用系统相机拍照&#xff0c;从系统相册选择图片和圆形裁剪显示 &#xff08;一&#xff09; Demo效果展示&#xff1a; 方案思路&#xff1a; 使用photoAccessHelper实现系统相册选择图片的功能。此API可在无需用户授权的情况下&#xff…

【Material-UI】Slider中的 Continuous Sliders 与 Sizes 详解

文章目录 一、Slider 组件概述1. 组件介绍2. 使用场景 二、Continuous Sliders 的详解1. Continuous Sliders 的作用2. Continuous Sliders 的基本用法3. 禁用状态下的 Continuous Sliders4. Continuous Sliders 的实际应用5. Continuous Sliders 的优缺点 三、Slider 的尺寸控…

vue 组件拖拽

需求&#xff1a;将一个组件拖动至页面任何位置&#xff0c;记录并回显 要拖动的组件&#xff1a; <divclass"left left_module_text"draggable"true"dragstart"dragstart($event)"dragend.stop"dragend1($event, { left: 0, top: 0 },…

macos 自定义用户目录方法, /Users/xxx 用户文件存储路径自定义方法

在macos中,我们的用户数据全部都存储在了 /Users/xxx 文件夹下, 而这个文件夹默认是和我们的macos系统文件存放在了同一个磁盘卷宗(分区)里面的, 这个就给我们在遭遇系统崩溃或者其他情况重装系统时带来了极大的不便, 如果是格式化后全新安装 数据全部丢失,如果是覆盖安装同…

刘文超行测笔记

一、判断推理 1.位置规律 2.样式规律 特征&#xff1a;元素组成相似 &#xff08;1&#xff09;加减同异 &#xff08;2&#xff09;黑白运算 1.特征&#xff1a;图形轮廓和分隔区域相同&#xff0c;内部的颜色不同 2.方法&#xff1a;相同位置运算 区分&#xff1a; 黑块…