当前位置:   article > 正文

C++ WebServer的细节理解

C++ WebServer的细节理解

1. 文件描述符-非阻塞模式

fcntl(fd, F_SETFL, fcntl(fd, F_GETFD, 0) | O_NONBLOCK);
  • 1

代码解释:
上面这句代码,先查询文件描述符 fd 当前的标志,然后将 O_NONBLOCK 标志加入,并通过 F_SETFL 更新文件描述符,最终实现将该文件描述符切换为非阻塞模式。

O_NONBLOCK 设置在文件描述符上时,后续对该文件描述符的 I/O 操作(读写等)会变为非阻塞模式。

在非阻塞模式下,如果 I/O 操作不能立即完成(例如,因为没有数据可读 或 写缓冲区满),系统不会让调用进程阻塞等待,而是立即返回一个错误(通常为 EAGAIN 或 EWOULDBLOCK)。
这样,进程可以避免因等待 I/O 完成而被长时间阻塞,实现更高的响应性和并发性。

非阻塞模式—应用场景举例:

int accept(int sockfd, struct sockaddr* addr, socklen_t* len)
  • 1

accept()函数,用于接受客户端的连接请求。默认情况下,accept() 是阻塞的。这意味着,如果没有待处理的连接请求(即没有客户端尝试连接到服务器),accept() 会一直阻塞,直到有新的连接请求到达或发生其他特定条件(如超时)为止。

当 accept() 处于阻塞状态时,它所在的线程是不能去做其他事情的。
若要改变这种行为,可以采用以下方法:

  1. 非阻塞模式:如前所述,可以使用 fcntl() 函数将套接字设置为非阻塞模式(使用 O_NONBLOCK 标志)。在这种模式下,accept() 调用将不会阻塞,而是立即返回。如果此时没有待处理的连接请求,accept() 将返回一个错误(通常为 EAGAINEWOULDBLOCK)。这样,线程可以在没有可用连接时执行其他任务,然后在适当的时候再次尝试 accept()

  2. 异步 I/O:如 Linux 中的 io_uring 或 Windows 中的 overlapped I/O,可以异步地执行 accept(),使得线程在发起 accept() 请求后可以继续执行其他任务,然后通过回调、事件通知等方式获知 accept() 的结果。

2. mmap() 和 munmap()

mmap() 和 munmap() 是用于内存映射操作的系统调用函数。
mmap() 允许将文件或其他对象直接映射到进程的虚拟地址空间中,从而实现高效的数据访问。

void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
  • 1
  • void* addr

    • 指定映射开始的期望虚拟地址。如果设为 NULL,内核将自动选择一个合适的地址。
  • size_t length

    • 指定映射区域的长度。必须是页面大小的整数倍。
  • int prot

    • 控制映射区域的访问权限:
      • PROT_READ:允许读取。
      • PROT_WRITE:允许写入。
      • PROT_NONE:禁止访问。
  • int flags

    • 控制映射行为和映射内容的处理方式:
      • MAP_SHARED:创建一个可共享的映射,对映射区域的修改会影响到所有映射此区域的进程,并且可能会同步回文件。
      • MAP_PRIVATE:创建一个私有映射,对映射区域的修改仅影响当前进程,不会改变底层文件。
  • int fd

    • 用于指定要映射的文件。如果使用匿名映射,可以传入 -1 或者一个未打开的文件描述符。
  • off_t offset

    • 文件中的偏移量,映射从该位置开始。通常应是文件系统块大小的整数倍。

返回值:

  • 成功时返回映射区域的起始地址。
  • 出现错误时返回 MAP_FAILED(通常是一个负值,如 (void*) -1)。

int munmap(void* addr, size_t length);
  • 1
  • void* addr
    • 指定要解除映射的内存区域的起始地址,该地址应是之前 mmap() 调用返回的地址。
  • size_t length
    • 指定解除映射的区域长度,应与对应的 mmap() 调用中的 length 参数一致。

返回值: 成功时返回 0。出现错误时返回 -1


mmap() 常用于以下场景:

  • 高效文件访问:将文件直接映射到内存,避免了常规的文件I/O操作,对于大数据文件或频繁访问的文件尤其高效。
  • 进程间通信(IPC):通过映射同一份共享内存,不同进程可以直接通过内存地址来交换数据,无需通过管道、套接字等传统IPC机制。

例如,映射一个已打开文件的部分内容到内存中:

#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <cerrno>

int main() {
    const char* filename = "/path/to/file";
    int fd = open(filename, O_RDONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    struct stat sb;
    if (fstat(fd, &sb) == -1) {
        perror("fstat");
        close(fd);
        return 1;
    }

    // 映射整个文件
    void* mapped_region = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (mapped_region == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return 1;
    }

    // 使用映射区域...
    // ...

    // 解除映射
    if (munmap(mapped_region, sb.st_size) == -1) {
        perror("munmap");
        close(fd);
        return 1;
    }

    close(fd);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

在WebServer中,使用方法也类似;下面对上面的代码做简单的分析:

int stat(const char *path, struct stat *buf);
  • 1

stat() 是一个标准C库函数,通常在 <sys/stat.h> 头文件中声明,用于获取文件或目录的状态信息。

  • const char *path: 一个指向包含文件路径的指针。可以是绝对路径,也可以是相对于当前工作目录的相对路径。
  • struct stat *buf: 一个指向 struct stat 结构体的指针,用于接收由 stat() 函数填充的关于指定路径对象的信息,例如:
    – 文件大小(st_size
    – 最近访问、修改和状态更改时间(st_atime, st_mtime, st_ctime
    – 权限位(st_mode

函数返回值:

  • 如果成功,返回0;否则,返回一个非零值(通常是错误代码),表示发生了某种错误(如权限不足、路径不存在等)。

3. io[]

4. 服务器如何发送响应

5. 服务器对连接的处理流程

  1. 读取缓冲区的信息(http请求)
  2. 解析请求
  3. 根据解析内容,服务器端进行可能的操作(如在数据库内进行增删改查)
  4. 生成响应
本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/478307
推荐阅读
  

闽ICP备14008679号