欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++實現(xiàn)簡單的HTTP服務(wù)器

 更新時間:2016年05月02日 19:29:52   作者:suvllian  
這篇文章主要為大家詳細(xì)介紹了C++實現(xiàn)簡單的HTTP服務(wù)器的相關(guān)資料,感興趣的朋友可以參考下

本文實例為大家分享了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)聽套接字與我們的事件進(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)行接受并申請內(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ù)包
    {
     //我在這里就進(jì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)
  {
    //在這里我沒有處理,我們要將內(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;
  }
  //我們要為用戶開辟新的線程
  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(&ltime);
 newtime = gmtime(&ltime);
 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);
  //計算出文件的大小后我們進(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)報文
 }
 //如果我們的文件沒有找到我們將引導(dǎo)用戶到另外的錯誤頁面
 else
 {
 }
 strcpy(szResponse,pResponseHeader);
 strcat(szResponse,BufferTemp);
 free(BufferTemp);
 BufferTemp = NULL;
 return true;
}

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助。

相關(guān)文章

  • 詳解c++中的異常

    詳解c++中的異常

    程序在運行過程中,有對也就有錯,正確那么就不用說了,但是如果錯誤,那么我們?nèi)绾慰焖俚亩ㄎ坏藉e誤的位置,以及知道發(fā)生了什么錯誤。當(dāng)一個函數(shù)發(fā)現(xiàn)自己無法處理的異常,就會拋出一個異常,讓函數(shù)調(diào)用者直接或者間接的處理這個錯誤。本文將詳解介紹c++中的異常
    2021-06-06
  • C語言泛型編程實例教程

    C語言泛型編程實例教程

    這篇文章主要介紹了C語言泛型編程,針對泛型的用法做了深入淺出的實例介紹,是C程序設(shè)計中非常實用的技巧,需要的朋友可以參考下
    2014-09-09
  • C++利用ImGUI繪制D3D外部菜單

    C++利用ImGUI繪制D3D外部菜單

    ImGUI 它是與平臺無關(guān)的C++輕量級跨平臺圖形界面庫,沒有任何第三方依賴,可以將ImGUI的源碼直接加到項目中使用。本文將利用ImGUI繪制D3D外部菜單,需要的可以參考一下
    2022-09-09
  • c++實現(xiàn)簡單隨機數(shù)的代碼

    c++實現(xiàn)簡單隨機數(shù)的代碼

    在本篇文章里小編給大家整理的是一篇關(guān)于c++實現(xiàn)簡單隨機數(shù)的代碼內(nèi)容,有需要的朋友們可以跟著學(xué)習(xí)下。
    2021-05-05
  • C語言超詳細(xì)講解函數(shù)指針的運用

    C語言超詳細(xì)講解函數(shù)指針的運用

    函數(shù)指針是一個指針變量,它可以存儲函數(shù)的地址,然后使用函數(shù)指針,下面這篇文章主要給大家介紹了關(guān)于C語言進(jìn)階教程之函數(shù)指針的相關(guān)資料,需要的朋友可以參考下
    2022-06-06
  • C++函數(shù)重載、隱藏與覆蓋重寫的精通指南

    C++函數(shù)重載、隱藏與覆蓋重寫的精通指南

    這篇文章主要給大家介紹了關(guān)于C++函數(shù)重載、隱藏與覆蓋重寫的相關(guān)資料,這幾個名詞看著好像很像,不過其實一樣都不一樣,本文通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-01-01
  • 使用Visual Studio 2010/2013編譯V8引擎步驟分享

    使用Visual Studio 2010/2013編譯V8引擎步驟分享

    這篇文章主要介紹了使用Visual Studio 2013編譯V8引擎步驟分享,需要的朋友可以參考下
    2015-08-08
  • 一文讀懂c++11 Lambda表達(dá)式

    一文讀懂c++11 Lambda表達(dá)式

    這篇文章主要介紹了c++11 Lambda表達(dá)式的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)C++,感興趣的朋友可以了解下
    2020-08-08
  • C++ 類的賦值運算符''''=''''重載的方法實現(xiàn)

    C++ 類的賦值運算符''''=''''重載的方法實現(xiàn)

    這篇文章主要介紹了C++ 類的賦值運算符'='重載的方法實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • C語言中雙鏈表的基本操作

    C語言中雙鏈表的基本操作

    這篇文章主要介紹了C語言中雙鏈表的基本操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02

最新評論