Echo服务器学习__01(基础)

ASIO是一个跨平台,主要用于实现异步网络和其他一些底层I/O操作的C++库

可以基于ASIO实现Echo服务端,在这之前,学习一些基础的知识和概念

1:IO多路复用

简单的来说,一个线程同时监听多个I/O事件就是I/O多路复用。任何一个I/O流操作不需要阻塞等待每个I/O流的完成,即非阻塞

2:并发、并行、串行

并发(Concurrency),并发偏重于多个任务交替执行,并发的"同时"是经过上下文快速切换,使得看上去多个进程同时都在运行的现象,是一种OS欺骗用户的现象

并行(Parallelism),并行指的是多个任务同时在多个处理单元上同时执行的能力,并行的"同时"是同一时刻可以多个进程在运行(处于running)

串行(Sequential),当任务按照固定的顺序依次执行,每个任务的开始都要等待上一个任务的完成,这就是串行执行

3:文件描述符(file descriptor,简称FD)

Linux 系统中,把一切都看做是文件(一切皆文件),当进程打开现有文件或创建新文件时,内核向进程返回一个文件描述符,文件描述符就是内核为了高效管理已被打开的文件所创建的索引,用来指向被打开的文件,所有执行I/O操作的系统调用都会通过文件描述符。

FD也可以被称为文件句柄(file handle)、文件指针(file pointer)或文件引用(file reference)。简单来说,它是操作系统为了管理 I/O 操作而维护的一个表中的索引,代表着系统中打开的文件的一个“门牌号”。

image.png

4:服务器套接字(Socket)

是在网络编程中用于实现网络通信的一种抽象接口,它提供了一种统一的编程接口,使得应用程序可以在网络上进行数据传输和通信。套接字的实现通常涉及到操作系统内核、网络协议栈和网络硬件设备等多个层面

Socket的过程形容为打电话,流程就如下:

  1. 创建Socket:就像拿起一部电话,准备开始通话。

  2. 绑定地址和端口:类似于确定你要打电话的号码。

  3. 监听连接请求(对于服务器端):准备接听来自其他人的电话。

  4. 接受连接(对于服务器端):接听来自其他人的电话。

  5. 连接到远程主机(对于客户端):拨打某个号码开始通话。

  6. 发送和接收数据:你可以通过电话传递信息了。

  7. 关闭连接:挂断电话,结束通话。

4:select(80年代),epoll(多用),poll

①Select的实现:

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

int main() {
    // 创建 TCP 服务器套接字
    int server_socket = socket(AF_INET, SOCK_STREAM, 0); //AF_INET: 是一个宏,表示使用 IPv4 地址族(Address Family),套接字将使用 IPv4 地址来标识主机和端口,也可以使用AF_INET6表示IPv6
                                                         //SOCK_STREAM: 是一个宏,表示创建一个基于流的套接字,用于 TCP 协议,如果是SOCK_DGRAM,就是使用UDP协议
                                                         //0: 是套接字的类型标志,通常为 0
    if (server_socket == -1) {      //-1表示没有成功创建socket
        perror("socket failed");    
        exit(EXIT_FAILURE);        //exit() 函数用于终止程序的执行,并返回一个整数参数作为程序的退出状态码。
                                    //EXIT_FAILURE 是一个宏,表示程序执行失败的状态码,通常定义为非零值。
    }

    // 设置服务器地址和端口
    struct sockaddr_in server_address;    //用于存储服务器地址信息的数据结构
    memset(&server_address, 0, sizeof(server_address));    //将 server_address 结构体中的所有字节都设置为零的操作,初始化结构体
    server_address.sin_family = AF_INET;                   //这行代码设置了地址族为 AF_INET,表示使用 IPv4 地址族。AF_INET 是一个常量,代表 IPv4 地址族。
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);    //这行代码设置了 IP 地址为 INADDR_ANY,表示服务器将接受来自任意网络接口的连接请求
    server_address.sin_port = htons(8080);                 //这行代码设置了端口号为 8080,并将其从主机字节序转换为网络字节序

    // 绑定地址和端口
    if (bind(server_socket, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) {    //bind() 函数的第二个参数是一个指向 struct sockaddr 类型的指针
                                                                                                   //&server_address 返回的是指向server_address 结构体变量的指针,也就是指向该变量在内存中的地址
                                                                                                   //server_address 的地址转换为一个指向通用地址结构体的指针,以便能够传递给网络编程函数
        perror("bind failed");
        close(server_socket);
        exit(EXIT_FAILURE);
    }

    // 监听连接请求
    if (listen(server_socket, 5) == -1) {
        perror("listen failed");
        close(server_socket);
        exit(EXIT_FAILURE);
    }

    std::cout << "Server started, waiting for connections..." << std::endl;

    // 创建要监视的文件描述符集合,并将服务器套接字加入集合中
    std::vector<int> client_sockets;
    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(server_socket, &read_fds);
    int max_fd = server_socket;

    while (true) {
        // 使用 select 函数等待文件描述符就绪
        fd_set tmp_fds = read_fds;    //fd_set 是一个位图(bitmap),它将每个文件描述符映射到一个位(bit)。在 fd_set 中,每个位代表一个文件描述符
                                      //当位被设置为 1 时,表示相应的文件描述符是待监听的;当位被设置为 0 时,表示相应的文件描述符不需要监听
        if (select(max_fd + 1, &tmp_fds, NULL, NULL, NULL) == -1) {    //max_fd 表示监视的文件描述符集合中的最大文件描述符
                                                                       //fd_set 结构的指针,select() 函数将检查 tmp_fds 集合中的文件描述符,判断是否有文件描述符处于就绪状态    
                                                                       //第三个参数用于指定要监视的写和异常事件的文件描述符集合
                                                                       //第四个参数用于指定 select() 函数的超时时间。在这里传入 NULL 表示 select() 函数将一直阻塞,直到有文件描述符就绪或者出错为止
                                                                       //第五个参数用于指定 select() 函数的超时时间精度
            perror("select failed");
            close(server_socket);
            exit(EXIT_FAILURE);
        }

        // 遍历就绪的文件描述符
        for (int fd = 0; fd <= max_fd; ++fd) {
            if (FD_ISSET(fd, &tmp_fds)) {    //FD_ISSET() 是一个宏,用于检查指定的文件描述符是否在给定的 fd_set 集合中被设置
                if (fd == server_socket) {     如果是服务器套接字,表示有新的连接请求
                    int client_socket = accept(server_socket, NULL, NULL);     //调用 accept() 函数来接受客户端的连接请求,并创建一个新的套接字用于与客户端进行通信。
                                                                               //第二个参数表示指向 struct sockaddr 类型的指针,用于获取客户端的地址信息。在这里传入 NULL 表示不获取客户端的地址信息。
                                                                               //第三个参数指向 socklen_t 类型的指针,用于获取客户端地址结构体的大小。在这里传入 NULL 表示不获取客户端地址结构体的大小
                    if (client_socket == -1) {
                        perror("accept failed");
                        close(server_socket);
                        exit(EXIT_FAILURE);
                    }
                    std::cout << "New connection" << std::endl;
                    client_sockets.push_back(client_socket);
                    FD_SET(client_socket, &read_fds);    //将 client_socket 添加到 read_fds 集合中,以便在调用 select() 函数时监视它的就绪状态。
                    max_fd = std::max(max_fd, client_socket);
                } else {
                    // 如果是客户端套接字,表示有数据可读
                    char buffer[1024];
                    ssize_t bytes_received = recv(fd, buffer, sizeof(buffer), 0);
                    if (bytes_received <= 0) {
                        // 客户端关闭连接
                        std::cout << "Connection closed" << std::endl;
                        close(fd);
                        FD_CLR(fd, &read_fds);
                        client_sockets.erase(std::remove(client_sockets.begin(), client_sockets.end(), fd), client_sockets.end());
                    } else {
                        buffer[bytes_received] = '\0';
                        std::cout << "Received from client: " << buffer << std::endl;
                    }
                }
            }
        }
    }

    return 0;
}

所以我们能看出select的缺点:

    bitmap缺点,只能是1024

    FDset不可重用,每次都需要新声明

    用户态到内核太切换要开销

    select()函数每次都要重新遍历文件描述符

②poll

struct pollfd{
    int fd;
    short events;
    short revents;
};    //fd 表示文件描述符,events 表示要监视的事件,revents 表示实际发生的事件
for(i = 0; i<5,i++)
    {
        memset(client,0,sizeof(client));    //memset 是 C/C++ 标准库中的一个函数,用于将一块内存区域的内容设置为指定的值。
        addrlen = sizeof(client);
        pllfds[i] = accept(sockfd,(struct sockaddr*)&client,&addelen);
        pooldfs[i].events = POLLIN;    //设置 poolfds[i].events 为 POLLIN,表示要监视该文件描述符的可读事件。
     }
     sleep(1);
     while(1){
     puts("round again");
     poll(poolfds,5,50000); //阻塞函数,等待文件描述符有数据
     for(i = 0;i<5;i++)    //遍历 poolfds 数组,检查每个文件描述符的事件。如果 revents 中包含 POLLIN 事件,表示该文件描述符有数据可读。
     {
         if(poolfds[i].revents & POOLIN){
             poolfds[i].revents = 0;    //清空 revents,就是置位
             memset(buffer,0,MAXBUF);
             read(poolfds[i].fd,buffer,MAXBUF); //读取数据
             puts(buffer);
             }
        }
  }

③epoll

struct epoll_event events[5];    
int epfd = epoll_create(10);    //参数 10 表示 epoll 实例的大小,但是在实际中这个参数并不会限制 epoll 实例的大小,内核会根据需要调整大小。
..
..
for(i = 0;i<5;i++)
{
    static struct epoll_event ev;
    memset(&client,0,sizeof(client));
    addlen = sizeof(client);
    ev.data.fd = accept(sockfd,(struct sockaddr*(&client, &addrlen);    //向 epoll 实例中添加了 5 个文件描述符,这些文件描述符是通过 accept() 函数接受客户端连接而得到的
    ev.events = EPOLLIN;    //每个事件的类型都设置为 EPOLLIN,表示监听可读事件
    epoll_ctl(epfd,EPOLL_CTL_ADD,ev.data.fd,&ev);    //epoll_ctl() 函数将其添加到 epoll 实例中
}
while(true){
    puts("round again");
    nfds = epoll_wait(epfd,events,5,10000);
    for(i == 0;i<nfds;i++){
        memset(buffer,0,MAXBUF);
        read(events[i].data.fd,buffer,MAXBUF);
        puts(buffer);
        }
}

epoll中最重要的函数就是epoll_wait()函数

他的原型是

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

epfd:是 epoll 实例的文件描述符,即通过 epoll_create() 创建的 epoll 实例。

events:是一个结构体数组,用于存储发生的事件信息

maxevents:是 events 数组的大小,即最多能够存储多少个事件。timeout:是超时时间,以毫秒为单位。如果设置为 -1,表示永远等待,直到有事件发生;如果设置为 0,表示立即返回,不阻塞;如果大于 0,表示等待指定时间后返回

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

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

相关文章

CSS学习(2)-盒子模型

1. CSS 长度单位 px &#xff1a;像素。em &#xff1a;相对元素 font-size 的倍数。rem &#xff1a;相对根字体大小&#xff0c;html标签就是根。% &#xff1a;相对父元素计算。 注意&#xff1a; CSS 中设置长度&#xff0c;必须加单位&#xff0c;否则样式无效&#xff…

设计模式在芯片验证中的应用——装饰器

一、装饰器模式 装饰器模式(Decorator)是一种结构化软件设计模式&#xff0c;它提供了一种通过向类对象添加行为来修改类对象的方法&#xff0c;而不会影响同一类的其它对象行为。该模式允许在不修改抽象类的情况下添加类功能。它从本质上允许基类代码对不可预见的修改具有前瞻…

鸿蒙开发入门教程—瀑布流的实战案例

给大家分享一下瀑布流的实战案例&#xff0c;和官方文档略有不同&#xff0c;本文数据直接从api接口获取&#xff0c;更接近实战。需要注意的是&#xff0c;要实现瀑布流&#xff0c;接口最好将图片尺寸一起返回。 本文效果图&#xff1a; 首先要实现IDataSource接口的对象&am…

c语言(数据在内存中的存储)

1. 整数在内存中的存储 整数的2进制表⽰⽅法有三种&#xff0c;即原码、反码和补码 三种表⽰⽅法均有符号位和数值位两部分&#xff0c;符号位都是⽤0表⽰“正”&#xff0c;⽤1表⽰“负”&#xff0c;⽽数值位最 ⾼位的⼀位是被当做符号位&#xff0c;剩余的都是数值位。 正整…

EI期刊复现:面向配电网韧性提升的移动储能预布局与动态调度策略程序代码!

适用平台&#xff1a;MatlabYalmipCplex/Gurobi/Mosek 程序提出一种多源协同的两阶段配电网韧性提升策略。在灾前考虑光伏出力不确定性与网络重构&#xff0c;以移动储能配置成本与负荷削减风险成本最小为目标对储能的配置数量与位置进行预布局&#xff1b;在灾后通过多源协同…

Javascript抓取京东、淘宝商品数据(商品采集商品详情图片抓取)

之前用的方法&#xff1a; let temp []var lists $(#J_goodsList li.gl-item)$.each(lists,function(idx,item){ temp.push({ id:$(item).data(sku), goods_img:$(item).find(img).attr(src), goods_name:$(item).find(.p-name em).text(), market_price:$(item).fi…

AI检测识别技术,为智能化视频生产赋能

在科技飞速发展的今天&#xff0c;智能化生产已经成为企业提高效率、降低成本、增强竞争力的关键所在。美摄科技&#xff0c;作为一家在音视频处理技术领域保持领先的创新型企业&#xff0c;不仅致力于提供卓越的音视频处理技术&#xff0c;更在AI检测识别领域积累了深厚的实力…

数据分析-Pandas序列滑动窗口配置参数

数据分析-Pandas序列滑动窗口配置参数 数据分析和处理中&#xff0c;难免会遇到各种数据&#xff0c;那么数据呈现怎样的规律呢&#xff1f;不管金融数据&#xff0c;风控数据&#xff0c;营销数据等等&#xff0c;莫不如此。如何通过图示展示数据的规律&#xff1f; 数据表&…

Flutter Widget:StatefulWidget StatelessWidget

Widget 概念 Widget 将是构建Flutter应用的基石&#xff0c;在Flutter开发中几乎所有的对象都是一个 Widget 。 在Flutter中的widget 不仅表示UI元素&#xff0c;也表示一些功能性的组件&#xff0c;如&#xff1a;手势 、主题Theme 等。而原生开发中的控件通常只是指UI元素。…

C++作业day6

编程1&#xff1a; 封装一个动物的基类&#xff0c;类中有私有成员&#xff1a;姓名&#xff0c;颜色&#xff0c;指针成员年纪 再封装一个狗这样类&#xff0c;共有继承于动物类&#xff0c;自己拓展的私有成员有&#xff1a;指针成员&#xff1a;腿的个数&#xff08;整型 …

nodeJs 学习

常用快捷键 二、fs模块 回调函数为空&#xff0c;则表示写入成功&#xff01; 练习 const fs require(fs); fs.readFile(../files/成绩.txt, utf-8, (err, dataStr) > {if (err) {console.log(读取失败);return err;}console.log(读取成功);const arr dataStr.split( )co…

C++_day6:2024/3/18

作业1&#xff1a;编程题&#xff1a; 以下是一个简单的比喻&#xff0c;将多态概念与生活中的实际情况相联系&#xff1a; 比喻&#xff1a;动物园的讲解员和动物表演 想象一下你去了一家动物园&#xff0c;看到了许多不同种类的动物&#xff0c;如狮子、大象、猴子等。现在…

一般做策划的的,上哪儿找策划方案借鉴?

许多策划人担忧&#xff0c;借鉴他人的经验和方案会会削弱自己的创新能力和独立思考。 但是&#xff0c;实际上&#xff0c;借鉴他人的经验和方案可以为自己提供更多的灵感和创意&#xff0c;同时也可以提高策划的成功率。 这里推荐策划人必备的宝藏网站&#xff08;专治不会…

Echarts横向柱形图

项目原型图如下: 实际上就是设置两个y轴&#xff0c;第一个显示底色柱子&#xff0c;另一个只显示真实数据的柱子,在这里只显示y轴,x轴不显示.. this.middleLeftOption {tooltip: {trigger: axis,axisPointer: {}},legend: {data: [回退次数],y: bottom},grid: {left: 3%,righ…

微信小程序注册流程

1. 微信小程序注册流程 1.1. 材料 新开通小程序则需要提供未注册过微信、从未绑定过微信公众号的邮箱号&#xff1b;法人的微信号&#xff08;必须已绑定银行卡&#xff09;&#xff1b;法人的身份证号码、身份证正反面照片&#xff1b;管理员手机号和微信绑定的&#xff1b;公…

Docker基本配置及使用

Docker基本配置及使用 使用步骤 1.卸载旧版 代码如下&#xff1a;首先如果系统中已经存在旧的Docker&#xff0c;则先卸载 yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engin…

嵌入式DSP教学实验箱操作教程:2-20 数模转换实验(模拟SPI总线输出电压值)

一、实验目的 掌握GPIO模拟SPI总线的使用&#xff0c;了解AD5724的芯片特性和使用&#xff0c;并实现基于AD5724输出电压值。 二、实验原理 StarterWare StarterWare是一个免费的软件开发包&#xff0c;它包含了示例应用程序。StarterWare提供了一套完整的GPIO寄存器配置接…

数据库系统概论-练手题集合【期末复习|考研复习】

前言 总结整理不易&#xff0c;希望大家点赞收藏。 给大家整理了一下数据库系统概论中的练手题&#xff0c;以供大家期末复习和考研复习的时候使用。 数据库系统概论系列文章传送门&#xff1a; 第一章 绪论 第二/三章 关系数据库和标准语言SQL 第四/五章 数据库安全性和完整性…

蓝桥杯之冲刺

文章目录 动态规划01背包小练一下 网格图上的DP 动态规划 01背包 由于不能拆开&#xff0c;那就是DP 问题&#xff0c;如果能拆开&#xff0c;那就是贪心问题 小练一下 import os import sys# 请在此输入您的代码N,V map(int,input().split())w [] v [] w.append(0) v.appen…

数据结构(三)复杂度的深层次剖析

之前发布了数据结构&#xff08;一&#xff09;&#xff0c;很多同学反响不够清晰&#xff0c;那今天就发一篇对复杂度专题的博客&#xff0c;希望对大家理解复杂度提供一些帮助。 时间复杂度 我们先来一个理解一个复杂度&#xff0c;二分查找的复杂度&#xff08;之前写过二…
最新文章