HTTP 服务器程序
2015-08-01 14:35
716 查看
1、 HTTP只有两类消息:HTTP请求消息,由客户机端(如浏览器)向服务器发送的消息,用于请求服务器提供某种类型的服务;HTTP响应消息,服务器接收到请求消息之后返回给客户端的信息,表明服务器所作出的回答。两种消息具有相同的格式,通常分为消息头和消息体两个部分。消息头一定要有,消息体是可选的。消息头部的首行有特殊的格式,首行的后面有多个头部标题字段行,也简称为消息标题。每个头部标题行由标题字段名和字段值组成,他们之间有“:”隔开,标题字段名不区分大小写。
HTTP请求消息的一般格式:HTTP请求的头部细分为请求行和标题字段区域。头部请求行是HTTP请求报文的第一行,交代请求使用的方法,请求的目标,和HTTP的版本号。例:GET /index.html HTTP/1.1;HTTP请求的第二行起是头部的标题字段区域,可以安排一行或多行消息标题,常用字段有Accept 表示客户端期望接收的媒体类型,Host 表明请求资源所在的主机地址,Referer 用于记录客户端获得URL资源的地址,User-Agent
使用的用户代理,Connection 对TCP连接的处理方式。
典型的HTTP请求消息:
GET /path/file.html HTTP/1.0
User-Agent:HTTPTool/1.0
Connection:Close
HTTP响应消息的一般格式:HTTP响应消息由两大部分组成,消息头和消息体,两者之间使用一个空白行分开。消息头又可细分为响应的状态行和响应的标题字段,消息体是响应的实体数据。HTTP响应消息的第一行为状态行,由HTTP版本号、响应码和响应描述符文本构成,中间用空格相连。例:HTTP/1.1 200 ok。HTTP状态码分为5种类型:1xx 信息响应,表示接收到请求并且继续处理,2xx 处理成功响应,表示动作被成功接收、理解和接收、3xx
重定向响应,为了完成指定的动作,必须接受进一步处理,4xx 客户端错误,请求包含语法错误或者请求无法实现,5xx 服务器错误,服务器不能正确执行一个正确的请求。最常用的状态码有:
200 OK:请求成功,并且被请求的资源将会在响应消息中返回。
301 Moved Permanently:客户请求的对象已永久性迁移,新的URL在Location头中给出,浏览器会自动访问新的URL。
302 Moved Temporarily:所请求的对象被暂时迁移
400 Bad Request:服务器无法理解客户端的请求
404 Not Found:服务器不存在所请求的文档。客户端在对该请求作出更改前不应再次向服务器重复发送该请求
500 Server Error:服务器异常,不能完成客户的请求。
505 HTTP Version Not Supported:服务器不支持所请求的HTTP协议版本
HTTP响应可用的标题字段分为响应标题和实体标题两类:
响应标题有:Location 表示请求被重定向的URL的实际位置,使用绝对URL,并在响应字段返回3xx的响应码
Server 用于告知客户端服务器端使用的www服务器软件名称及版本信息
WWW-Authenticate 告知客户端受限资源所在的区域和要求的认证方法,同时应返回401响应码
实体标题有:Allow 告知客户机端他请求的资源所允许的请求方法
Content-Type 用于描述HTTP响应消息中所含有的实体数据的类型
Content-Length 用于描述消息内任何类型的实体数据长度
2、程序主流程图:
ClientThread流程图:
3、具体代码注释:
HTTPProtocol.h文件:
#ifndef _HTTPPROTOCOL_H
#define _HTTPPRPTOCOL_H
#pragma once
#include <WinSock2.h>
#pragma comment( lib, "ws2_32.lib")
#define HTTPPORT 80
#define METHOD_GET 0
#define METHOD_POST 1
#define METHOD_HEAD 2
//响应码
#define HTTP_STATUS_OK "200 OK"
#define HTTP_STATUS_CREATED "201 Created"
#define HTTP_STATUS_ACCEPTED "202 Accepted"
#define HTTP_STATUS_NOCONTENT "204 No Content"
#define HTTP_STATUS_MOVEDPERM "301 Moved Permanently"
#define HTTP_STATUS_MOVEDTEMP "302 Moved Temporarily"
#define HTTP_STATUS_NOTMODIFIED "304 Not Modified"
#define HTTP_STATUS_BADREQUEST "400 Bad Request"
#define HTTP_STATUS_UNAUTHORIZED "401 Unauthorized"
#define HTTP_STATUS_FORBIDDEN "403 Forbidden"
#define HTTP_STATUS_NOTFOUND "404 File can not fonund!"
#define HTTP_STATUS_SERVERERROR "500 Internal Server Error"
#define HTTP_STATUS_NOTIMPLEMENTED "501 Not Implemented"
#define HTTP_STATUS_BADGATEWAY "502 Bad Gateway"
#define HTTP_STATUS_UNAVAILABLE "503 Service Unavailable"
typedef struct REQUEST
{
HANDLE hExit;
SOCKET Socket; //请求的socket
int nMethod; //请求的使用方法
DWORD dwRecv; //收到的字节数
DWORD dwSend; //发送的字节数
HANDLE hFile; //请求连接的文件
char szFileName[_MAX_PATH]; //文件的相对路径
char postfix[10]; //存储扩展名
char StatuCodeReason[100]; //响应码
bool permitted; //用户权限判断
char* authority; //用户提供的认证信息
char key[1024]; //正确认证信息
void* pHttpProtocol; //指向类CHttpProtocol的指针
}REQUEST,*PREQUEST;
#include <map>
#include <string>
using namespace std;
class CHttpProtocol
{
public:
HWND m_hwndDlg;
SOCKET m_listenSocket;
map<CString,char *> m_typeMap; //保存content-type和文件后缀的对应关系
CWinThread* m_pListenThread;
HANDLE m_hExit;
static HANDLE None; //标志是否有Client连接到服务器
static UINT ClientNum; //连接的Client数量
static CCriticalSection m_critSect; //互斥变量
CString m_strRootDir; //web的根目录
UINT m_nPort; //服务器的端口号
void CreateTypeMap(); //创建content-type和文件后缀map
void StopHttpSrv(); //关闭服务
bool StartHttpSrv(); //启动服务
static UINT ListenThread(LPVOID param); //监听线程
static UINT ClientThread(LPVOID param); //请求处理线程
bool RecvRequest(PREQUEST pReq, LPBYTE pBuf, DWORD dwBufSize);
int Analyze(PREQUEST pReq, LPBYTE pBuf);
void SendHeader(PREQUEST pReq);
int FileExist(PREQUEST pReq);
void SendFile(PREQUEST pReq);
bool SendBuffer(PREQUEST pReq, LPBYTE pBuf, DWORD dwBufSize);
bool GetContenType(PREQUEST pReq, LPSTR type);
CHttpProtocol(void);
~CHttpProtocol(void);
};
#endif _HTTPPROTOCOL_H
********************************************************************************************************************************
HTTPProtocol.cpp文件:
#include "stdafx.h"
#include "HttpProtocol.h"
#include "process.h"
#include "MFCHTTPSERVER.h"
#include "resource.h"
#include "MFCHTTPSERVERDlg.h"
//静态变量初始化
UINT CHttpProtocol::ClientNum = 0;
CCriticalSection CHttpProtocol::m_critSect; // 排斥区初始化
HANDLE CHttpProtocol::None = NULL;
CHttpProtocol::CHttpProtocol(void)
{
m_pListenThread=NULL;
m_hwndDlg=NULL;
}
CHttpProtocol::~CHttpProtocol(void)
{
}
void CHttpProtocol::CreateTypeMap()
{
// 初始化map
m_typeMap[".doc"] = "application/msword";
m_typeMap[".bin"] = "application/octet-stream";
m_typeMap[".dll"] = "application/octet-stream";
m_typeMap[".exe"] = "application/octet-stream";
m_typeMap[".pdf"] = "application/pdf";
m_typeMap[".ai"] = "application/postscript";
m_typeMap[".eps"] = "application/postscript";
m_typeMap[".ps"] = "application/postscript";
m_typeMap[".rtf"] = "application/rtf";
m_typeMap[".fdf"] = "application/vnd.fdf";
m_typeMap[".arj"] = "application/x-arj";
m_typeMap[".gz"] = "application/x-gzip";
m_typeMap[".class"] = "application/x-java-class";
m_typeMap[".js"] = "application/x-javascript";
m_typeMap[".lzh"] = "application/x-lzh";
m_typeMap[".lnk"] = "application/x-ms-shortcut";
m_typeMap[".tar"] = "application/x-tar";
m_typeMap[".hlp"] = "application/x-winhelp";
m_typeMap[".cert"] = "application/x-x509-ca-cert";
m_typeMap[".zip"] = "application/zip";
m_typeMap[".cab"] = "application/x-compressed";
m_typeMap[".arj"] = "application/x-compressed";
m_typeMap[".aif"] = "audio/aiff";
m_typeMap[".aifc"] = "audio/aiff";
m_typeMap[".aiff"] = "audio/aiff";
m_typeMap[".au"] = "audio/basic";
m_typeMap[".snd"] = "audio/basic";
m_typeMap[".mid"] = "audio/midi";
m_typeMap[".rmi"] = "audio/midi";
m_typeMap[".mp3"] = "audio/mpeg";
m_typeMap[".vox"] = "audio/voxware";
m_typeMap[".wav"] = "audio/wav";
m_typeMap[".ra"] = "audio/x-pn-realaudio";
m_typeMap[".ram"] = "audio/x-pn-realaudio";
m_typeMap[".bmp"] = "image/bmp";
m_typeMap[".gif"] = "image/gif";
m_typeMap[".jpeg"] = "image/jpeg";
m_typeMap[".jpg"] = "image/jpeg";
m_typeMap[".tif"] = "image/tiff";
m_typeMap[".tiff"] = "image/tiff";
m_typeMap[".xbm"] = "image/xbm";
m_typeMap[".wrl"] = "model/vrml";
m_typeMap[".htm"] = "text/html";
m_typeMap[".html"] = "text/html";
m_typeMap[".c"] = "text/plain";
m_typeMap[".cpp"] = "text/plain";
m_typeMap[".def"] = "text/plain";
m_typeMap[".h"] = "text/plain";
m_typeMap[".txt"] = "text/plain";
m_typeMap[".rtx"] = "text/richtext";
m_typeMap[".rtf"] = "text/richtext";
m_typeMap[".java"] = "text/x-java-source";
m_typeMap[".css"] = "text/css";
m_typeMap[".mpeg"] = "video/mpeg";
m_typeMap[".mpg"] = "video/mpeg";
m_typeMap[".mpe"] = "video/mpeg";
m_typeMap[".avi"] = "video/msvideo";
m_typeMap[".mov"] = "video/quicktime";
m_typeMap[".qt"] = "video/quicktime";
m_typeMap[".shtml"] = "wwwserver/html-ssi";
m_typeMap[".asa"] = "wwwserver/isapi";
m_typeMap[".asp"] = "wwwserver/isapi";
m_typeMap[".cfm"] = "wwwserver/isapi";
m_typeMap[".dbm"] = "wwwserver/isapi";
m_typeMap[".isa"] = "wwwserver/isapi";
m_typeMap[".plx"] = "wwwserver/isapi";
m_typeMap[".url"] = "wwwserver/isapi";
m_typeMap[".cgi"] = "wwwserver/isapi";
m_typeMap[".php"] = "wwwserver/isapi";
m_typeMap[".wcgi"] = "wwwserver/isapi";
}
bool CHttpProtocol::StartHttpSrv()
{
WORD wVersionRequested=WINSOCK_VERSION;
WSADATA wsaData;
int nRet;
//启动Winsock
nRet=WSAStartup(wVersionRequested,&wsaData);
if(nRet)
{
//错误处理
AfxMessageBox("Initialize WinSock Failed");
return false;
}
m_hExit=CreateEvent(NULL,TRUE,FALSE,NULL);
if(m_hExit==NULL)
{
return false;
}
//创建套接字
m_listenSocket=WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED);
if(m_listenSocket==INVALID_SOCKET)
{
//异常处理
return false;
}
SOCKADDR_IN sockAddr;
// LPSERVENT lpServEnt;
if(m_nPort!=0)
{
sockAddr.sin_port=htons(m_nPort);
}
else
{
sockAddr.sin_port=htons(HTTPPORT);//默认端口号
}
sockAddr.sin_family=AF_INET;
char localName[256]; //本地机器名
gethostname(localName,256); //获取本机名
HOSTENT * pHost; //指向主机信息的指针
pHost=gethostbyname(localName);
sockAddr.sin_addr=*(in_addr *)pHost->h_addr_list[0];//设定IP地址
// 初始化content-type和文件后缀对应关系的map
CreateTypeMap();
//套接字绑定
nRet=bind(m_listenSocket,(LPSOCKADDR)&sockAddr,sizeof(sockaddr));
if(nRet==SOCKET_ERROR)
{
//绑定发生错误
closesocket(m_listenSocket); //断开连接
return false;
}
//套接字监听
nRet=listen(m_listenSocket,SOMAXCONN);
if(nRet==SOCKET_ERROR)
{
closesocket(m_listenSocket); //断开连接
return false;
}
//创建listening进程,接受客户机连接请求
m_pListenThread=AfxBeginThread(ListenThread,this);
if(!m_pListenThread)
{
closesocket(m_listenSocket);
return false;
}
CString *pStr1=new CString;
pStr1->Format("%s",localName);
CString strIP;
strIP=inet_ntoa(*(struct in_addr*)*(pHost->h_addr_list));
*pStr1=*pStr1+"["+strIP+"]"+"Port";
CString strTemp;
strTemp.Format("%d",htons(sockAddr.sin_port));
*pStr1=*pStr1+strTemp;
SendMessage(m_hwndDlg,LOG_MSG,(UINT)pStr1,NULL);
return true;
}
UINT CHttpProtocol::ListenThread(LPVOID param)
{
CHttpProtocol *pHttpProtocol=(CHttpProtocol *)param;
SOCKET socketClient;
CWinThread* pClientThread;
SOCKADDR_IN SockAddr;
PREQUEST pReq;
int nLen;
//DWORD dwRet;
ClientNum=0;
HANDLE hNoClients;
hNoClients=CreateEvent(NULL,TRUE,TRUE,NULL);
while(1)
{
nLen=sizeof(SockAddr);
//套接字等待连接,返回对应已接受的客户机连接的套接字
socketClient=accept(pHttpProtocol->m_listenSocket,(LPSOCKADDR)&SockAddr,&nLen);
if(socketClient==INVALID_SOCKET)
{
continue;
}
//将客户端网络地址转换为用点分割的IP地址
CString *pStr=new CString;
pStr->Format("%s Connecting on socket:%d",inet_ntoa(SockAddr.sin_addr),socketClient);
SendMessage(pHttpProtocol->m_hwndDlg,LOG_MSG,(UINT)pStr,NULL);
pReq=new REQUEST;
if(pReq==NULL)
{
AfxMessageBox("No memory for request");
continue;
}
pReq->hExit=pHttpProtocol->m_hExit;
pReq->Socket=socketClient;
pReq->hFile=INVALID_HANDLE_VALUE;
pReq->dwRecv=0;
pReq->dwSend=0;
pReq->pHttpProtocol=pHttpProtocol;
//创建client进程,处理request
pClientThread=AfxBeginThread(ClientThread,pReq);
if(!pClientThread)
{
delete pReq;
}
}
//等待所有线程结束
WaitForSingleObject((HANDLE)pHttpProtocol->m_hExit,INFINITE);
//等待所有client进程结束
WaitForSingleObject(hNoClients,5000);
CloseHandle(None);
return 0;
}
UINT CHttpProtocol::ClientThread(LPVOID param)
{
int nRet;
BYTE buf[1024];
PREQUEST pReq=(PREQUEST)param;
CHttpProtocol *pHttpProtocol=(CHttpProtocol *)pReq->pHttpProtocol;
//进入临界区
m_critSect.Lock();
ClientNum++;
//离开临界区
m_critSect.Unlock();
//重置为无信号事件对象
ResetEvent(None);
if(!pHttpProtocol->RecvRequest(pReq,buf,sizeof(buf)))
{
closesocket(pReq->Socket);
delete pReq;
m_critSect.Lock();
if(ClientNum > 0)
{
ClientNum--;
}
// 离开排斥区
m_critSect.Unlock();
if(ClientNum < 1)
{
// 重置为有信号事件对象
SetEvent(None);
}
return 0;
}
nRet=pHttpProtocol->Analyze(pReq,buf);
if(nRet)
{
closesocket(pReq->Socket);
delete pReq;
m_critSect.Lock();
if(ClientNum > 0)
{
ClientNum--;
}
// 离开排斥区
m_critSect.Unlock();
if(ClientNum < 1)
{
// 重置为有信号事件对象
SetEvent(None);
}
return 0;
}
//生成并返回头部
pHttpProtocol->SendHeader(pReq);
if(pReq->nMethod==METHOD_GET)
{
pHttpProtocol->SendFile(pReq);
}
closesocket(pReq->Socket);
delete pReq;
//进入临界区
m_critSect.Lock();
if(ClientNum>0)
{
ClientNum--;
}
//离开临界区
m_critSect.Unlock();
if(ClientNum<1)
{
//重置为有信号事件对象
SetEvent(None);
}
return 0;
}
bool CHttpProtocol::RecvRequest(PREQUEST pReq, LPBYTE pBuf, DWORD dwBufSize)
{
WSABUF wsabuf; //发送/接收缓冲区结构
WSAOVERLAPPED over; //指向调用重叠操作时指定的WSAOVERLAPPED结构
DWORD dwRecv;
DWORD dwFlags;
DWORD dwRet;
HANDLE hEvents[2];
bool fPending;
int nRet;
memset(pBuf,0,dwBufSize); //初始化缓冲区
wsabuf.buf=(char *)pBuf;
wsabuf.len=dwBufSize;
over.hEvent=WSACreateEvent();
dwFlags=0;
fPending=FALSE;
//接收数据
nRet=WSARecv(pReq->Socket,&wsabuf,1,&dwRecv,&dwFlags,&over,NULL);
if(nRet!=0)
{
//错误代码WSA_IO_PENDING表示重叠操作成功启动
if(WSAGetLastError()!=WSA_IO_PENDING)
{
//重叠操作未能成功
CloseHandle(over.hEvent);
return false;
}
else
{
fPending=true;
}
}
if(fPending)
{
hEvents[0]=over.hEvent;
hEvents[1]=pReq->hExit;
dwRet=WaitForMultipleObjects(2,hEvents,FALSE,INFINITE);
if(dwRet!=0)
{
CloseHandle(over.hEvent);
return false;
}
//重叠操作未完成
if(!WSAGetOverlappedResult(pReq->Socket,&over,&dwRecv,FALSE,&dwFlags))
{
CloseHandle(over.hEvent);
return false;
}
}
CloseHandle(over.hEvent);
return true;
}
int CHttpProtocol::Analyze(PREQUEST pReq, LPBYTE pBuf)
{
//分析接收到的信息
char szSeps[]="\n";
char *cpToken;
//cpToken=strtok((char *)pBuf,szSeps);// 缓存中字符串以“\n”为分解符分解为一组标记串,返回分解后的第一组字符串
// 防止非法请求
if (strstr((const char *)pBuf, "..") != NULL)//找到第二个字符串在第一个字符串第一次出现的位置
{
strcpy(pReq->StatuCodeReason, HTTP_STATUS_BADREQUEST);
return 1;
}
// 判断ruquest的mothed
cpToken = strtok((char *)pBuf, " "); // 缓存中字符串以“\n”为分解符分解为一组标记串,返回分解后的第一组字符串
//AfxMessageBox(cpToken);
//if(!_stricmp(cpToken,"GET"))
if(strstr(cpToken,"GET"))
{
pReq->nMethod=METHOD_GET;
}
//else if (!_stricmp(cpToken, "HEAD")) // HEAD命令
else if(strstr(cpToken,"HEAD"))
{
pReq->nMethod = METHOD_HEAD;
}
else
{
strcpy(pReq->StatuCodeReason, HTTP_STATUS_NOTIMPLEMENTED);
return 1;
}
//获取Request-URL
cpToken=strtok(NULL," "); //返回第二组字符串,即资源的路径
// AfxMessageBox(cpToken);
if(cpToken==NULL)
{
strcpy(pReq->StatuCodeReason,HTTP_STATUS_BADREQUEST);
return 1;
}
strcpy(pReq->szFileName,m_strRootDir);
if(strlen(cpToken)>1)
{
strcat(pReq->szFileName,cpToken); //把文件名添加到结尾处形成路径
}
else
{
strcat(pReq->szFileName,"/index.html");
}
// AfxMessageBox(pReq->szFileName);
return 0;
}
// 发送头部
void CHttpProtocol::SendHeader(PREQUEST pReq)
{
int n = FileExist(pReq);
if(!n)
{
return;
}
char Header[2048]="";
DWORD length;
length=GetFileSize(pReq->hFile,NULL);
char ContenType[50]="";
GetContenType(pReq,(char *)ContenType);
sprintf((char *)Header,"HTTP/1.0 %s\r\nServer:%s\r\nContent-Type:%s\r\nContent-Length:%d\r\n",
HTTP_STATUS_OK,
"My Http Server",
ContenType,
length);
//发送头部
send(pReq->Socket,Header,strlen(Header),0);
}
void CHttpProtocol::SendFile(PREQUEST pReq)
{
int n=FileExist(pReq);
if(!n)
{
return;
}
CString *pStr=new CString;
*pStr=*pStr+&pReq->szFileName[strlen(m_strRootDir)];
SendMessage(m_hwndDlg,LOG_MSG,UINT(pStr),NULL);
static BYTE buf[2048];
DWORD dwRead;
BOOL fRet;
int flag=1;
//读写数据直到完成
while(1)
{
//从file中读入到buffer中
fRet=ReadFile(pReq->hFile,buf,sizeof(buf),&dwRead,NULL);
if(!fRet)
{
static char szMsg[512];
wsprintf(szMsg,"%s",HTTP_STATUS_SERVERERROR);
//向客户端发送出错信息
send(pReq->Socket,szMsg,strlen(szMsg),0);
break;
}
//完成
if(dwRead==0)
{
break;
}
//将buffer内容传送给client
if(!SendBuffer(pReq,buf,dwRead))
{
break;
}
}
//关闭文件
if(CloseHandle(pReq->hFile))
{
pReq->hFile=INVALID_HANDLE_VALUE;
}
else
{
CString *pStr=new CString;
*pStr="Error occurs when closing file";
SendMessage(m_hwndDlg,LOG_MSG,(UINT)pStr,NULL);
}
}
bool CHttpProtocol::SendBuffer(PREQUEST pReq,LPBYTE pBuf,DWORD dwBufSize)
{
//发送缓存中的内容
WSABUF wsabuf;
WSAOVERLAPPED over;
DWORD dwRecv;
DWORD dwFlags;
DWORD dwRet;
HANDLE hEvents[2];
BOOL fPending;
int nRet;
wsabuf.buf=(char *)pBuf;
wsabuf.len=dwBufSize;
over.hEvent=WSACreateEvent();
fPending=false;
//发送数据
nRet=WSASend(pReq->Socket,&wsabuf,1,&dwRecv,0,&over,NULL);
if(nRet!=0)
{
//错误处理
if(WSAGetLastError()==WSA_IO_PENDING)
{
fPending=true;
}
else
{
CString *pStr=new CString;
pStr->Format("WSASend() error:%d",WSAGetLastError());
SendMessage(m_hwndDlg,LOG_MSG,(UINT)pStr,NULL);
CloseHandle(over.hEvent);
return false;
}
}
if(fPending)
{
hEvents[0]=over.hEvent;
hEvents[1]=pReq->hExit;
dwRet=WaitForMultipleObjects(2,hEvents,FALSE,INFINITE);
if(dwRet!=0)
{
CloseHandle(over.hEvent);
return false;
}
//重叠操作未完成
if(!WSAGetOverlappedResult(pReq->Socket,&over,&dwRecv,FALSE,&dwFlags))
{
//错误处理
CString *pStr=new CString;
pStr->Format("WSAGetOverlappedResult() error:%d",WSAGetLastError());
SendMessage(m_hwndDlg,LOG_MSG,(UINT)pStr,NULL);
CloseHandle(over.hEvent);
return false;
}
}
CloseHandle(over.hEvent);
return true;
}
int CHttpProtocol::FileExist(PREQUEST pReq)
{
pReq->hFile = CreateFile(pReq->szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// int d=access(pReq->hFile,00);
// 如果文件不存在,则返回出错信息
AfxMessageBox(pReq->szFileName);
if (pReq->hFile == INVALID_HANDLE_VALUE)
{
strcpy(pReq->StatuCodeReason, HTTP_STATUS_NOTFOUND);
return 0;
}
else
{
return 1;
}
}
bool CHttpProtocol::GetContenType(PREQUEST pReq, LPSTR type)
{
// 取得文件的类型
CString cpToken;
cpToken = strstr(pReq->szFileName, ".");
strcpy(pReq->postfix, cpToken);
// 遍历搜索该文件类型对应的content-type
map<CString, char *>::iterator it = m_typeMap.find(pReq->postfix);
if(it != m_typeMap.end())
{
wsprintf(type,"%s",(*it).second);
}
return TRUE;
}
void CHttpProtocol::StopHttpSrv()
{
int nRet;
SetEvent(m_hExit);
nRet=closesocket(m_listenSocket);
nRet=WaitForSingleObject((HANDLE)m_pListenThread,10000);
if(nRet==WAIT_TIMEOUT)
{
CString *pStr=new CString;
*pStr="TIMEOUT waiting for ListenThread";
SendMessage(m_hwndDlg,LOG_MSG,(UINT)pStr,NULL);
}
CloseHandle(m_hExit);
CString *pStr1=new CString;
*pStr1="Server Stopped";
SendMessage(m_hwndDlg,LOG_MSG,(UINT)pStr1,NULL);
}
*********************************************************************************************************************
MFCHTTPSERVERDlg.h文件:
// MFCHTTPSERVERDlg.h : 头文件
//
#include "HttpProtocol.h"
#pragma once
#define LOG_MSG (WM_USER+100)
// CMFCHTTPSERVERDlg 对话框
class CMFCHTTPSERVERDlg : public CDialog
{
// 构造
public:
CMFCHTTPSERVERDlg(CWnd* pParent = NULL); // 标准构造函数
CHttpProtocol *pHttpProtocol;
bool m_bStart;
// 对话框数据
enum { IDD = IDD_MFCHTTPSERVER_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg LRESULT AddLog(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
public:
UINT m_nPort;
CString m_strRootDir;
afx_msg void OnBnClickedStartStop();
afx_msg void OnBnClickedCancel();
};
******************************************************************************************************************************
MFCHTTPSERVERDlg.cpp文件:
// MFCHTTPSERVERDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "MFCHTTPSERVER.h"
#include "MFCHTTPSERVERDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 对话框数据
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CMFCHTTPSERVERDlg 对话框
CMFCHTTPSERVERDlg::CMFCHTTPSERVERDlg(CWnd* pParent /*=NULL*/)
: CDialog(CMFCHTTPSERVERDlg::IDD, pParent)
, m_nPort(8000)
, m_strRootDir(_T(""))
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMFCHTTPSERVERDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Text(pDX, IDC_PORT, m_nPort);
DDX_Text(pDX, IDC_ROOTDIR, m_strRootDir);
}
BEGIN_MESSAGE_MAP(CMFCHTTPSERVERDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_MESSAGE(LOG_MSG, AddLog)
ON_BN_CLICKED(IDOK, &CMFCHTTPSERVERDlg::OnBnClickedStartStop)
ON_BN_CLICKED(IDCANCEL, &CMFCHTTPSERVERDlg::OnBnClickedCancel)
END_MESSAGE_MAP()
// CMFCHTTPSERVERDlg 消息处理程序
BOOL CMFCHTTPSERVERDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
m_bStart=false;
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CMFCHTTPSERVERDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CMFCHTTPSERVERDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CMFCHTTPSERVERDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
LRESULT CMFCHTTPSERVERDlg::AddLog(WPARAM wParam, LPARAM lParam)
{
char szBuf[284];
CString *strTemp=(CString *)wParam;
CListBox* pListBox;
pListBox=(CListBox*)GetDlgItem(IDC_LOG);
SYSTEMTIME st;
GetLocalTime(&st);
wsprintf(szBuf,"%02d:%02d:%02d.%03d %s",st.wHour,st.wMinute,st.wSecond,
st.wMilliseconds,*strTemp);
pListBox->AddString(szBuf);
UpdateData(TRUE);
delete strTemp;
strTemp=NULL;
return 0;
}
void CMFCHTTPSERVERDlg::OnBnClickedStartStop()
{
// TODO: 在此添加控件通知处理程序代码
CWnd* pWndButton = GetDlgItem(IDOK);
if(!m_bStart)
{
UpdateData(TRUE);
pHttpProtocol=new CHttpProtocol;
pHttpProtocol->m_strRootDir=m_strRootDir;
pHttpProtocol->m_nPort=m_nPort;
pHttpProtocol->m_hwndDlg=m_hWnd;
if(pHttpProtocol->StartHttpSrv())
{
pWndButton->SetWindowTextA("停止服务");
m_bStart=true;
}
else
{
if(pHttpProtocol)
{
delete pHttpProtocol;
pHttpProtocol=NULL;
}
}
}
else
{
pHttpProtocol->StopHttpSrv();
pWndButton->SetWindowTextA("启动服务");
if(pHttpProtocol)
{
delete pHttpProtocol;
pHttpProtocol=NULL;
}
m_bStart=false;
}
//CDialog::OnOK();
}
void CMFCHTTPSERVERDlg::OnBnClickedCancel()
{
// TODO: 在此添加控件通知处理程序代码
if(m_bStart)
{
pHttpProtocol->StopHttpSrv();
}
if(pHttpProtocol)
{
delete pHttpProtocol;
pHttpProtocol=NULL;
}
m_bStart=false;
CDialog::OnCancel();
}
HTTP请求消息的一般格式:HTTP请求的头部细分为请求行和标题字段区域。头部请求行是HTTP请求报文的第一行,交代请求使用的方法,请求的目标,和HTTP的版本号。例:GET /index.html HTTP/1.1;HTTP请求的第二行起是头部的标题字段区域,可以安排一行或多行消息标题,常用字段有Accept 表示客户端期望接收的媒体类型,Host 表明请求资源所在的主机地址,Referer 用于记录客户端获得URL资源的地址,User-Agent
使用的用户代理,Connection 对TCP连接的处理方式。
典型的HTTP请求消息:
GET /path/file.html HTTP/1.0
User-Agent:HTTPTool/1.0
Connection:Close
HTTP响应消息的一般格式:HTTP响应消息由两大部分组成,消息头和消息体,两者之间使用一个空白行分开。消息头又可细分为响应的状态行和响应的标题字段,消息体是响应的实体数据。HTTP响应消息的第一行为状态行,由HTTP版本号、响应码和响应描述符文本构成,中间用空格相连。例:HTTP/1.1 200 ok。HTTP状态码分为5种类型:1xx 信息响应,表示接收到请求并且继续处理,2xx 处理成功响应,表示动作被成功接收、理解和接收、3xx
重定向响应,为了完成指定的动作,必须接受进一步处理,4xx 客户端错误,请求包含语法错误或者请求无法实现,5xx 服务器错误,服务器不能正确执行一个正确的请求。最常用的状态码有:
200 OK:请求成功,并且被请求的资源将会在响应消息中返回。
301 Moved Permanently:客户请求的对象已永久性迁移,新的URL在Location头中给出,浏览器会自动访问新的URL。
302 Moved Temporarily:所请求的对象被暂时迁移
400 Bad Request:服务器无法理解客户端的请求
404 Not Found:服务器不存在所请求的文档。客户端在对该请求作出更改前不应再次向服务器重复发送该请求
500 Server Error:服务器异常,不能完成客户的请求。
505 HTTP Version Not Supported:服务器不支持所请求的HTTP协议版本
HTTP响应可用的标题字段分为响应标题和实体标题两类:
响应标题有:Location 表示请求被重定向的URL的实际位置,使用绝对URL,并在响应字段返回3xx的响应码
Server 用于告知客户端服务器端使用的www服务器软件名称及版本信息
WWW-Authenticate 告知客户端受限资源所在的区域和要求的认证方法,同时应返回401响应码
实体标题有:Allow 告知客户机端他请求的资源所允许的请求方法
Content-Type 用于描述HTTP响应消息中所含有的实体数据的类型
Content-Length 用于描述消息内任何类型的实体数据长度
2、程序主流程图:
ClientThread流程图:
3、具体代码注释:
HTTPProtocol.h文件:
#ifndef _HTTPPROTOCOL_H
#define _HTTPPRPTOCOL_H
#pragma once
#include <WinSock2.h>
#pragma comment( lib, "ws2_32.lib")
#define HTTPPORT 80
#define METHOD_GET 0
#define METHOD_POST 1
#define METHOD_HEAD 2
//响应码
#define HTTP_STATUS_OK "200 OK"
#define HTTP_STATUS_CREATED "201 Created"
#define HTTP_STATUS_ACCEPTED "202 Accepted"
#define HTTP_STATUS_NOCONTENT "204 No Content"
#define HTTP_STATUS_MOVEDPERM "301 Moved Permanently"
#define HTTP_STATUS_MOVEDTEMP "302 Moved Temporarily"
#define HTTP_STATUS_NOTMODIFIED "304 Not Modified"
#define HTTP_STATUS_BADREQUEST "400 Bad Request"
#define HTTP_STATUS_UNAUTHORIZED "401 Unauthorized"
#define HTTP_STATUS_FORBIDDEN "403 Forbidden"
#define HTTP_STATUS_NOTFOUND "404 File can not fonund!"
#define HTTP_STATUS_SERVERERROR "500 Internal Server Error"
#define HTTP_STATUS_NOTIMPLEMENTED "501 Not Implemented"
#define HTTP_STATUS_BADGATEWAY "502 Bad Gateway"
#define HTTP_STATUS_UNAVAILABLE "503 Service Unavailable"
typedef struct REQUEST
{
HANDLE hExit;
SOCKET Socket; //请求的socket
int nMethod; //请求的使用方法
DWORD dwRecv; //收到的字节数
DWORD dwSend; //发送的字节数
HANDLE hFile; //请求连接的文件
char szFileName[_MAX_PATH]; //文件的相对路径
char postfix[10]; //存储扩展名
char StatuCodeReason[100]; //响应码
bool permitted; //用户权限判断
char* authority; //用户提供的认证信息
char key[1024]; //正确认证信息
void* pHttpProtocol; //指向类CHttpProtocol的指针
}REQUEST,*PREQUEST;
#include <map>
#include <string>
using namespace std;
class CHttpProtocol
{
public:
HWND m_hwndDlg;
SOCKET m_listenSocket;
map<CString,char *> m_typeMap; //保存content-type和文件后缀的对应关系
CWinThread* m_pListenThread;
HANDLE m_hExit;
static HANDLE None; //标志是否有Client连接到服务器
static UINT ClientNum; //连接的Client数量
static CCriticalSection m_critSect; //互斥变量
CString m_strRootDir; //web的根目录
UINT m_nPort; //服务器的端口号
void CreateTypeMap(); //创建content-type和文件后缀map
void StopHttpSrv(); //关闭服务
bool StartHttpSrv(); //启动服务
static UINT ListenThread(LPVOID param); //监听线程
static UINT ClientThread(LPVOID param); //请求处理线程
bool RecvRequest(PREQUEST pReq, LPBYTE pBuf, DWORD dwBufSize);
int Analyze(PREQUEST pReq, LPBYTE pBuf);
void SendHeader(PREQUEST pReq);
int FileExist(PREQUEST pReq);
void SendFile(PREQUEST pReq);
bool SendBuffer(PREQUEST pReq, LPBYTE pBuf, DWORD dwBufSize);
bool GetContenType(PREQUEST pReq, LPSTR type);
CHttpProtocol(void);
~CHttpProtocol(void);
};
#endif _HTTPPROTOCOL_H
********************************************************************************************************************************
HTTPProtocol.cpp文件:
#include "stdafx.h"
#include "HttpProtocol.h"
#include "process.h"
#include "MFCHTTPSERVER.h"
#include "resource.h"
#include "MFCHTTPSERVERDlg.h"
//静态变量初始化
UINT CHttpProtocol::ClientNum = 0;
CCriticalSection CHttpProtocol::m_critSect; // 排斥区初始化
HANDLE CHttpProtocol::None = NULL;
CHttpProtocol::CHttpProtocol(void)
{
m_pListenThread=NULL;
m_hwndDlg=NULL;
}
CHttpProtocol::~CHttpProtocol(void)
{
}
void CHttpProtocol::CreateTypeMap()
{
// 初始化map
m_typeMap[".doc"] = "application/msword";
m_typeMap[".bin"] = "application/octet-stream";
m_typeMap[".dll"] = "application/octet-stream";
m_typeMap[".exe"] = "application/octet-stream";
m_typeMap[".pdf"] = "application/pdf";
m_typeMap[".ai"] = "application/postscript";
m_typeMap[".eps"] = "application/postscript";
m_typeMap[".ps"] = "application/postscript";
m_typeMap[".rtf"] = "application/rtf";
m_typeMap[".fdf"] = "application/vnd.fdf";
m_typeMap[".arj"] = "application/x-arj";
m_typeMap[".gz"] = "application/x-gzip";
m_typeMap[".class"] = "application/x-java-class";
m_typeMap[".js"] = "application/x-javascript";
m_typeMap[".lzh"] = "application/x-lzh";
m_typeMap[".lnk"] = "application/x-ms-shortcut";
m_typeMap[".tar"] = "application/x-tar";
m_typeMap[".hlp"] = "application/x-winhelp";
m_typeMap[".cert"] = "application/x-x509-ca-cert";
m_typeMap[".zip"] = "application/zip";
m_typeMap[".cab"] = "application/x-compressed";
m_typeMap[".arj"] = "application/x-compressed";
m_typeMap[".aif"] = "audio/aiff";
m_typeMap[".aifc"] = "audio/aiff";
m_typeMap[".aiff"] = "audio/aiff";
m_typeMap[".au"] = "audio/basic";
m_typeMap[".snd"] = "audio/basic";
m_typeMap[".mid"] = "audio/midi";
m_typeMap[".rmi"] = "audio/midi";
m_typeMap[".mp3"] = "audio/mpeg";
m_typeMap[".vox"] = "audio/voxware";
m_typeMap[".wav"] = "audio/wav";
m_typeMap[".ra"] = "audio/x-pn-realaudio";
m_typeMap[".ram"] = "audio/x-pn-realaudio";
m_typeMap[".bmp"] = "image/bmp";
m_typeMap[".gif"] = "image/gif";
m_typeMap[".jpeg"] = "image/jpeg";
m_typeMap[".jpg"] = "image/jpeg";
m_typeMap[".tif"] = "image/tiff";
m_typeMap[".tiff"] = "image/tiff";
m_typeMap[".xbm"] = "image/xbm";
m_typeMap[".wrl"] = "model/vrml";
m_typeMap[".htm"] = "text/html";
m_typeMap[".html"] = "text/html";
m_typeMap[".c"] = "text/plain";
m_typeMap[".cpp"] = "text/plain";
m_typeMap[".def"] = "text/plain";
m_typeMap[".h"] = "text/plain";
m_typeMap[".txt"] = "text/plain";
m_typeMap[".rtx"] = "text/richtext";
m_typeMap[".rtf"] = "text/richtext";
m_typeMap[".java"] = "text/x-java-source";
m_typeMap[".css"] = "text/css";
m_typeMap[".mpeg"] = "video/mpeg";
m_typeMap[".mpg"] = "video/mpeg";
m_typeMap[".mpe"] = "video/mpeg";
m_typeMap[".avi"] = "video/msvideo";
m_typeMap[".mov"] = "video/quicktime";
m_typeMap[".qt"] = "video/quicktime";
m_typeMap[".shtml"] = "wwwserver/html-ssi";
m_typeMap[".asa"] = "wwwserver/isapi";
m_typeMap[".asp"] = "wwwserver/isapi";
m_typeMap[".cfm"] = "wwwserver/isapi";
m_typeMap[".dbm"] = "wwwserver/isapi";
m_typeMap[".isa"] = "wwwserver/isapi";
m_typeMap[".plx"] = "wwwserver/isapi";
m_typeMap[".url"] = "wwwserver/isapi";
m_typeMap[".cgi"] = "wwwserver/isapi";
m_typeMap[".php"] = "wwwserver/isapi";
m_typeMap[".wcgi"] = "wwwserver/isapi";
}
bool CHttpProtocol::StartHttpSrv()
{
WORD wVersionRequested=WINSOCK_VERSION;
WSADATA wsaData;
int nRet;
//启动Winsock
nRet=WSAStartup(wVersionRequested,&wsaData);
if(nRet)
{
//错误处理
AfxMessageBox("Initialize WinSock Failed");
return false;
}
m_hExit=CreateEvent(NULL,TRUE,FALSE,NULL);
if(m_hExit==NULL)
{
return false;
}
//创建套接字
m_listenSocket=WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED);
if(m_listenSocket==INVALID_SOCKET)
{
//异常处理
return false;
}
SOCKADDR_IN sockAddr;
// LPSERVENT lpServEnt;
if(m_nPort!=0)
{
sockAddr.sin_port=htons(m_nPort);
}
else
{
sockAddr.sin_port=htons(HTTPPORT);//默认端口号
}
sockAddr.sin_family=AF_INET;
char localName[256]; //本地机器名
gethostname(localName,256); //获取本机名
HOSTENT * pHost; //指向主机信息的指针
pHost=gethostbyname(localName);
sockAddr.sin_addr=*(in_addr *)pHost->h_addr_list[0];//设定IP地址
// 初始化content-type和文件后缀对应关系的map
CreateTypeMap();
//套接字绑定
nRet=bind(m_listenSocket,(LPSOCKADDR)&sockAddr,sizeof(sockaddr));
if(nRet==SOCKET_ERROR)
{
//绑定发生错误
closesocket(m_listenSocket); //断开连接
return false;
}
//套接字监听
nRet=listen(m_listenSocket,SOMAXCONN);
if(nRet==SOCKET_ERROR)
{
closesocket(m_listenSocket); //断开连接
return false;
}
//创建listening进程,接受客户机连接请求
m_pListenThread=AfxBeginThread(ListenThread,this);
if(!m_pListenThread)
{
closesocket(m_listenSocket);
return false;
}
CString *pStr1=new CString;
pStr1->Format("%s",localName);
CString strIP;
strIP=inet_ntoa(*(struct in_addr*)*(pHost->h_addr_list));
*pStr1=*pStr1+"["+strIP+"]"+"Port";
CString strTemp;
strTemp.Format("%d",htons(sockAddr.sin_port));
*pStr1=*pStr1+strTemp;
SendMessage(m_hwndDlg,LOG_MSG,(UINT)pStr1,NULL);
return true;
}
UINT CHttpProtocol::ListenThread(LPVOID param)
{
CHttpProtocol *pHttpProtocol=(CHttpProtocol *)param;
SOCKET socketClient;
CWinThread* pClientThread;
SOCKADDR_IN SockAddr;
PREQUEST pReq;
int nLen;
//DWORD dwRet;
ClientNum=0;
HANDLE hNoClients;
hNoClients=CreateEvent(NULL,TRUE,TRUE,NULL);
while(1)
{
nLen=sizeof(SockAddr);
//套接字等待连接,返回对应已接受的客户机连接的套接字
socketClient=accept(pHttpProtocol->m_listenSocket,(LPSOCKADDR)&SockAddr,&nLen);
if(socketClient==INVALID_SOCKET)
{
continue;
}
//将客户端网络地址转换为用点分割的IP地址
CString *pStr=new CString;
pStr->Format("%s Connecting on socket:%d",inet_ntoa(SockAddr.sin_addr),socketClient);
SendMessage(pHttpProtocol->m_hwndDlg,LOG_MSG,(UINT)pStr,NULL);
pReq=new REQUEST;
if(pReq==NULL)
{
AfxMessageBox("No memory for request");
continue;
}
pReq->hExit=pHttpProtocol->m_hExit;
pReq->Socket=socketClient;
pReq->hFile=INVALID_HANDLE_VALUE;
pReq->dwRecv=0;
pReq->dwSend=0;
pReq->pHttpProtocol=pHttpProtocol;
//创建client进程,处理request
pClientThread=AfxBeginThread(ClientThread,pReq);
if(!pClientThread)
{
delete pReq;
}
}
//等待所有线程结束
WaitForSingleObject((HANDLE)pHttpProtocol->m_hExit,INFINITE);
//等待所有client进程结束
WaitForSingleObject(hNoClients,5000);
CloseHandle(None);
return 0;
}
UINT CHttpProtocol::ClientThread(LPVOID param)
{
int nRet;
BYTE buf[1024];
PREQUEST pReq=(PREQUEST)param;
CHttpProtocol *pHttpProtocol=(CHttpProtocol *)pReq->pHttpProtocol;
//进入临界区
m_critSect.Lock();
ClientNum++;
//离开临界区
m_critSect.Unlock();
//重置为无信号事件对象
ResetEvent(None);
if(!pHttpProtocol->RecvRequest(pReq,buf,sizeof(buf)))
{
closesocket(pReq->Socket);
delete pReq;
m_critSect.Lock();
if(ClientNum > 0)
{
ClientNum--;
}
// 离开排斥区
m_critSect.Unlock();
if(ClientNum < 1)
{
// 重置为有信号事件对象
SetEvent(None);
}
return 0;
}
nRet=pHttpProtocol->Analyze(pReq,buf);
if(nRet)
{
closesocket(pReq->Socket);
delete pReq;
m_critSect.Lock();
if(ClientNum > 0)
{
ClientNum--;
}
// 离开排斥区
m_critSect.Unlock();
if(ClientNum < 1)
{
// 重置为有信号事件对象
SetEvent(None);
}
return 0;
}
//生成并返回头部
pHttpProtocol->SendHeader(pReq);
if(pReq->nMethod==METHOD_GET)
{
pHttpProtocol->SendFile(pReq);
}
closesocket(pReq->Socket);
delete pReq;
//进入临界区
m_critSect.Lock();
if(ClientNum>0)
{
ClientNum--;
}
//离开临界区
m_critSect.Unlock();
if(ClientNum<1)
{
//重置为有信号事件对象
SetEvent(None);
}
return 0;
}
bool CHttpProtocol::RecvRequest(PREQUEST pReq, LPBYTE pBuf, DWORD dwBufSize)
{
WSABUF wsabuf; //发送/接收缓冲区结构
WSAOVERLAPPED over; //指向调用重叠操作时指定的WSAOVERLAPPED结构
DWORD dwRecv;
DWORD dwFlags;
DWORD dwRet;
HANDLE hEvents[2];
bool fPending;
int nRet;
memset(pBuf,0,dwBufSize); //初始化缓冲区
wsabuf.buf=(char *)pBuf;
wsabuf.len=dwBufSize;
over.hEvent=WSACreateEvent();
dwFlags=0;
fPending=FALSE;
//接收数据
nRet=WSARecv(pReq->Socket,&wsabuf,1,&dwRecv,&dwFlags,&over,NULL);
if(nRet!=0)
{
//错误代码WSA_IO_PENDING表示重叠操作成功启动
if(WSAGetLastError()!=WSA_IO_PENDING)
{
//重叠操作未能成功
CloseHandle(over.hEvent);
return false;
}
else
{
fPending=true;
}
}
if(fPending)
{
hEvents[0]=over.hEvent;
hEvents[1]=pReq->hExit;
dwRet=WaitForMultipleObjects(2,hEvents,FALSE,INFINITE);
if(dwRet!=0)
{
CloseHandle(over.hEvent);
return false;
}
//重叠操作未完成
if(!WSAGetOverlappedResult(pReq->Socket,&over,&dwRecv,FALSE,&dwFlags))
{
CloseHandle(over.hEvent);
return false;
}
}
CloseHandle(over.hEvent);
return true;
}
int CHttpProtocol::Analyze(PREQUEST pReq, LPBYTE pBuf)
{
//分析接收到的信息
char szSeps[]="\n";
char *cpToken;
//cpToken=strtok((char *)pBuf,szSeps);// 缓存中字符串以“\n”为分解符分解为一组标记串,返回分解后的第一组字符串
// 防止非法请求
if (strstr((const char *)pBuf, "..") != NULL)//找到第二个字符串在第一个字符串第一次出现的位置
{
strcpy(pReq->StatuCodeReason, HTTP_STATUS_BADREQUEST);
return 1;
}
// 判断ruquest的mothed
cpToken = strtok((char *)pBuf, " "); // 缓存中字符串以“\n”为分解符分解为一组标记串,返回分解后的第一组字符串
//AfxMessageBox(cpToken);
//if(!_stricmp(cpToken,"GET"))
if(strstr(cpToken,"GET"))
{
pReq->nMethod=METHOD_GET;
}
//else if (!_stricmp(cpToken, "HEAD")) // HEAD命令
else if(strstr(cpToken,"HEAD"))
{
pReq->nMethod = METHOD_HEAD;
}
else
{
strcpy(pReq->StatuCodeReason, HTTP_STATUS_NOTIMPLEMENTED);
return 1;
}
//获取Request-URL
cpToken=strtok(NULL," "); //返回第二组字符串,即资源的路径
// AfxMessageBox(cpToken);
if(cpToken==NULL)
{
strcpy(pReq->StatuCodeReason,HTTP_STATUS_BADREQUEST);
return 1;
}
strcpy(pReq->szFileName,m_strRootDir);
if(strlen(cpToken)>1)
{
strcat(pReq->szFileName,cpToken); //把文件名添加到结尾处形成路径
}
else
{
strcat(pReq->szFileName,"/index.html");
}
// AfxMessageBox(pReq->szFileName);
return 0;
}
// 发送头部
void CHttpProtocol::SendHeader(PREQUEST pReq)
{
int n = FileExist(pReq);
if(!n)
{
return;
}
char Header[2048]="";
DWORD length;
length=GetFileSize(pReq->hFile,NULL);
char ContenType[50]="";
GetContenType(pReq,(char *)ContenType);
sprintf((char *)Header,"HTTP/1.0 %s\r\nServer:%s\r\nContent-Type:%s\r\nContent-Length:%d\r\n",
HTTP_STATUS_OK,
"My Http Server",
ContenType,
length);
//发送头部
send(pReq->Socket,Header,strlen(Header),0);
}
void CHttpProtocol::SendFile(PREQUEST pReq)
{
int n=FileExist(pReq);
if(!n)
{
return;
}
CString *pStr=new CString;
*pStr=*pStr+&pReq->szFileName[strlen(m_strRootDir)];
SendMessage(m_hwndDlg,LOG_MSG,UINT(pStr),NULL);
static BYTE buf[2048];
DWORD dwRead;
BOOL fRet;
int flag=1;
//读写数据直到完成
while(1)
{
//从file中读入到buffer中
fRet=ReadFile(pReq->hFile,buf,sizeof(buf),&dwRead,NULL);
if(!fRet)
{
static char szMsg[512];
wsprintf(szMsg,"%s",HTTP_STATUS_SERVERERROR);
//向客户端发送出错信息
send(pReq->Socket,szMsg,strlen(szMsg),0);
break;
}
//完成
if(dwRead==0)
{
break;
}
//将buffer内容传送给client
if(!SendBuffer(pReq,buf,dwRead))
{
break;
}
}
//关闭文件
if(CloseHandle(pReq->hFile))
{
pReq->hFile=INVALID_HANDLE_VALUE;
}
else
{
CString *pStr=new CString;
*pStr="Error occurs when closing file";
SendMessage(m_hwndDlg,LOG_MSG,(UINT)pStr,NULL);
}
}
bool CHttpProtocol::SendBuffer(PREQUEST pReq,LPBYTE pBuf,DWORD dwBufSize)
{
//发送缓存中的内容
WSABUF wsabuf;
WSAOVERLAPPED over;
DWORD dwRecv;
DWORD dwFlags;
DWORD dwRet;
HANDLE hEvents[2];
BOOL fPending;
int nRet;
wsabuf.buf=(char *)pBuf;
wsabuf.len=dwBufSize;
over.hEvent=WSACreateEvent();
fPending=false;
//发送数据
nRet=WSASend(pReq->Socket,&wsabuf,1,&dwRecv,0,&over,NULL);
if(nRet!=0)
{
//错误处理
if(WSAGetLastError()==WSA_IO_PENDING)
{
fPending=true;
}
else
{
CString *pStr=new CString;
pStr->Format("WSASend() error:%d",WSAGetLastError());
SendMessage(m_hwndDlg,LOG_MSG,(UINT)pStr,NULL);
CloseHandle(over.hEvent);
return false;
}
}
if(fPending)
{
hEvents[0]=over.hEvent;
hEvents[1]=pReq->hExit;
dwRet=WaitForMultipleObjects(2,hEvents,FALSE,INFINITE);
if(dwRet!=0)
{
CloseHandle(over.hEvent);
return false;
}
//重叠操作未完成
if(!WSAGetOverlappedResult(pReq->Socket,&over,&dwRecv,FALSE,&dwFlags))
{
//错误处理
CString *pStr=new CString;
pStr->Format("WSAGetOverlappedResult() error:%d",WSAGetLastError());
SendMessage(m_hwndDlg,LOG_MSG,(UINT)pStr,NULL);
CloseHandle(over.hEvent);
return false;
}
}
CloseHandle(over.hEvent);
return true;
}
int CHttpProtocol::FileExist(PREQUEST pReq)
{
pReq->hFile = CreateFile(pReq->szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// int d=access(pReq->hFile,00);
// 如果文件不存在,则返回出错信息
AfxMessageBox(pReq->szFileName);
if (pReq->hFile == INVALID_HANDLE_VALUE)
{
strcpy(pReq->StatuCodeReason, HTTP_STATUS_NOTFOUND);
return 0;
}
else
{
return 1;
}
}
bool CHttpProtocol::GetContenType(PREQUEST pReq, LPSTR type)
{
// 取得文件的类型
CString cpToken;
cpToken = strstr(pReq->szFileName, ".");
strcpy(pReq->postfix, cpToken);
// 遍历搜索该文件类型对应的content-type
map<CString, char *>::iterator it = m_typeMap.find(pReq->postfix);
if(it != m_typeMap.end())
{
wsprintf(type,"%s",(*it).second);
}
return TRUE;
}
void CHttpProtocol::StopHttpSrv()
{
int nRet;
SetEvent(m_hExit);
nRet=closesocket(m_listenSocket);
nRet=WaitForSingleObject((HANDLE)m_pListenThread,10000);
if(nRet==WAIT_TIMEOUT)
{
CString *pStr=new CString;
*pStr="TIMEOUT waiting for ListenThread";
SendMessage(m_hwndDlg,LOG_MSG,(UINT)pStr,NULL);
}
CloseHandle(m_hExit);
CString *pStr1=new CString;
*pStr1="Server Stopped";
SendMessage(m_hwndDlg,LOG_MSG,(UINT)pStr1,NULL);
}
*********************************************************************************************************************
MFCHTTPSERVERDlg.h文件:
// MFCHTTPSERVERDlg.h : 头文件
//
#include "HttpProtocol.h"
#pragma once
#define LOG_MSG (WM_USER+100)
// CMFCHTTPSERVERDlg 对话框
class CMFCHTTPSERVERDlg : public CDialog
{
// 构造
public:
CMFCHTTPSERVERDlg(CWnd* pParent = NULL); // 标准构造函数
CHttpProtocol *pHttpProtocol;
bool m_bStart;
// 对话框数据
enum { IDD = IDD_MFCHTTPSERVER_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg LRESULT AddLog(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
public:
UINT m_nPort;
CString m_strRootDir;
afx_msg void OnBnClickedStartStop();
afx_msg void OnBnClickedCancel();
};
******************************************************************************************************************************
MFCHTTPSERVERDlg.cpp文件:
// MFCHTTPSERVERDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "MFCHTTPSERVER.h"
#include "MFCHTTPSERVERDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 对话框数据
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CMFCHTTPSERVERDlg 对话框
CMFCHTTPSERVERDlg::CMFCHTTPSERVERDlg(CWnd* pParent /*=NULL*/)
: CDialog(CMFCHTTPSERVERDlg::IDD, pParent)
, m_nPort(8000)
, m_strRootDir(_T(""))
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMFCHTTPSERVERDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Text(pDX, IDC_PORT, m_nPort);
DDX_Text(pDX, IDC_ROOTDIR, m_strRootDir);
}
BEGIN_MESSAGE_MAP(CMFCHTTPSERVERDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_MESSAGE(LOG_MSG, AddLog)
ON_BN_CLICKED(IDOK, &CMFCHTTPSERVERDlg::OnBnClickedStartStop)
ON_BN_CLICKED(IDCANCEL, &CMFCHTTPSERVERDlg::OnBnClickedCancel)
END_MESSAGE_MAP()
// CMFCHTTPSERVERDlg 消息处理程序
BOOL CMFCHTTPSERVERDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
m_bStart=false;
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CMFCHTTPSERVERDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CMFCHTTPSERVERDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CMFCHTTPSERVERDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
LRESULT CMFCHTTPSERVERDlg::AddLog(WPARAM wParam, LPARAM lParam)
{
char szBuf[284];
CString *strTemp=(CString *)wParam;
CListBox* pListBox;
pListBox=(CListBox*)GetDlgItem(IDC_LOG);
SYSTEMTIME st;
GetLocalTime(&st);
wsprintf(szBuf,"%02d:%02d:%02d.%03d %s",st.wHour,st.wMinute,st.wSecond,
st.wMilliseconds,*strTemp);
pListBox->AddString(szBuf);
UpdateData(TRUE);
delete strTemp;
strTemp=NULL;
return 0;
}
void CMFCHTTPSERVERDlg::OnBnClickedStartStop()
{
// TODO: 在此添加控件通知处理程序代码
CWnd* pWndButton = GetDlgItem(IDOK);
if(!m_bStart)
{
UpdateData(TRUE);
pHttpProtocol=new CHttpProtocol;
pHttpProtocol->m_strRootDir=m_strRootDir;
pHttpProtocol->m_nPort=m_nPort;
pHttpProtocol->m_hwndDlg=m_hWnd;
if(pHttpProtocol->StartHttpSrv())
{
pWndButton->SetWindowTextA("停止服务");
m_bStart=true;
}
else
{
if(pHttpProtocol)
{
delete pHttpProtocol;
pHttpProtocol=NULL;
}
}
}
else
{
pHttpProtocol->StopHttpSrv();
pWndButton->SetWindowTextA("启动服务");
if(pHttpProtocol)
{
delete pHttpProtocol;
pHttpProtocol=NULL;
}
m_bStart=false;
}
//CDialog::OnOK();
}
void CMFCHTTPSERVERDlg::OnBnClickedCancel()
{
// TODO: 在此添加控件通知处理程序代码
if(m_bStart)
{
pHttpProtocol->StopHttpSrv();
}
if(pHttpProtocol)
{
delete pHttpProtocol;
pHttpProtocol=NULL;
}
m_bStart=false;
CDialog::OnCancel();
}
相关文章推荐
- 【网页访问单向、双向验证均可以】https原理及tomcat配置https方法[生成CA根证书配置tomcat后,若要成功访问axis中的webservice,需要配置它对应的axis2.xml文件]
- CNN卷积神经网络推导和实现
- Cocos2d-x 3.2 项目源代码从Mac打包到安卓教程【转自:http://www.2cto.com/kf/201410/342649.html】
- Tomcat6 配置HTTPS SSL 发布Axis2 webservice(一)配置【windows中操作,linux中类似此操作】
- C#:this索引器《转载http://www.cnblogs.com/ArmyShen/archive/2012/08/27/2659405.html》
- http://blog.sina.com.cn/s/blog_4aae007d0100inxi.html
- Servlet之Http协议【入门版,初学者必看】
- 常见的HTTP状态码汇总
- Ubuntu 14.04.2 系统无线网络不稳定问题
- 常见HTTP状态(304,200等)
- POJ1087(网络流,二分图匹配)
- 解决Android Studio Import Sample网络连接失败问题
- HTTP 方法:GET 对比 POST
- http各种状态码详解
- 计算机网络读书笔记-----应用层
- 计算机网络读书笔记-----UDP vs TCP
- java网络编程(4)——udp实现聊天
- 计算机网络读书笔记-----传输层
- TCP通信丢包原因总结
- HttpClient详解(三)—get post封装实例