C++實(shí)現(xiàn)簡(jiǎn)單的HTTP服務(wù)器
更新時(shí)間:2016年05月02日 19:29:52 作者:suvllian
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)簡(jiǎn)單的HTTP服務(wù)器的相關(guān)資料,感興趣的朋友可以參考下
本文實(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;
//多線(xiàn)程處理多個(gè)客戶(hù)端的連接
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();//線(xiàn)程函數(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è)接受線(xiàn)程
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) //接收線(xiàn)程
{
//創(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
{
//如果接收成功我們要把用戶(hù)的所有信息存放到鏈表中
if (!AddClientList(sClient,ClientAddr))
{
continue;
}
}
}
}
}
return 0;
}
DWORD WINAPI ClientThread(LPVOID lpParam)
{
//我們將每個(gè)用戶(hù)的信息以參數(shù)的形式傳入到該線(xiàn)程
pNode pTemp = (pNode)lpParam;
SOCKET sClient = pTemp->s; //這是通信套接字
WSAEVENT Event = WSACreateEvent(); //該事件是與通信套接字關(guān)聯(lián)以判斷事件的種類(lèi)
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)到客戶(hù)端
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;
}
//我們要為用戶(hù)開(kāi)辟新的線(xiàn)程
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)求頭部行末尾的回車(chē)控制符和換行符以及空行
{
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寫(xiě)成大寫(xiě)
{
//獲取文件名
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;
}
// 分析鏈接類(lèi)型
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)用戶(hù)到另外的錯(cuò)誤頁(yè)面
else
{
}
strcpy(szResponse,pResponseHeader);
strcat(szResponse,BufferTemp);
free(BufferTemp);
BufferTemp = NULL;
return true;
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。
相關(guān)文章
C++實(shí)現(xiàn)簡(jiǎn)單24點(diǎn)游戲
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)簡(jiǎn)單24點(diǎn)游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03
C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的<三子棋>案例
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的《三子棋》,本文通過(guò)功能區(qū)分一步步實(shí)現(xiàn)該案例,通過(guò)逐步的解析和代碼列舉,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
VS Code 中安裝運(yùn)行、編寫(xiě)C語(yǔ)言程序的詳細(xì)教程
這篇文章主要介紹了VS Code 中安裝運(yùn)行、編寫(xiě)C語(yǔ)言程序的詳細(xì)教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03
C語(yǔ)言修煉之路靈根孕育源流出?初識(shí)C言大道生下篇
C語(yǔ)言是一門(mén)面向過(guò)程、抽象化的通用程序設(shè)計(jì)語(yǔ)言,廣泛應(yīng)用于底層開(kāi)發(fā)。C語(yǔ)言能以簡(jiǎn)易的方式編譯、處理低級(jí)存儲(chǔ)器。C語(yǔ)言是僅產(chǎn)生少量的機(jī)器語(yǔ)言以及不需要任何運(yùn)行環(huán)境支持便能運(yùn)行的高效率程序設(shè)計(jì)語(yǔ)言2022-03-03
C++Primer筆記之關(guān)聯(lián)容器的使用詳解
本篇文章對(duì)C++Primer 關(guān)聯(lián)容器的使用進(jìn)行了詳細(xì)的分析介紹。需要的朋友參考下2013-05-05
C/C++并查集的查詢(xún)與合并實(shí)現(xiàn)原理
這篇文章主要介紹了C/C++并查集的查詢(xún)與合并,并查集是一種用來(lái)管理元素分組情況的數(shù)據(jù)結(jié)構(gòu)。并查集可以高效地進(jìn)行如下操作2023-02-02

