基本信息
源码名称:httpserver(c++ http服务器源码)
源码大小:7.28KB
文件格式:.rar
开发语言:C/C++
更新时间:2021-08-25
   友情提示:(无需注册或充值,赞助后即可获取资源下载链接)

     嘿,亲!知识可是无价之宝呢,但咱这精心整理的资料也耗费了不少心血呀。小小地破费一下,绝对物超所值哦!如有下载和支付问题,请联系我们QQ(微信同号):813200300

本次赞助数额为: 2 元 
   源码介绍


#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;
}