基本信息
源码名称:httpserver(c++ http服务器源码)
源码大小:7.28KB
文件格式:.rar
开发语言:C/C++
更新时间:2021-08-25
友情提示:(无需注册或充值,赞助后即可获取资源下载链接)
嘿,亲!知识可是无价之宝呢,但咱这精心整理的资料也耗费了不少心血呀。小小地破费一下,绝对物超所值哦!如有下载和支付问题,请联系我们QQ(微信同号):813200300
本次赞助数额为: 2 元×
微信扫码支付:2 元
×
请留下您的邮箱,我们将在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(<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* 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;
}