1. C++实现一个最简单的服务器和五种I/O模型

embedded/2024/12/22 19:56:12/

这只是一个简单的服务器
server.h
这个里面就是实现一个Server的类。然后一个run方法

/***Server class*@author lipu123*@date 2024-5-24*/#ifndef SERVER_H
#define SERVER_H
namespace avdance{class Server{
public:Server();~Server();
public:void run();
};}#endif // SERVER_H

server.cpp,这个里面实现了Server中的具体函数

/***Server class*@author lipu123*@date 2024-5-24*/#include "server.h"
#include<iostream>
#include<unistd.h>namespace avdance{Server::Server() {std::cout<<"construct...."<<std::endl;
}
Server::~Server(){std::cout<<"destruct...."<<std::endl;
}
void Server::run(){while(1){std::cout<<"runing..."<<std::endl;::usleep(1000000);//sleep one second}return ;
}
}

mian.cpp

#include<iostream>#include "server.h"int main(int argc,char* argv[]) {avdance::Server* server=new avdance::Server();if(server){server->run();}return 0;
}

五种 I/O 模型

先花费点时间了解这几种 I/O 模型,有助于后面的理解。

阻塞 I/O 与非阻塞 I/O

阻塞和非阻塞的概念能应用于所有的文件描述符,而不仅仅是socket。我们称阻塞的文件描述符为阻塞 I/O,称非阻塞的文件描述符为非阻塞I/O
socket在创建的时候默认是阻塞的,我们可以给socket系统调用的第 2 个参数传递 SOCK_NONBLOCK标志,或者通过fcntl系统调用的F_SETFL命令将其设置为非阻塞的。

  • 针对阻塞 I/O 执行的系统调用可能因为无法立即完成而被操作系统挂起,直到等待的事件发生为止。可能被阻塞的系统调用为acceptsendrecvconnect
  • 针对非阻塞I/O执行的系统调用则总是立即返回,而不管事件是否已经发生。如果事件没有立即发生,这些系统调用就返回-1,和出错的情况一样。此时我们必须根据errno来区分这两种情况。
    • acceptsendrecv而言,事件未发生时errno通常被设置为EAGAIN(意为“再来一次”)或者 EWOUDBLOCK(意为“期望阻塞”);
    • connect而言,errno则被设置成EINPROGRESS(意为“在处理中”)。

很显然,只有在事件已经发生的情况下操作非阻塞I/O(读、写等),才能提高程序的效率。因此,非阻塞I/O 通常要和其他I/O通知机制一起使用,比如I/O复用和SIGIO信号。

笔者认为,我们使用非阻塞I/O的最佳情况是:【当我们进行系统调用的时候,它所需要的事件已经发生了】,这样系统调用就不会被阻塞,直接进行处理。比如accept函数,I/O复用的好处就是当我们调用accept函数的时候,已经有客户端在请求连接,这样直接调用accept,提高运行效率。

I/O 复用

I/O 复用是一种 I/O 通知机制,而且是最常用的通知机制。

I/O 复用是指应用程序通过 I/O 复用函数(select、poll、epoll_wait)向内核注册一组事件,内核通过 I/O 复用函数把其中就绪的事件通知给应用程序。

需要注意的是 I/O 复用函数本身是阻塞的,它们能提高程序效率的原因在于它们具有同时监听多个 I/O 事件的能力。

信号驱动 I/O

为一个目标文件描述符指定宿主进程,那么被指定的宿主进程将捕获到 SIGIO 信号。这样,当文件描述符上有事件发生时,SIGIO 信号的信号处理函数将被触发,我们也就可以在该信号处理函数中对目标文件描述符执行非阻塞 I/O 操作了。

异步 I/O

理论上讲,阻塞 I/O、非阻塞 I/O、信号驱动 I/O 和 I/O 复用都是同步 I/O。

  • 同步I/O:内核向应用程序通知的是就绪事件,比如只通知有客户端连接,要求用户代码自动执行I/O操作(将数据从内核缓冲区读入用户缓冲区,或将数据从用户缓冲区写入内核缓冲区);
  • 异步I/O:内核向应用程序通知的是完成事件,比如读取客户端的数据之后才通知应用程序,由内核完成I/O操作(数据在内核缓冲区和用户缓冲区之间的移动是由内核在“后台”完成的)。

对异步 I/O 而言,用户可以直接对 I/O 执行读写操作,这些操作告诉内核用户读写缓冲区的位置,以及 I/O 操作完成之后内核通知应用程序的方式。异步 I/O 的读写操作总是立即返回,不论 I/O 是否是阻塞的,因为真正的读写操作已经由内核接管。

https://www.cnblogs.com/lafiteee/p/16222757.html


http://www.ppmy.cn/embedded/43195.html

相关文章

力扣239. 滑动窗口最大值

Problem: 239. 滑动窗口最大值 文章目录 题目描述思路复杂度Code 题目描述 思路 1.编写实现优先队列类&#xff1a; 1.1.实现push(int n):将元素n添加到队列尾&#xff0c;同时将n前面大于n的元素删除 1.2.实现int max():将队列头元素取出&#xff08;由于实现了push所以此时队…

CLIP 论文的关键内容

CLIP 论文整体架构 该论文总共有 48 页&#xff0c;除去最后的补充材料十页去掉&#xff0c;正文也还有三十多页&#xff0c;其中大部分篇幅都留给了实验和响应的一些分析。 从头开始的话&#xff0c;第一页就是摘要&#xff0c;接下来一页多是引言&#xff0c;接下来的两页就…

【Rust日报】函数指针与闭包的区别

函数指针与闭包的区别 在 Rust 中&#xff0c;函数指针用于直接指向一个确定签名的函数&#xff0c;适用于不需要捕获外部环境的场景。相对闭包来说&#xff0c;函数指针语法简单&#xff0c;性能略高但不能保持状态。闭包则功能更强大&#xff0c;能够捕获和使用其定义时的环境…

Day01-python函数

目录 一、函数介绍 二、函数使用 2-1 语法格式 2-2 函数的基本定义和使用 2-3 函数参数 2-4 参数接收数据类型 2-5 函数的返回值 2-6 函数的文档 2-7 函数的嵌套调用 三、变量作用域 3-1 变量的引用 3-2 变量的分类 四、函数参数详解 五、函数的数据传递 5-1 将函…

C++ | Leetcode C++题解之第116题填充每个节点的下一个右侧节点指针

题目&#xff1a; 题解&#xff1a; class Solution { public:Node* connect(Node* root) {if (root nullptr) {return root;}// 从根节点开始Node* leftmost root;while (leftmost->left ! nullptr) {// 遍历这一层节点组织成的链表&#xff0c;为下一层的节点更新 next…

Python实现对Word文档内容出现“重复标题”进行自动去重(3)

前言 本文是该专栏的第3篇,后面会持续分享Python办公自动化干货知识,记得关注。 在本文中,笔者将针对word文档(docx格式)的正文内容中的“标题”,进行自动去重。具体怎么实现,笔者接下来结合实际案例进行详细说明。 如上图所示,有时候word文档的标题出现重复显示,而现…

无线蓝牙耳机品牌推荐:倍思M2s Pro,让旅途更添乐趣

随着端午节的临近,许多人开始规划起出游计划。出游除了要做好行程安排,还需准备一些实用的物品来提升旅途的舒适度。特别是在高铁等长途旅行中,一款优质的降噪蓝牙耳机无疑是消磨时光、享受音乐的绝佳选择。那么,在众多的无线蓝牙耳机品牌中,有哪些值得推荐的呢?今天,我们就来…

03-02-Vue组件之间的传值

前言 我们接着上一篇文章 03-01-Vue组件的定义和注册 来讲。 下一篇文章 04-Vue&#xff1a;ref获取页面节点–很简单 父组件向子组件传值 我们可以这样理解&#xff1a;Vue实例就是一个父组件&#xff0c;而我们自定义的组件&#xff08;包括全局组件、私有组件&#xff09;…