C++實現(xiàn)簡單的HTTP服務(wù)器
本文實例為大家分享了C++實現(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;
//多線程處理多個客戶端的連接
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ù)包的校驗函數(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);
//啟動一個接受線程
HANDLE hAcceptThread = CreateThread(NULL,0,AcceptThread,NULL,0,NULL);
//在這里我們使用事件模型來實現(xiàn)我們的Web服務(wù)器
//創(chuàng)建一個事件
WaitForSingleObject(hAcceptThread,INFINITE);
}
DWORD WINAPI AcceptThread(LPVOID lpParam) //接收線程
{
//創(chuàng)建一個監(jiān)聽套接字
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)聽
listen(sListen,5);
//創(chuàng)建一個事件
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)聽套接字與我們的事件進行關(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;
}
//如果有真正的事件我們就進行判斷
WSAEnumNetworkEvents(sListen,Event,&NetWorkEvent);
ResetEvent(&Event); //
if (NetWorkEvent.lNetworkEvents == FD_ACCEPT)
{
if (NetWorkEvent.iErrorCode[FD_ACCEPT_BIT]==0)
{
//我們要為新的連接進行接受并申請內(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)
{
//我們將每個用戶的信息以參數(shù)的形式傳入到該線程
pNode pTemp = (pNode)lpParam;
SOCKET sClient = pTemp->s; //這是通信套接字
WSAEVENT Event = WSACreateEvent(); //該事件是與通信套接字關(guān)聯(lián)以判斷事件的種類
WSANETWORKEVENTS NetWorkEvent;
char szRequest[1024]={0}; //請求報文
char szResponse[1024]={0}; //響應(yīng)報文
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);
//我們在這里要檢測是否得到的完整請求
memcpy(szRequest,szBuffer,NumberOfBytesRecvd);
if (!IoComplete(szRequest)) //校驗數(shù)據(jù)包
{
continue;
}
if (!ParseRequest(szRequest, szResponse, bKeepAlive)) //分析數(shù)據(jù)包
{
//我在這里就進行了簡單的處理
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)
{
//在這里我沒有處理,我們要將內(nèi)存進行釋放否則內(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;
}
//我們要為用戶開辟新的線程
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;
}
//校驗數(shù)據(jù)包
bool IoComplete(char* szRequest)
{
char* pTemp = NULL; //定義臨時空指針
int nLen = strlen(szRequest); //請求數(shù)據(jù)包長度
pTemp = szRequest;
pTemp = pTemp+nLen-4; //定位指針
if (strcmp(pTemp,"\r\n\r\n")==0) //校驗請求頭部行末尾的回車控制符和換行符以及空行
{
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; //指針長度
// pTemp = pTemp + n - 1; //將我們的指針下移
//定義一個臨時的緩沖區(qū)來存放我們
char szMode[10]={0};
char szFileName[10]={0};
memcpy(szMode,p,n); //將請求方法拷貝到szMode數(shù)組中
if (strcmp(szMode,"GET")==0) //一定要將Get寫成大寫
{
//獲取文件名
pTemp = strstr(pTemp," ");
pTemp = pTemp + 1; //只有調(diào)試的時候才能發(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程序的運行
{
bKeepAlive = TRUE;
}
//定義一個回顯頭
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);
//讀取文件
//定義一個文件流指針
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);
//計算出文件的大小后我們進行分配內(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)報文
}
//如果我們的文件沒有找到我們將引導(dǎo)用戶到另外的錯誤頁面
else
{
}
strcpy(szResponse,pResponseHeader);
strcat(szResponse,BufferTemp);
free(BufferTemp);
BufferTemp = NULL;
return true;
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助。
上一篇:C語言中用于產(chǎn)生隨機數(shù)的函數(shù)使用方法總結(jié)
欄 目:C語言
本文標(biāo)題:C++實現(xiàn)簡單的HTTP服務(wù)器
本文地址:http://www.jygsgssxh.com/a1/Cyuyan/2349.html
您可能感興趣的文章
- 04-02c語言沒有round函數(shù) round c語言
- 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計-用棧實現(xiàn)表達式求值的方法詳解
- 01-10使用OpenGL實現(xiàn)3D立體顯示的程序代碼
- 01-10深入理解C++中常見的關(guān)鍵字含義
- 01-10求斐波那契(Fibonacci)數(shù)列通項的七種實現(xiàn)方法
- 01-10C語言 解決不用+、-、&#215;、&#247;數(shù)字運算符做加法
- 01-10使用C++實現(xiàn)全排列算法的方法詳解
- 01-10c++中inline的用法分析
- 01-10用C++實現(xiàn)DBSCAN聚類算法
- 01-10深入全排列算法及其實現(xiàn)方法


閱讀排行
本欄相關(guān)
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言的正則匹配函數(shù) c語言正則表達
- 04-02c語言用函數(shù)寫分段 用c語言表示分段
- 04-02c語言中對數(shù)函數(shù)的表達式 c語言中對
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數(shù) c語言中怎
- 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求
隨機閱讀
- 01-10C#中split用法實例總結(jié)
- 01-11ajax實現(xiàn)頁面的局部加載
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 04-02jquery與jsp,用jquery
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-10delphi制作wav文件的方法
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文


