• 1.14.C++项目:仿muduo库实现并发服务器之Util模块的设计


    一、Util模块

    在这里插入图片描述

    二、实现思想

    (一)管理

    1. 实现一些工具接口
    2. 读取文件内容
    3. 向文件写入内容
    4. URL编码
    5. URL解码
    6. 通过HTTP状态码获取描述信息
    7. 通过文件后缀名获取mime
    8. 判断一个文件是不是目录
    9. 判断一个文件是否是一个普通文件
    10. HTTP资源路径有效性判断

    (二)意义

    在协议支持模块中,当需要某些零碎功能的时候,便于使用!

    (三)功能设计

    class Util {
        public:
            // 字符串分割函数
            size_t Spilt();
            // 读取文件内容
            static bool ReadFile() {
    
            }
            // 向文件写入内容
            static bool  WriteFile();
            // URL编码
            static bool UrlEncode();
            // URL解码
            static bool UrlDecode();
            // 通过HTTP状态码获取描述信息
            static std::string StatusDesc();
            // 根据文件后缀名获取文件MINE
            static std::string ExtMine();
            // 判断一个文件是不是目录
            static bool IsDirectory();
            //判断一个文件是否是一个普通文件
            static bool IsRegular();
            //HTTP资源路径有效性判断
            static bool VaildPath();
    };
    
    • 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

    三、代码

    class Util {
        public:
            //字符串分割函数,将src字符串按照sep字符进行分割,得到的各个字串放到arry中,最终返回字串的数量
            static size_t Split(const std::string &src, const std::string &sep, std::vector<std::string> *arry) {
                size_t offset = 0;
                // 有10个字符,offset是查找的起始位置,范围应该是0~9,offset==10就代表已经越界了
                while(offset < src.size()) {
                    size_t pos = src.find(sep, offset);//在src字符串偏移量offset处,开始向后查找sep字符/字串,返回查找到的位置
                    if (pos == std::string::npos) {//没有找到特定的字符
                        //将剩余的部分当作一个字串,放入arry中
                        if(pos == src.size()) break;
                        arry->push_back(src.substr(offset));
                        return arry->size();
                    }
                    if (pos == offset) {
                        offset = pos + sep.size();
                        continue;//当前字串是一个空的,没有内容
                    }
                    arry->push_back(src.substr(offset, pos - offset));
                    offset = pos + sep.size();
                }
                return arry->size();
            }
            //读取文件的所有内容,将读取的内容放到一个Buffer中
            static bool ReadFile(const std::string &filename, std::string *buf) {
                std::ifstream ifs(filename, std::ios::binary);
                if (ifs.is_open() == false) {
                    printf("OPEN %s FILE FAILED!!", filename.c_str());
                    return false;
                }
                size_t fsize = 0;
                ifs.seekg(0, ifs.end);//跳转读写位置到末尾
                fsize = ifs.tellg();  //获取当前读写位置相对于起始位置的偏移量,从末尾偏移刚好就是文件大小
                ifs.seekg(0, ifs.beg);//跳转到起始位置
                buf->resize(fsize); //开辟文件大小的空间
                ifs.read(&(*buf)[0], fsize);
                if (ifs.good() == false) {
                    printf("READ %s FILE FAILED!!", filename.c_str());
                    ifs.close();
                    return false;
                }
                ifs.close();
                return true;
            }
            //向文件写入数据
            static bool WriteFile(const std::string &filename, const std::string &buf) {
                std::ofstream ofs(filename, std::ios::binary | std::ios::trunc);
                if (ofs.is_open() == false) {
                    printf("OPEN %s FILE FAILED!!", filename.c_str());
                    return false;
                }
                ofs.write(buf.c_str(), buf.size());
                if (ofs.good() == false) {
                    ERR_LOG("WRITE %s FILE FAILED!", filename.c_str());
                    ofs.close();    
                    return false;
                }
                ofs.close();
                return true;
            }
            //URL编码,避免URL中资源路径与查询字符串中的特殊字符与HTTP请求中特殊字符产生歧义
            //编码格式:将特殊字符的ascii值,转换为两个16进制字符,前缀%   C++ -> C%2B%2B
            //不编码的特殊字符: RFC3986文档规定 . - _ ~ 字母,数字属于绝对不编码字符
            //RFC3986文档规定,编码格式 %HH 
            //W3C标准中规定,查询字符串中的空格,需要编码为+, 解码则是+转空格
            static std::string UrlEncode(const std::string url, bool convert_space_to_plus) {
                std::string res;
                for (auto &c : url) {
                    if (c == '.' || c == '-' || c == '_' || c == '~' || isalnum(c)) {
                        res += c;
                        continue;
                    }
                    if (c == ' ' && convert_space_to_plus == true) {
                        res += '+';
                        continue;
                    }
                    //剩下的字符都是需要编码成为 %HH 格式
                    char tmp[4] = {0};
                    //snprintf 与 printf比较类似,都是格式化字符串,只不过一个是打印,一个是放到一块空间中
                    snprintf(tmp, 4, "%%%02X", c);
                    res += tmp;
                }
                return res;
            }
            static char HEXTOI(char c) {
                if (c >= '0' && c <= '9') {
                    return c - '0';
                }else if (c >= 'a' && c <= 'z') {
                    return c - 'a' + 10;
                }else if (c >= 'A' && c <= 'Z') {
                    return c - 'A' + 10;
                }
                return -1; 
            }
            static std::string UrlDecode(const std::string url, bool convert_plus_to_space) {
                //遇到了%,则将紧随其后的2个字符,转换为数字,第一个数字左移4位,然后加上第二个数字  + -> 2b  %2b->2 << 4 + 11
                std::string res;
                for (int i = 0; i < url.size(); i++) {
                    if (url[i] == '+' && convert_plus_to_space == true) {
                        res += ' ';
                        continue;
                    }
                    if (url[i] == '%' && (i + 2) < url.size()) {
                        char v1 = HEXTOI(url[i + 1]);
                        char v2 = HEXTOI(url[i + 2]);
                        char v = v1 * 16 + v2;
                        res += v;
                        i += 2;
                        continue;
                    }
                    res += url[i];
                }
                return res;
            }
            //响应状态码的描述信息获取
            static std::string StatuDesc(int statu) {
                
                auto it = _statu_msg.find(statu);
                if (it != _statu_msg.end()) {
                    return it->second;
                }
                return "Unknow";
            }
            //根据文件后缀名获取文件mime
            static std::string ExtMime(const std::string &filename) {
                
                // a.b.txt  先获取文件扩展名
                size_t pos = filename.find_last_of('.');
                if (pos == std::string::npos) {
                    return "application/octet-stream";
                }
                //根据扩展名,获取mime
                std::string ext = filename.substr(pos);
                auto it = _mime_msg.find(ext);
                if (it == _mime_msg.end()) {
                    return "application/octet-stream";
                }
                return it->second;
            }
            //判断一个文件是否是一个目录
            static bool IsDirectory(const std::string &filename) {
                struct stat st;
                int ret = stat(filename.c_str(), &st);
                if (ret < 0) {
                    return false;
                }
                return S_ISDIR(st.st_mode);
            }
            //判断一个文件是否是一个普通文件
            static bool IsRegular(const std::string &filename) {
                struct stat st;
                int ret = stat(filename.c_str(), &st);
                if (ret < 0) {
                    return false;
                }
                return S_ISREG(st.st_mode);
            }
            //http请求的资源路径有效性判断
            // /index.html  --- 前边的/叫做相对根目录  映射的是某个服务器上的子目录
            // 想表达的意思就是,客户端只能请求相对根目录中的资源,其他地方的资源都不予理会
            // /../login, 这个路径中的..会让路径的查找跑到相对根目录之外,这是不合理的,不安全的
            static bool ValidPath(const std::string &path) {
                //思想:按照/进行路径分割,根据有多少子目录,计算目录深度,有多少层,深度不能小于0
                std::vector<std::string> subdir;
                Split(path, "/", &subdir);
                int level = 0;
                for (auto &dir : subdir) {
                    if (dir == "..") {
                        level--; //任意一层走出相对根目录,就认为有问题
                        if (level < 0) return false;
                        continue;
                    }
                    level++;
                }
                return true;
            }
    };
    
    • 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
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177

    状态码

    std::unordered_map<int, std::string> _statu_msg = {
        {100,  "Continue"},
        {101,  "Switching Protocol"},
        {102,  "Processing"},
        {103,  "Early Hints"},
        {200,  "OK"},
        {201,  "Created"},
        {202,  "Accepted"},
        {203,  "Non-Authoritative Information"},
        {204,  "No Content"},
        {205,  "Reset Content"},
        {206,  "Partial Content"},
        {207,  "Multi-Status"},
        {208,  "Already Reported"},
        {226,  "IM Used"},
        {300,  "Multiple Choice"},
        {301,  "Moved Permanently"},
        {302,  "Found"},
        {303,  "See Other"},
        {304,  "Not Modified"},
        {305,  "Use Proxy"},
        {306,  "unused"},
        {307,  "Temporary Redirect"},
        {308,  "Permanent Redirect"},
        {400,  "Bad Request"},
        {401,  "Unauthorized"},
        {402,  "Payment Required"},
        {403,  "Forbidden"},
        {404,  "Not Found"},
        {405,  "Method Not Allowed"},
        {406,  "Not Acceptable"},
        {407,  "Proxy Authentication Required"},
        {408,  "Request Timeout"},
        {409,  "Conflict"},
        {410,  "Gone"},
        {411,  "Length Required"},
        {412,  "Precondition Failed"},
        {413,  "Payload Too Large"},
        {414,  "URI Too Long"},
        {415,  "Unsupported Media Type"},
        {416,  "Range Not Satisfiable"},
        {417,  "Expectation Failed"},
        {418,  "I'm a teapot"},
        {421,  "Misdirected Request"},
        {422,  "Unprocessable Entity"},
        {423,  "Locked"},
        {424,  "Failed Dependency"},
        {425,  "Too Early"},
        {426,  "Upgrade Required"},
        {428,  "Precondition Required"},
        {429,  "Too Many Requests"},
        {431,  "Request Header Fields Too Large"},
        {451,  "Unavailable For Legal Reasons"},
        {501,  "Not Implemented"},
        {502,  "Bad Gateway"},
        {503,  "Service Unavailable"},
        {504,  "Gateway Timeout"},
        {505,  "HTTP Version Not Supported"},
        {506,  "Variant Also Negotiates"},
        {507,  "Insufficient Storage"},
        {508,  "Loop Detected"},
        {510,  "Not Extended"},
        {511,  "Network Authentication Required"}
    };
    
    std::unordered_map<std::string, std::string> _mime_msg = {
        {".aac",        "audio/aac"},
        {".abw",        "application/x-abiword"},
        {".arc",        "application/x-freearc"},
        {".avi",        "video/x-msvideo"},
        {".azw",        "application/vnd.amazon.ebook"},
        {".bin",        "application/octet-stream"},
        {".bmp",        "image/bmp"},
        {".bz",         "application/x-bzip"},
        {".bz2",        "application/x-bzip2"},
        {".csh",        "application/x-csh"},
        {".css",        "text/css"},
        {".csv",        "text/csv"},
        {".doc",        "application/msword"},
        {".docx",       "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
        {".eot",        "application/vnd.ms-fontobject"},
        {".epub",       "application/epub+zip"},
        {".gif",        "image/gif"},
        {".htm",        "text/html"},
        {".html",       "text/html"},
        {".ico",        "image/vnd.microsoft.icon"},
        {".ics",        "text/calendar"},
        {".jar",        "application/java-archive"},
        {".jpeg",       "image/jpeg"},
        {".jpg",        "image/jpeg"},
        {".js",         "text/javascript"},
        {".json",       "application/json"},
        {".jsonld",     "application/ld+json"},
        {".mid",        "audio/midi"},
        {".midi",       "audio/x-midi"},
        {".mjs",        "text/javascript"},
        {".mp3",        "audio/mpeg"},
        {".mpeg",       "video/mpeg"},
        {".mpkg",       "application/vnd.apple.installer+xml"},
        {".odp",        "application/vnd.oasis.opendocument.presentation"},
        {".ods",        "application/vnd.oasis.opendocument.spreadsheet"},
        {".odt",        "application/vnd.oasis.opendocument.text"},
        {".oga",        "audio/ogg"},
        {".ogv",        "video/ogg"},
        {".ogx",        "application/ogg"},
        {".otf",        "font/otf"},
        {".png",        "image/png"},
        {".pdf",        "application/pdf"},
        {".ppt",        "application/vnd.ms-powerpoint"},
        {".pptx",       "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
        {".rar",        "application/x-rar-compressed"},
        {".rtf",        "application/rtf"},
        {".sh",         "application/x-sh"},
        {".svg",        "image/svg+xml"},
        {".swf",        "application/x-shockwave-flash"},
        {".tar",        "application/x-tar"},
        {".tif",        "image/tiff"},
        {".tiff",       "image/tiff"},
        {".ttf",        "font/ttf"},
        {".txt",        "text/plain"},
        {".vsd",        "application/vnd.visio"},
        {".wav",        "audio/wav"},
        {".weba",       "audio/webm"},
        {".webm",       "video/webm"},
        {".webp",       "image/webp"},
        {".woff",       "font/woff"},
        {".woff2",      "font/woff2"},
        {".xhtml",      "application/xhtml+xml"},
        {".xls",        "application/vnd.ms-excel"},
        {".xlsx",       "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
        {".xml",        "application/xml"},
        {".xul",        "application/vnd.mozilla.xul+xml"},
        {".zip",        "application/zip"},
        {".3gp",        "video/3gpp"},
        {".3g2",        "video/3gpp2"},
        {".7z",         "application/x-7z-compressed"}
    };
    
    • 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
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
  • 相关阅读:
    分布式系列之分布式实时计算系统Storm解析
    int和Integer的区别
    Java实现RSA加密和验证
    打开获取需求的大门——用例图绘制指南
    《持续交付:发布可靠软件的系统方法》- 读书笔记(三)
    怎么防止文件夹被删除、复制?
    C#高级--加密解密详解
    ASP.NET Core 6框架揭秘实例演示[19]:数据加解密与哈希
    动作捕捉系统用于地下隧道移动机器人定位与建图
    【MyBatis源码分析】六、MyBatis Plugins(拦截器)
  • 原文地址:https://blog.csdn.net/weixin_54447296/article/details/133845525