基本信息
源码名称:httpserver(c++ http服务器源码)
源码大小:7.28KB
文件格式:.rar
开发语言:C/C++
更新时间:2021-08-25
友情提示:(无需注册或充值,赞助后即可获取资源下载链接)
嘿,亲!知识可是无价之宝呢,但咱这精心整理的资料也耗费了不少心血呀。小小地破费一下,绝对物超所值哦!如有下载和支付问题,请联系我们QQ(微信同号):813200300
本次赞助数额为: 2 元×
微信扫码支付:2 元
×
请留下您的邮箱,我们将在2小时内将文件发到您的邮箱
源码介绍
#include "stdafx.h" #include <Winsock2.h> #include <windows.h> #include <malloc.h> #include <cstdio> #include <string> #include <ctime> #include <Common/HyHelper.h> #include <json/json.h> #pragma comment (lib,"ws2_32.lib") #define uPort 8099 #define MAX_BUFFER 100000 #define SENDBLOCK 200000 #define SERVERNAME "AcIDSoftWebServer/0.1b" #define FileName "HelloWorld.html" typedef struct _NODE_ { SOCKET s; sockaddr_in Addr; _NODE_* pNext; }Node, *pNode; //多线程处理多个客户端的连接 typedef struct _THREAD_ { DWORD ThreadID; HANDLE hThread; _THREAD_* pNext; }Thread, *pThread; pNode pHead = NULL; pNode pTail = NULL; pThread pHeadThread = NULL; pThread pTailThread = NULL; bool InitSocket();//线程函数 DWORD WINAPI AcceptThread(LPVOID lpParam); DWORD WINAPI ClientThread(LPVOID lpParam); bool IoComplete(std::string& szRequest); //数据包的校验函数 bool AddClientList(SOCKET s, sockaddr_in addr); bool AddThreadList(HANDLE hThread, DWORD ThreadID); bool ParseRequest(std::string& szRequest, std::string& szResponse, BOOL &bKeepAlive); //我们存放Html文件的目录 char HtmlDir[512] = { 0 }; void main() { if (!InitSocket()) { printf("InitSocket Error\n"); return; } std::string appPath = HyHelper::wstring2string(HyHelper::GetAppPath()); strcpy(HtmlDir, appPath.c_str()); strcat(HtmlDir, "\\HTML\\"); strcat(HtmlDir, FileName); printf("初始路径为 %s \n", HtmlDir); //启动一个接受线程 HANDLE hAcceptThread = CreateThread(NULL, 0, AcceptThread, NULL, 0, NULL); if (hAcceptThread != INVALID_HANDLE_VALUE) { printf("开启HTTP服务线程......\n"); } //在这里我们使用事件模型来实现我们的Web服务器 //创建一个事件 WaitForSingleObject(hAcceptThread, INFINITE); } DWORD WINAPI AcceptThread(LPVOID lpParam) //接收线程 { //创建一个监听套接字 SOCKET sListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED); //使用事件重叠的套接字 if (sListen == INVALID_SOCKET) { printf("Create Listen Error\n"); return -1; } //初始化本服务器的地址 sockaddr_in LocalAddr; LocalAddr.sin_addr.S_un.S_addr = INADDR_ANY; LocalAddr.sin_family = AF_INET; LocalAddr.sin_port = htons(uPort); //绑定套接字 80端口 int Ret = bind(sListen, (sockaddr*)&LocalAddr, sizeof(LocalAddr)); if (Ret == SOCKET_ERROR) { printf("Bind Error\n"); return -1; } //监听 listen(sListen, 5); //创建一个事件 WSAEVENT Event = WSACreateEvent(); if (Event == WSA_INVALID_EVENT) { printf("Create WSAEVENT Error\n"); closesocket(sListen); CloseHandle(Event); //创建事件失败 关闭套接字 关闭事件 return -1; } //将我们的监听套接字与我们的事件进行关联属性为Accept WSAEventSelect(sListen, Event, FD_ACCEPT); WSANETWORKEVENTS NetWorkEvent; sockaddr_in ClientAddr; int nLen = sizeof(ClientAddr); DWORD dwIndex = 0; while (1) { dwIndex = WSAWaitForMultipleEvents(1, &Event, FALSE, WSA_INFINITE, FALSE); dwIndex = dwIndex - WAIT_OBJECT_0; if (dwIndex == WSA_WAIT_TIMEOUT || dwIndex == WSA_WAIT_FAILED) { continue; } //如果有真正的事件我们就进行判断 WSAEnumNetworkEvents(sListen, Event, &NetWorkEvent); ResetEvent(&Event); // if (NetWorkEvent.lNetworkEvents == FD_ACCEPT) { if (NetWorkEvent.iErrorCode[FD_ACCEPT_BIT] == 0) { //我们要为新的连接进行接受并申请内存存入链表中 SOCKET sClient = WSAAccept(sListen, (sockaddr*)&ClientAddr, &nLen, NULL, NULL); if (sClient == INVALID_SOCKET) { continue; } else { //如果接收成功我们要把用户的所有信息存放到链表中 if (!AddClientList(sClient, ClientAddr)) { continue; } } } } } return 0; } DWORD WINAPI ClientThread(LPVOID lpParam) { //我们将每个用户的信息以参数的形式传入到该线程 pNode pTemp = (pNode)lpParam; SOCKET sClient = pTemp->s; //这是通信套接字 WSAEVENT Event = WSACreateEvent(); //该事件是与通信套接字关联以判断事件的种类 WSANETWORKEVENTS NetWorkEvent; std::string szRequest; //请求报文 std::string szResponse; //响应报文 BOOL bKeepAlive = FALSE; //是否持续连接 if (Event == WSA_INVALID_EVENT) { return -1; } int Ret = WSAEventSelect(sClient, Event, FD_READ | FD_WRITE | FD_CLOSE); //关联事件和套接字 DWORD dwIndex = 0; while (1) { dwIndex = WSAWaitForMultipleEvents(1, &Event, FALSE, WSA_INFINITE, FALSE); dwIndex = dwIndex - WAIT_OBJECT_0; if (dwIndex == WSA_WAIT_TIMEOUT || dwIndex == WSA_WAIT_FAILED) { continue; } // 分析什么网络事件产生 Ret = WSAEnumNetworkEvents(sClient, Event, &NetWorkEvent); //其他情况 if (!NetWorkEvent.lNetworkEvents) { continue; } if (NetWorkEvent.lNetworkEvents & FD_READ) //这里很有意思的 { DWORD NumberOfBytesRecvd; WSABUF Buffers; DWORD dwBufferCount = 1; char szBuffer[MAX_BUFFER]; DWORD Flags = 0; Buffers.buf = szBuffer; Buffers.len = MAX_BUFFER; Ret = WSARecv(sClient, &Buffers, dwBufferCount, &NumberOfBytesRecvd, &Flags, NULL, NULL); //我们在这里要检测是否得到的完整请求 szRequest = std::string(szBuffer, NumberOfBytesRecvd); printf("Url请求:%s \n", szRequest.c_str()); if (!IoComplete(szRequest)) //校验数据包 { continue; } if (!ParseRequest(szRequest, szResponse, bKeepAlive)) //分析数据包 { //我在这里就进行了简单的处理 continue; } DWORD NumberOfBytesSent = 0; DWORD dwBytesSent = 0; //发送响应到客户端 do { Buffers.len = (szResponse.length() - dwBytesSent) >= SENDBLOCK ? SENDBLOCK : szResponse.length() - dwBytesSent; Buffers.buf = (char*)((DWORD)szResponse.c_str() dwBytesSent); Ret = WSASend( sClient, &Buffers, 1, &NumberOfBytesSent, 0, 0, NULL); if (SOCKET_ERROR != Ret) dwBytesSent = NumberOfBytesSent; } while ((dwBytesSent < szResponse.length()) && SOCKET_ERROR != Ret); } if (NetWorkEvent.lNetworkEvents & FD_CLOSE) { //在这里我没有处理,我们要将内存进行释放否则内存泄露 } } return 0; } bool InitSocket() { WSADATA wsadata; if (WSAStartup(MAKEWORD(2, 2), &wsadata) == 0) //使用Socket前必须调用 参数 作用 返回值 { return true; } return false; } bool AddClientList(SOCKET s, sockaddr_in addr) { char FAR * PASCAL strIp = inet_ntoa(addr.sin_addr); printf("有新客户端连接:%s:%d\n", strIp, addr.sin_port); pNode pTemp = (pNode)malloc(sizeof(Node)); HANDLE hThread = NULL; DWORD ThreadID = 0; if (pTemp == NULL) { printf("No Memory\n"); return false; } else { pTemp->s = s; pTemp->Addr = addr; pTemp->pNext = NULL; if (pHead == NULL) { pHead = pTail = pTemp; } else { pTail->pNext = pTemp; pTail = pTail->pNext; } //我们要为用户开辟新的线程 hThread = CreateThread(NULL, 0, ClientThread, (LPVOID)pTemp, 0, &ThreadID); if (hThread == NULL) { free(pTemp); return false; } if (!AddThreadList(hThread, ThreadID)) { free(pTemp); CloseHandle(hThread); return false; } } return true; } bool AddThreadList(HANDLE hThread, DWORD ThreadID) { pThread pTemp = (pThread)malloc(sizeof(Thread)); if (pTemp == NULL) { printf("No Memory\n"); return false; } else { pTemp->hThread = hThread; pTemp->ThreadID = ThreadID; pTemp->pNext = NULL; if (pHeadThread == NULL) { pHeadThread = pTailThread = pTemp; } else { pTailThread->pNext = pTemp; pTailThread = pTailThread->pNext; } } return true; } //校验数据包 bool IoComplete(std::string& szRequest) { const char* pTemp = NULL; //定义临时空指针 int nLen = szRequest.length(); //请求数据包长度 pTemp = szRequest.c_str(); pTemp = pTemp nLen - 4; //定位指针 if (strcmp(pTemp, "\r\n\r\n") == 0) //校验请求头部行末尾的回车控制符和换行符以及空行 { return true; } return false; } //分析数据包 bool ParseRequest(std::string& szRequest, std::string& szResponse, BOOL &bKeepAlive) { const char* p = szRequest.c_str(); int n = 0; const char* pTemp = strstr(p, " "); //判断字符串str2是否是str1的子串。如果是,则该函数返回str2在str1中首次出现的地址;否则,返回NULL。 n = pTemp - p; //指针长度 // pTemp = pTemp n - 1; //将我们的指针下移 //定义一个临时的缓冲区来存放我们 char szMode[10] = { 0 }; char szFileName[128] = { 0 }; memcpy(szMode, p, n); //将请求方法拷贝到szMode数组中 if (strcmp(szMode, "GET") == 0 || strcmp(szMode, "POST") == 0) //一定要将Get写成大写 { //获取文件名 pTemp = strstr(pTemp, " "); pTemp = pTemp 1; //只有调试的时候才能发现这里的秘密 memcpy(szFileName, pTemp, 1); if (strcmp(szFileName, "/") == 0) { strcpy(szFileName, FileName); } else { return false; } } else { return false; } // 分析链接类型 pTemp = strstr(szRequest.c_str(), "\nConnection: Keep-Alive"); //协议版本 n = pTemp - p; if (p > 0) { bKeepAlive = TRUE; } else //这里的设置是为了Proxy程序的运行 { bKeepAlive = TRUE; } //定义一个回显头 std::string pHeader = ""; std::string szStatusCode = ""; std::string szContentType = ""; szStatusCode = "200 OK"; szContentType = "application/json;charset=UTF-8"; char szDT[128]; struct tm *newtime; time_t ltime = time(NULL); newtime = gmtime(<ime); strftime(szDT, 128, "%a, %d %b %Y %H:%M:%S GMT", newtime); //读取文件 //定义一个文件流指针 FILE* fp = fopen(HtmlDir, "rb"); fpos_t lengthActual = 0; int length = 0; char* pBuffer = NULL; if (fp != NULL) { // 获得文件大小 fseek(fp, 0, SEEK_END); fgetpos(fp, &lengthActual); fseek(fp, 0, SEEK_SET); //计算出文件的大小后我们进行分配内存 pBuffer = new char[lengthActual]; memset(pBuffer, 0, lengthActual); fseek(fp, 3, SEEK_SET); length = fread(pBuffer, 1, (int)lengthActual-3, fp); fclose(fp); // 返回响应 //sprintf(pHeader, pHeader.append("HTTP/1.0 "); pHeader.append(szStatusCode); pHeader.append("\r\nDate: "); pHeader.append(szDT); pHeader.append("\r\nServer:"); pHeader.append(SERVERNAME); pHeader.append("\r\nAccept-Ranges: bytes\r\nContent-Length:"); char pValue[16] = { 0 }; _itoa_s(length, pValue, 10); pHeader.append(pValue); pHeader.append("\r\nConnection:"); if (bKeepAlive) { pHeader.append("Keep-Alive"); } else { pHeader.append("close"); } pHeader.append("\r\nContent-Type:"); pHeader.append(szContentType); pHeader.append("\r\n\r\n"); if (!pHeader.empty()) { szResponse = pHeader; } if (pBuffer) { szResponse = pBuffer; std::string strRes = HyHelper::UTF8ToGBK(pBuffer); printf("Url响应:%s\n", strRes.c_str()); delete[] pBuffer; pBuffer = NULL; } } return true; }