在好例子网,分享、交流、成长!
您当前所在位置:首页C/C++ 开发实例C/C++网络编程 → httpserver(c++ http服务器源码)

httpserver(c++ http服务器源码)

C/C++网络编程

下载此实例
  • 开发语言:C/C++
  • 实例大小:7.28KB
  • 下载次数:102
  • 浏览次数:411
  • 发布时间:2021-08-25
  • 实例类别:C/C++网络编程
  • 发 布 人:caoweimei
  • 文件格式:.rar
  • 所需积分:2
 相关标签: Server https http rve ps

实例介绍

【实例简介】
【实例截图】

【核心代码】

#include "stdafx.h"
#include <Winsock2.h>
#include <windows.h>
#include <malloc.h>
#include <cstdio>
#include <string>
#include <ctime>
#include <Common/HyHelper.h>
#include <json/json.h>
#pragma comment (lib,"ws2_32.lib")
#define uPort 8099
#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(std::string& szRequest);     //数据包的校验函数
bool AddClientList(SOCKET s, sockaddr_in addr);
bool AddThreadList(HANDLE hThread, DWORD ThreadID);
bool ParseRequest(std::string& szRequest, std::string& szResponse, BOOL &bKeepAlive);

//我们存放Html文件的目录
char HtmlDir[512] = { 0 };

void main()
{
	if (!InitSocket())
	{
		printf("InitSocket Error\n");
		return;
	}

	std::string appPath = HyHelper::wstring2string(HyHelper::GetAppPath());
	strcpy(HtmlDir, appPath.c_str());
	strcat(HtmlDir, "\\HTML\\");
	strcat(HtmlDir, FileName);
	printf("初始路径为 %s \n", HtmlDir);
	
	//启动一个接受线程
	HANDLE hAcceptThread = CreateThread(NULL, 0, AcceptThread, NULL, 0, NULL);
	if (hAcceptThread != INVALID_HANDLE_VALUE)
	{
		printf("开启HTTP服务线程......\n");
	}
	//在这里我们使用事件模型来实现我们的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;
	std::string szRequest; //请求报文
	std::string szResponse; //响应报文
	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);
			//我们在这里要检测是否得到的完整请求
			szRequest = std::string(szBuffer, NumberOfBytesRecvd);
			printf("Url请求:%s \n", szRequest.c_str());
			if (!IoComplete(szRequest)) //校验数据包
			{
				continue;
			}
			if (!ParseRequest(szRequest, szResponse, bKeepAlive)) //分析数据包
			{
				//我在这里就进行了简单的处理
				continue;
			}
			DWORD NumberOfBytesSent = 0;
			DWORD dwBytesSent = 0;
			//发送响应到客户端
			do
			{
				Buffers.len = (szResponse.length() - dwBytesSent) >= SENDBLOCK ? SENDBLOCK : szResponse.length() - dwBytesSent;
				Buffers.buf = (char*)((DWORD)szResponse.c_str()   dwBytesSent);
				Ret = WSASend(
					sClient,
					&Buffers,
					1,
					&NumberOfBytesSent,
					0,
					0,
					NULL);
				if (SOCKET_ERROR != Ret)
					dwBytesSent  = NumberOfBytesSent;
			} while ((dwBytesSent < szResponse.length()) && 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)
{
	char FAR * PASCAL strIp = inet_ntoa(addr.sin_addr);
	printf("有新客户端连接:%s:%d\n", strIp, addr.sin_port);
	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);
			CloseHandle(hThread);
			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(std::string& szRequest)
{
	const char* pTemp = NULL;   //定义临时空指针
	int nLen = szRequest.length(); //请求数据包长度
	pTemp = szRequest.c_str();
	pTemp = pTemp   nLen - 4; //定位指针
	if (strcmp(pTemp, "\r\n\r\n") == 0)   //校验请求头部行末尾的回车控制符和换行符以及空行
	{
		return true;
	}
	return false;
}

//分析数据包
bool ParseRequest(std::string& szRequest, std::string& szResponse, BOOL &bKeepAlive)
{
	const char* p = szRequest.c_str();
	int n = 0;
	const char* pTemp = strstr(p, " "); //判断字符串str2是否是str1的子串。如果是,则该函数返回str2在str1中首次出现的地址;否则,返回NULL。
	n = pTemp - p;    //指针长度
					  // pTemp = pTemp   n - 1; //将我们的指针下移
					  //定义一个临时的缓冲区来存放我们
	char szMode[10] = { 0 };
	char szFileName[128] = { 0 };
	memcpy(szMode, p, n);   //将请求方法拷贝到szMode数组中
	if (strcmp(szMode, "GET") == 0 || strcmp(szMode, "POST") == 0)  //一定要将Get写成大写
	{
		//获取文件名
		pTemp = strstr(pTemp, " ");
		pTemp = pTemp   1;   //只有调试的时候才能发现这里的秘密
		memcpy(szFileName, pTemp, 1);
		if (strcmp(szFileName, "/") == 0)
		{
			strcpy(szFileName, FileName);
		}
		else
		{
			return false;
		}
	}
	else
	{
		return false;
	}
	// 分析链接类型
	pTemp = strstr(szRequest.c_str(), "\nConnection: Keep-Alive");  //协议版本
	n = pTemp - p;
	if (p > 0)
	{
		bKeepAlive = TRUE;
	}
	else  //这里的设置是为了Proxy程序的运行
	{
		bKeepAlive = TRUE;
	}
	//定义一个回显头
	std::string pHeader = "";
	std::string szStatusCode = "";
	std::string szContentType = "";
	szStatusCode = "200 OK";
	szContentType = "application/json;charset=UTF-8";
	char szDT[128];
	struct tm *newtime;
	time_t ltime = time(NULL);
	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* pBuffer = NULL;
	if (fp != NULL)
	{
		// 获得文件大小
		fseek(fp, 0, SEEK_END);
		fgetpos(fp, &lengthActual);
		fseek(fp, 0, SEEK_SET);
		//计算出文件的大小后我们进行分配内存
		pBuffer = new char[lengthActual];
		memset(pBuffer, 0, lengthActual);
		fseek(fp, 3, SEEK_SET);
		length = fread(pBuffer, 1, (int)lengthActual-3, fp);
		fclose(fp);
		// 返回响应		
		//sprintf(pHeader,
			pHeader.append("HTTP/1.0 ");
			pHeader.append(szStatusCode);
			pHeader.append("\r\nDate: ");
			pHeader.append(szDT);
			pHeader.append("\r\nServer:");
			pHeader.append(SERVERNAME);
			pHeader.append("\r\nAccept-Ranges: bytes\r\nContent-Length:");
			char pValue[16] = { 0 };
			_itoa_s(length, pValue, 10);			
			pHeader.append(pValue);
			pHeader.append("\r\nConnection:");
			
			if (bKeepAlive)
			{
				pHeader.append("Keep-Alive");
			}
			else
			{
				pHeader.append("close");
			}
			pHeader.append("\r\nContent-Type:");
			pHeader.append(szContentType);
			pHeader.append("\r\n\r\n");
			
		if (!pHeader.empty())
		{
			szResponse  = pHeader;			
		}
		if (pBuffer)
		{
			szResponse  = pBuffer;
			std::string strRes = HyHelper::UTF8ToGBK(pBuffer);
			printf("Url响应:%s\n", strRes.c_str());
			delete[] pBuffer;
			pBuffer = NULL;
		}
	}	
	return true;
}

标签: Server https http rve ps

实例下载地址

httpserver(c++ http服务器源码)

不能下载?内容有错? 点击这里报错 + 投诉 + 提问

好例子网口号:伸出你的我的手 — 分享

网友评论

第 1 楼 56797360 发表于: 2023-10-11 22:27 01
下载解压不了呀

支持(0) 盖楼(回复)

发表评论

(您的评论需要经过审核才能显示)

查看所有1条评论>>

小贴士

感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。

  • 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
  • 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
  • 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
  • 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。

关于好例子网

本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明

;
报警