项目地址:https://github.com/lanofblue/SimpleWebServer
本文实现的文件在源码中的SimpleWebServer/file_manager
目录下
本文内容:定位服务器上的文件并获取文件内容
- 定位文件
- 获取文件信息和内容
上一篇文章中,我们已经解析了HTTP请求报文,并将文件名存入http_parser::m_url
中,那么我们接下来要在服务器中定位文件,并得到它的内容。在此项目中,我设定的服务器主页是homepage.html文件,那么以访问该文件为例。若访问网站的主页,经HTTP_parser
解析,我们可以得到m_url = "homepage.html";
定位文件
首先定义网站的根目录:这是客户端能够访问到的服务器的最顶层目录。客户端只能访问该目录下的子目录,不能访问其上层目录,如SimpleServer目录。
const char* doc_root = "/home/blue/SimpleServer/root";
homepage.html文件存放在root目录里,因此homepage.html在服务器中的文件路径即为
/home/blue/SimpleServer/root/homepage.html
定义m_real_file
来存放此路径:
strcpy(m_real_file, doc_root);
int len = strlen(doc_root);
strncpy(m_real_file + len, m_url, FILENAME_LEN - len - 1);
获取文件信息和内容
文件基本信息
在Linux系统中,文件的信息通过以下结构体保存
struct stat {
mode_t st_mode; // protection:客户是否有查看此文件的权限
off_t st_size; /* total size, in bytes:要传输的文件大小
// 在本项目中我们只关心上面两个成员
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
blksize_t st_blksize; /* blocksize for filesystem I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};
获取文件内容
我们要获取文件内容,必须建立文件与内存的映射
下面说一下内存映射的步骤:
用open
系统调用打开文件, 并返回描述符fd
用mmap
建立内存映射, 并返回映射首地址指针start
对映射(文件)进行各种操作, 显示(printf), 修改(sprintf)
用munmap(void *start, size_t lenght)
关闭内存映射
用close
系统调用关闭文件fd
void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
int munmap(void* start,size_t length);
mmap函数的参数 | 描述 |
---|---|
start | 指向欲映射的内存起始地址,通常设为 NULL ,代表让系统自动选定地址,映射成功后返回该地址 |
length | 代表将文件中多大的部分映射到内存 |
prot | 映射区域的保护方式 |
flags | 影响映射区域的各种特性。在调用mmap() 时必须要指定MAP_SHARED 或MAP_PRIVATE 。MAP_FIXED 如果参数start所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此旗标。MAP_SHARED 对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任何修改都不会写回原来的文件内容。 |
fd | 要映射到内存中的文件描述符 |
offset | 文件映射的偏移量 |
返回值:
若映射成功则返回映射区的内存起始地址,否则返回MAP_FAILED(-1),错误原因存于errno 中
(内存映射的内容比较复杂,详情可阅读这篇博客Linux 内存映射函数 mmap()函数详解)
根据以上步骤我们可以实现如下获取文件资源的函数
HTTP_CODE file_manager::locate_file() {
// 获取文件在服务器上的完整路径
strcpy(m_real_file, doc_root);
int len = strlen(doc_root);
strncpy(m_real_file + len, m_url, FILENAME_LEN - len - 1);
// 获取文件基本信息
if (stat(m_real_file, &m_file_stat) < 0) { // 文件不存在
return NO_RESOURCE;
}
// 检查权限
if (!(m_file_stat.st_mode & O_RDONLY)) { // 无权读取文件
return FORBIDDEN_REQUEST;
}
// 检查文件类型
if (S_ISDIR(m_file_stat.st_mode)) { // 文件类型是目录
return BAD_REQUEST;
}
// 建立文件内存映射
int fd = open(m_real_file, O_RDONLY);
m_file_address = (char*)mmap(0, m_file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
return FILE_REQUEST;
}
经过以上的操作,m_file_address
指向了文件在内存中的起始位置,m_file_stat.st_size
是文件的大小(字节),知道这两个重要的参数即可传输该文件的内容