需要了解的知识

1. socket编程

2. socket select模型

3. request请求头与response响应头

4. c/c++中文件的读写操作

实现功能

  1. 自动解析资源目录与请求参数

  2. 自定义404页面

  3. 实现基本的FTP服务

    #include <WinSock2.h>
    #include<iostream>
    #include<string>
    #include<unordered_map>
    #include<sstream>
    #pragma comment(lib,"ws2_32.lib")
    #pragma warning(disable:4996)
    using namespace std;
    unordered_map<string, string> contentType;
    void inItContentType(){//初始化contentType
     contentType["html"] = "text/html";
     contentType["txt"] = "text/plain";
     contentType["xml"] = "text/xml";
     contentType["gif"] = "image/gif";
     contentType["jpg"] = "image/jpeg";
     contentType["png"] = "image/png";
     contentType["mp3"] = "audio/mp3";
     contentType["mp4"] = "video/mpeg4";
    }
    bool Socket_Select(SOCKET hSocket, int  TimeOut = 100, bool Read = true) {  //封装select函数
     fd_set fdset; //socket自带的结构体 类似于队列
     timeval tv; //自带结构体
     FD_ZERO(&fdset);//清空
     FD_SET(hSocket, &fdset);//类似与入队
     tv.tv_sec = 0;
     tv.tv_usec = TimeOut;//超时时间
    
     int return_value;
     if (Read) {
         return_value = select(0, &fdset, NULL, NULL, &tv); //可读
     }
     else {
         return_value = select(0, NULL, &fdset, NULL, &tv);  //可写
     }
    
     if (return_value <= 0) return false;  //出现错误 或者无活动
     if (FD_ISSET(hSocket, &fdset)) return true;
     return false;
    }
    //读取文件
    string readFile(const char * const fileName, char **bufferPtr, int *size)
    {
     FILE * fp;
     fopen_s(&fp, fileName, "rb+");
     if (fp == nullptr)//未找到文件 寻找404文件
     {
         if ("404.html" == fileName){ //如果404文件也不存在 返回内置的错误页面
             *bufferPtr = new char[30];
             strcpy(*bufferPtr, "404 not found");
             *size = strlen(*bufferPtr);
             return "404 NotFound";
         }
         readFile("404.html", bufferPtr, size);
         return "404 NotFound";
     }
     fseek(fp, 0, SEEK_END);
     *size = ftell(fp);
     fseek(fp, 0, 0);
     *bufferPtr = new char[*size];
     fread(*bufferPtr, *size, 1, fp);
     fclose(fp);
     return "200 OK";
    }
    int main()
    {
     inItContentType();
     //请求版本
     WSADATA wsData;
     WSAStartup(MAKEWORD(2, 2), &wsData);
     if (HIBYTE(wsData.wVersion) != 2 || LOBYTE(wsData.wVersion) != 2)
     {
         cout << "版本请求失败" << endl;
         cin.get();
     }
     //创建socket
     SOCKET webServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//IP4 TCP流传输 TCP协议
     if (SOCKET_ERROR == webServer)
     {
         cout << "socket创建失败" << endl;
         cin.get();
         return -1;
     }
     //创建协议地址族
     SOCKADDR_IN serverAddr;
     serverAddr.sin_family = AF_INET;//IP4
     serverAddr.sin_addr.s_addr = INADDR_ANY;//任意IP地址
     serverAddr.sin_port = htons(80);//监听端口转换  网络使用大端存储 电脑存储方式有大小端
     //socket与IP端口绑定
     if (bind(webServer, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
     {
         cout << "绑定失败" << endl;
         cin.get();
         return -1;
     }
     //监听
     if (listen(webServer, 5) == INVALID_SOCKET) {
         cout << "设置监听状态失败!" << endl;
         return -1;
     }
     cout << "服务端正在监听...." << endl;
    
     //等待连接
     SOCKADDR_IN clientAddr;
     int len = sizeof(clientAddr);
     while (1)
    
     {
         SOCKET webClient = accept(webServer, (SOCKADDR*)&clientAddr, &len);
         try {
             if (webClient == -1)
             {
                 cout << "连接失败" << endl;
             }
    
             //cout << inet_ntoa(clientAddr.sin_addr) << "请求了网页" << endl;
             char recvData[1024] = { 0 };
             string requestHeader = "";//请求头
             while (1){
                 memset(recvData, 0, 1024);
                 if (Socket_Select(webClient)){
                     int r = recv(webClient, recvData, 1024, 0);
                     if (r > 0)
                     {
                         requestHeader += recvData;
                     }
                     else{ //用户关闭了连接
                         throw "连接强制关闭";
                     }
                 }
                 else
                     break;
             }
    
             int fullPathStart = requestHeader.find("GET /") + 5;
             int fullPathEnd = requestHeader.find(" HTTP/1.1");
             if (-1 == fullPathStart || -1 == fullPathEnd) //没有接收到头文件
             {
    
                 throw "资源路径有误";
             }
             string full_path = requestHeader.substr(fullPathStart, fullPathEnd - fullPathStart);//路径+参数
    
             string pathInfo;//路径
             string parameters="";//参数
             int parametersStart = full_path.find("?");
             if (-1 != parametersStart){//含有参数
                 pathInfo = full_path.substr(0, parametersStart);
                 parameters = full_path.substr(parametersStart);
                 cout << pathInfo << endl << parameters << endl;
             }
             else{
                 pathInfo = full_path;
             }
             if (-1 == pathInfo.find('.'))//路径中没有文件名 默认index.html
             {
                 if (pathInfo.size() != 0 && pathInfo[pathInfo.size() - 1] != '/') //自动添加/
                     pathInfo += "/";
                 pathInfo += "index.html";
             }
    
             //读取资源
             int size;
             char *Buffer = nullptr;
             string response_code = readFile(pathInfo.c_str(), &Buffer, &size);
             if (Buffer == nullptr)
             {
                 throw "文件读取失败";
    
             }
             //根据文件名构造响应头
             string fileType = pathInfo.substr(pathInfo.rfind('.') + 1);
    
             string responseHeader = "HTTP/1.1 " + response_code + "\r\n" + "Content-Type: " + contentType[fileType] + ";\r\n\r\n";
             send(webClient, responseHeader.c_str(), responseHeader.size(), 0);
             send(webClient, Buffer, size, 0);
             delete[] Buffer;
         }
         catch (char *str){
             cout << "异常:" << str << endl;
         }
         closesocket(webClient);
     }
    }