• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > C++实现简单的HTTP服务器

C++实现简单的HTTP服务器

作者:suvllian 字体:[增加 减小] 来源:互联网 时间:2017-05-28

suvllian 通过本文主要向大家介绍了c++ http服务器,c++ http,c++ http请求,c++服务器开发,c++服务器开发教程等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

本文实例为大家分享了C++实现HTTP服务器的相关代码,供大家参考,具体内容如下

#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();//线程函数
DWORD WINAPI AcceptThread(LPVOID lpParam);
DWORD WINAPI ClientThread(LPVOID lpParam);
bool IoComplete(char* szRequest);     //数据包的校验函数
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);
 
 //在这里我们使用事件模型来实现我们的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;
 char szRequest[1024]={0}; //请求报文
 char szResponse[1024]={0}; //响应报文
 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);
    //我们在这里要检测是否得到的完整请求
    memcpy(szRequest,szBuffer,NumberOfBytesRecvd);
    if (!IoComplete(szRequest)) //校验数据包
    {
     continue;
    }
    if (!ParseRequest(szRequest, szResponse, bKeepAlive)) //分析数据包
    {
     //我在这里就进行了简单的处理
     continue;
    }
    DWORD NumberOfBytesSent = 0;
    DWORD dwBytesSent = 0;
    //发送响应到客户端
    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)
  {
    //在这里我没有处理,我们要将内存进行释放否则内存泄露
  }
 }
 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)
{
 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;
}
 
//校验数据包
bool IoComplete(char* szRequest)
{
 char* pTemp = NULL;   //定义临时空指针
 int nLen = strlen(szRequest); //请求数据包长度
 pTemp = szRequest;   
 pTemp = pTemp+nLen-4; //定位指针
 if (strcmp(pTemp,"\r\n\r\n")==0)   //校验请求头部行末尾的回车控制符和换行符以及空行
 {
  return true;
 }
 return false;
}
 
//分析数据包
bool ParseRequest(char* szRequest, char* szResponse, BOOL &bKeepAlive)
{
 char* p = NULL;
 p = szRequest;
 int n = 0;
 char* pTemp = strstr(p," "); //判断字符串str2是否是str1的子串。如果是,则该函数返回str2在str1中首次出现的地址;否则,返回NULL。
 n = pTemp - p;    //指针长度
// pTemp = pTemp + n - 1; //将我们的指针下移
 //定义一个临时的缓



 
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C++实现简单的HTTP服务器
  • C++简单QQ程序服务器端的实现代码
  • C++发送HTTP请求的实现代码
  • c++实现发送http请求通过get方式获取网页源代码

相关文章

  • 2017-05-28深入C++浮点数无效值定义与判定的解决办法
  • 2018-08-06支持C++,python,java等语言的跨平台的农历库(天文历算法)
  • 2017-05-28c++实现MD5算法实现代码
  • 2022-04-30C语言函数指针(指向函数的指针)详解
  • 2017-05-28详解C语言中条件判断语句if和switch的用法
  • 2017-05-28C++ 构造函数中使用new时注意事项
  • 2017-05-28数据结构之堆详解
  • 2017-05-28C++中拷贝构造函数的应用详解
  • 2017-05-28Species Tree 利用HashTable实现实例代码
  • 2017-05-28关于STL中list容器的一些总结

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • (C和指针) #if 0/#if 1...#end if
    • 直观理解C语言中指向一位数组与二维数组的指针
    • C++中 静态局部变量实例详解
    • LintCode 堆化详解及实例代码
    • 打印菱形以及斐波纳契数列的几种解法介绍
    • 完全掌握C++编程中构造函数使用的超级学习教程
    • C/C++ ip地址与int类型的转换实例详解
    • C++动态数组类的封装实例
    • 数组指针、指针数组以及二位数组的深入解析
    • 如何在程序中判断VS的版本(实现方法详解)

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有