需要了解的知识
1. socket编程
2. socket select模型
3. request请求头与response响应头
4. c/c++中文件的读写操作
实现功能
自动解析资源目录与请求参数
自定义404页面
实现基本的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); } }