C++實(shí)現(xiàn)簡(jiǎn)單的HTTP服務(wù)器
本文實(shí)例為大家分享了C++實(shí)現(xiàn)HTTP服務(wù)器的相關(guān)代碼,供大家參考,具體內(nèi)容如下
#include <Winsock2.h> #include <windows.h> #include <malloc.h> #include <stdio.h> #include <string.h> #include <time.h> #pragma comment (lib,"ws2_32") #define uPort 80 #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; //多線程處理多個(gè)客戶端的連接 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();//線程函數(shù) DWORD WINAPI AcceptThread(LPVOID lpParam); DWORD WINAPI ClientThread(LPVOID lpParam); bool IoComplete(char* szRequest); //數(shù)據(jù)包的校驗(yàn)函數(shù) bool AddClientList(SOCKET s,sockaddr_in addr); bool AddThreadList(HANDLE hThread,DWORD ThreadID); bool ParseRequest(char* szRequest, char* szResponse, BOOL &bKeepAlive); //我們存放Html文件的目錄 char HtmlDir[512]={0}; void main() { if (!InitSocket()) { printf("InitSocket Error\n"); return; } GetCurrentDirectory(512,HtmlDir); strcat(HtmlDir,"\\HTML\\"); strcat(HtmlDir,FileName); //啟動(dòng)一個(gè)接受線程 HANDLE hAcceptThread = CreateThread(NULL,0,AcceptThread,NULL,0,NULL); //在這里我們使用事件模型來(lái)實(shí)現(xiàn)我們的Web服務(wù)器 //創(chuàng)建一個(gè)事件 WaitForSingleObject(hAcceptThread,INFINITE); } DWORD WINAPI AcceptThread(LPVOID lpParam) //接收線程 { //創(chuàng)建一個(gè)監(jiān)聽(tīng)套接字 SOCKET sListen = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED); //使用事件重疊的套接字 if (sListen==INVALID_SOCKET) { printf("Create Listen Error\n"); return -1; } //初始化本服務(wù)器的地址 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; } //監(jiān)聽(tīng) listen(sListen,5); //創(chuàng)建一個(gè)事件 WSAEVENT Event = WSACreateEvent(); if (Event==WSA_INVALID_EVENT) { printf("Create WSAEVENT Error\n"); closesocket(sListen); CloseHandle(Event); //創(chuàng)建事件失敗 關(guān)閉套接字 關(guān)閉事件 return -1; } //將我們的監(jiān)聽(tīng)套接字與我們的事件進(jìn)行關(guān)聯(lián)屬性為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; } //如果有真正的事件我們就進(jìn)行判斷 WSAEnumNetworkEvents(sListen,Event,&NetWorkEvent); ResetEvent(&Event); // if (NetWorkEvent.lNetworkEvents == FD_ACCEPT) { if (NetWorkEvent.iErrorCode[FD_ACCEPT_BIT]==0) { //我們要為新的連接進(jìn)行接受并申請(qǐng)內(nèi)存存入鏈表中 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) { //我們將每個(gè)用戶的信息以參數(shù)的形式傳入到該線程 pNode pTemp = (pNode)lpParam; SOCKET sClient = pTemp->s; //這是通信套接字 WSAEVENT Event = WSACreateEvent(); //該事件是與通信套接字關(guān)聯(lián)以判斷事件的種類 WSANETWORKEVENTS NetWorkEvent; char szRequest[1024]={0}; //請(qǐng)求報(bào)文 char szResponse[1024]={0}; //響應(yīng)報(bào)文 BOOL bKeepAlive = FALSE; //是否持續(xù)連接 if(Event == WSA_INVALID_EVENT) { return -1; } int Ret = WSAEventSelect(sClient, Event, FD_READ | FD_WRITE | FD_CLOSE); //關(guān)聯(lián)事件和套接字 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; } // 分析什么網(wǎng)絡(luò)事件產(chǎn)生 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); //我們?cè)谶@里要檢測(cè)是否得到的完整請(qǐng)求 memcpy(szRequest,szBuffer,NumberOfBytesRecvd); if (!IoComplete(szRequest)) //校驗(yàn)數(shù)據(jù)包 { continue; } if (!ParseRequest(szRequest, szResponse, bKeepAlive)) //分析數(shù)據(jù)包 { //我在這里就進(jìn)行了簡(jiǎn)單的處理 continue; } DWORD NumberOfBytesSent = 0; DWORD dwBytesSent = 0; //發(fā)送響應(yīng)到客戶端 do { Buffers.len = (strlen(szResponse) - dwBytesSent) >= SENDBLOCK ? SENDBLOCK : strlen(szResponse) - dwBytesSent; Buffers.buf = (char*)((DWORD)szResponse + dwBytesSent); Ret = WSASend( sClient, &Buffers, 1, &NumberOfBytesSent, 0, 0, NULL); if(SOCKET_ERROR != Ret) dwBytesSent += NumberOfBytesSent; } while((dwBytesSent < strlen(szResponse)) && SOCKET_ERROR != Ret); } if(NetWorkEvent.lNetworkEvents & FD_CLOSE) { //在這里我沒(méi)有處理,我們要將內(nèi)存進(jìn)行釋放否則內(nèi)存泄露 } } return 0; } bool InitSocket() { WSADATA wsadata; if (WSAStartup(MAKEWORD(2,2),&wsadata)==0) //使用Socket前必須調(diào)用 參數(shù) 作用 返回值 { return true; } return false; } bool AddClientList(SOCKET s,sockaddr_in addr) { 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; } //我們要為用戶開(kāi)辟新的線程 hThread = CreateThread(NULL,0,ClientThread,(LPVOID)pTemp,0,&ThreadID); if (hThread==NULL) { free(pTemp); return false; } if (!AddThreadList(hThread,ThreadID)) { free(pTemp); 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; } //校驗(yàn)數(shù)據(jù)包 bool IoComplete(char* szRequest) { char* pTemp = NULL; //定義臨時(shí)空指針 int nLen = strlen(szRequest); //請(qǐng)求數(shù)據(jù)包長(zhǎng)度 pTemp = szRequest; pTemp = pTemp+nLen-4; //定位指針 if (strcmp(pTemp,"\r\n\r\n")==0) //校驗(yàn)請(qǐng)求頭部行末尾的回車控制符和換行符以及空行 { return true; } return false; } //分析數(shù)據(jù)包 bool ParseRequest(char* szRequest, char* szResponse, BOOL &bKeepAlive) { char* p = NULL; p = szRequest; int n = 0; char* pTemp = strstr(p," "); //判斷字符串str2是否是str1的子串。如果是,則該函數(shù)返回str2在str1中首次出現(xiàn)的地址;否則,返回NULL。 n = pTemp - p; //指針長(zhǎng)度 // pTemp = pTemp + n - 1; //將我們的指針下移 //定義一個(gè)臨時(shí)的緩沖區(qū)來(lái)存放我們 char szMode[10]={0}; char szFileName[10]={0}; memcpy(szMode,p,n); //將請(qǐng)求方法拷貝到szMode數(shù)組中 if (strcmp(szMode,"GET")==0) //一定要將Get寫成大寫 { //獲取文件名 pTemp = strstr(pTemp," "); pTemp = pTemp + 1; //只有調(diào)試的時(shí)候才能發(fā)現(xiàn)這里的秘密 memcpy(szFileName,pTemp,1); if (strcmp(szFileName,"/")==0) { strcpy(szFileName,FileName); } else { return false; } } else { return false; } // 分析鏈接類型 pTemp = strstr(szRequest,"\nConnection: Keep-Alive"); //協(xié)議版本 n = pTemp - p; if (p>0) { bKeepAlive = TRUE; } else //這里的設(shè)置是為了Proxy程序的運(yùn)行 { bKeepAlive = TRUE; } //定義一個(gè)回顯頭 char pResponseHeader[512]={0}; char szStatusCode[20]={0}; char szContentType[20]={0}; strcpy(szStatusCode,"200 OK"); strcpy(szContentType,"text/html"); char szDT[128]; struct tm *newtime; long ltime; time(<ime); newtime = gmtime(<ime); strftime(szDT, 128,"%a, %d %b %Y %H:%M:%S GMT", newtime); //讀取文件 //定義一個(gè)文件流指針 FILE* fp = fopen(HtmlDir,"rb"); fpos_t lengthActual = 0; int length = 0; char* BufferTemp = NULL; if (fp!=NULL) { // 獲得文件大小 fseek(fp, 0, SEEK_END); fgetpos(fp, &lengthActual); fseek(fp, 0, SEEK_SET); //計(jì)算出文件的大小后我們進(jìn)行分配內(nèi)存 BufferTemp = (char*)malloc(sizeof(char)*((int)lengthActual)); length = fread(BufferTemp,1,(int)lengthActual,fp); fclose(fp); // 返回響應(yīng) sprintf(pResponseHeader, "HTTP/1.0 %s\r\nDate: %s\r\nServer: %s\r\nAccept-Ranges: bytes\r\nContent-Length: %d\r\nConnection: %s\r\nContent-Type: %s\r\n\r\n", szStatusCode, szDT, SERVERNAME, length, bKeepAlive ? "Keep-Alive" : "close", szContentType); //響應(yīng)報(bào)文 } //如果我們的文件沒(méi)有找到我們將引導(dǎo)用戶到另外的錯(cuò)誤頁(yè)面 else { } strcpy(szResponse,pResponseHeader); strcat(szResponse,BufferTemp); free(BufferTemp); BufferTemp = NULL; return true; }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。
上一篇:C語(yǔ)言中用于產(chǎn)生隨機(jī)數(shù)的函數(shù)使用方法總結(jié)
欄 目:C語(yǔ)言
下一篇:C語(yǔ)言程序中遞歸算法的使用實(shí)例教程
本文標(biāo)題:C++實(shí)現(xiàn)簡(jiǎn)單的HTTP服務(wù)器
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/2349.html
您可能感興趣的文章
- 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
- 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計(jì)-用棧實(shí)現(xiàn)表達(dá)式求值的方法詳解
- 01-10使用OpenGL實(shí)現(xiàn)3D立體顯示的程序代碼
- 01-10深入理解C++中常見(jiàn)的關(guān)鍵字含義
- 01-10求斐波那契(Fibonacci)數(shù)列通項(xiàng)的七種實(shí)現(xiàn)方法
- 01-10C語(yǔ)言 解決不用+、-、&#215;、&#247;數(shù)字運(yùn)算符做加法
- 01-10使用C++實(shí)現(xiàn)全排列算法的方法詳解
- 01-10c++中inline的用法分析
- 01-10用C++實(shí)現(xiàn)DBSCAN聚類算法
- 01-10深入全排列算法及其實(shí)現(xiàn)方法


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)
- 04-02c語(yǔ)言用函數(shù)寫分段 用c語(yǔ)言表示分段
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)
- 04-02c語(yǔ)言編寫函數(shù)冒泡排序 c語(yǔ)言冒泡排
- 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
- 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎
- 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求
隨機(jī)閱讀
- 01-10C#中split用法實(shí)例總結(jié)
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 04-02jquery與jsp,用jquery
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10delphi制作wav文件的方法
- 01-11Mac OSX 打開(kāi)原生自帶讀寫NTFS功能(圖文