1. 程式人生 > >c++實現簡單http伺服器

c++實現簡單http伺服器

http基於tcp協議的應用層協議,說白了就是寫死的自定義協議,程式碼實現了簡單的get請求,開啟服務後,可以通過網站訪問本地資源,適合新手學習的簡單程式碼,有助於理解get和http報文,很簡單

HttpService.h
#ifndef HTTP_SERVICE
#define HTTP_SERVICE


#include <stdio.h>
#include <winsock2.h>
#include <iostream>
 
#pragma comment(lib, "ws2_32.lib")  /* WinSock使用的庫函式 */
 
 
/* 定義常量 */
#define HTTP_PORT 6080 /* 連線的預設埠 */
#define HTTP_BUF_SIZE 1024 /* 緩衝區的大小 */
#define HTTP_FILENAME_LEN 999 /* 檔名長度 */
#defineHTTP_FILEEXTENSION_LEN 20//檔案字尾長度
#defineHTTP_CMD_LEN 20//命令長度


/* 定義檔案型別對應的 Content-Type */
struct FileExtension
{
    char * pExtensive; /* 檔案字尾 */
    char * pType;/* Content-Type */
};


 


class HttpService
{
public:
char cRecvBuffer[HTTP_BUF_SIZE];//快取請求命令
protected:
char cRecvFile[HTTP_FILENAME_LEN];//請求檔案
char cExtensive[HTTP_FILEEXTENSION_LEN];//檔案字尾
char cHttpCmd[HTTP_CMD_LEN];

public:
HttpService();
~HttpService();


public:
void AnalyRecvBuffer();
void HttpResponse(SOCKET sSocket);
protected:
void Cmd_Get(SOCKET sSocket);
void Cmd_Post(SOCKET sSocket);
char *AnalyExtensive();
};










#endif;


httpService類實現

HttpService.cpp
#include "HttpService.h"


char * pHttpHead = "HTTP/1.1 200 OK\r\nDate:%s\r\nServer: ZhaoYueYou's Server \r\n"
    "Accept-Ranges: bytes\r\nContent-Length: %d\r\nConnection: close\r\n"
    "Content-Type: %s\r\n\r\n";
struct FileExtension FileType[] = 
	{
		{"html",    "text/html"  },
		{"gif",     "image/gif"  },
		{"jpeg",    "image/jpeg" },
		{"jpg",     "image/jpeg" },
		{"png",		"image/png"},
		{ "js",		"text/javascript"},
		{"css",		"text/css"},
		{"txt",		"text/plain"},
		{"mp4",		"video/mp4"},
		{ NULL,      NULL        }
	};



HttpService::HttpService()
{
	memset(cRecvBuffer,0,HTTP_BUF_SIZE);
	memset(cRecvFile,0,HTTP_FILENAME_LEN);
	memset(cExtensive,0,HTTP_FILEEXTENSION_LEN);
	memset(cHttpCmd,0,HTTP_CMD_LEN); 
}

HttpService::~HttpService()
{

}

void HttpService::AnalyRecvBuffer()
{
	int nLength = 0;
    char *pBegin;
	char *pEnd;
	char *pFile;
	int	nHeadLen = 0;

 
    /* 查詢 URL 的開始位置 */
    pBegin = strchr(cRecvBuffer, ' ');

	nHeadLen =  pBegin-cRecvBuffer;
	if(nHeadLen<HTTP_CMD_LEN && nHeadLen>0)
		  memcpy(cHttpCmd, cRecvBuffer, nHeadLen);
	else
		std::cout<<"命令頭長度異常 "<<std::endl;

    pBegin += 1;
         
    /* 查詢 URL 的結束位置 */
    pEnd = strchr(pBegin, ' ');
    *pEnd = 0;
 
    pFile = strchr(pBegin, '/');
    nLength = pEnd - pFile;


 
    /* 找到檔名的開始位置 */
    if ((*pFile == '/') || (*pFile == '\\'))
    {
        pFile++;
        nLength--;
    }
    /* 得到檔名 */
    if (nLength > 0 && nLength <HTTP_FILENAME_LEN )
    {
        memcpy(cRecvFile, pFile, nLength);
        cRecvFile[nLength] = 0;
 
        pBegin = strchr(cRecvFile, '.');
		int nExtensiveLen = pEnd-pBegin;
        if (pBegin && nExtensiveLen<HTTP_FILEEXTENSION_LEN)
            strcpy(cExtensive, pBegin + 1);
		else
			std::cout<<"字尾長度異常 "<<std::endl;
    }
	else
		std::cout<<"檔案長度異常 "<<std::endl;
}

void HttpService::HttpResponse(SOCKET sSocket)
{
	if(!strcmp(cHttpCmd,"GET"))
		Cmd_Get(sSocket);
	else if(!strcmp(cHttpCmd,"POST"))
		Cmd_Post(sSocket);
	else
	{
		std::cout<<"其他命令 "<<cHttpCmd<<std::endl;
	}

}
void HttpService::Cmd_Get(SOCKET sSocket)
{
	int nHttpHeadLen =0;
	int	nFileLen = 0;
	int nSendLen = 0;
	int nRendLen = 0;
	char cReadBuf[HTTP_FILENAME_LEN] = {0};


	FILE *pFile;
	char *pFileType;
    pFile = fopen(cRecvFile, "rb+"); /* 用二進位制格式開啟檔案 */
    if (pFile == NULL)
    {
        printf("[Web] The file [%s] is not existed\n", cRecvFile);
        return ;
    }
	fseek(pFile, 0, SEEK_END);
    nFileLen = ftell(pFile);
    fseek(pFile, 0, SEEK_SET);

	pFileType = AnalyExtensive();
	if (pFileType == NULL)
    {
        printf("[Web] There is not the related content type\n");
        return ;
    }

	SYSTEMTIME sys;   
	GetLocalTime( &sys );   
	char ptr[40]={0};
	sprintf( ptr,"%4d/%02d/%02d %02d:%02d:%02d.%03d ",sys.wYear,sys.wMonth,sys.wDay,sys.wHour,sys.wMinute, sys.wSecond,sys.wMilliseconds,sys.wDayOfWeek);   



	 /* 構造 HTTP 首部,併發送 */
	char HttpHeader[HTTP_FILENAME_LEN]={0};
    nHttpHeadLen = sprintf(HttpHeader, pHttpHead,ptr, nFileLen, pFileType);
    nSendLen = send(sSocket, HttpHeader, nHttpHeadLen, 0);
	std::cout<<HttpHeader<<std::endl;
    //send_len=1;
    if (nSendLen == SOCKET_ERROR)
    {
        fclose(pFile);
        printf("[Web] Fail to send, error = %d\n", WSAGetLastError());
        return ;
    }
 
    do /* 傳送檔案, HTTP 的訊息體 */
    {
        nRendLen = fread(cReadBuf, sizeof(char), HTTP_FILENAME_LEN, pFile);
 
        if (nRendLen > 0)
        {
            nSendLen = send(sSocket, cReadBuf, nRendLen, 0);
            nFileLen -= nRendLen;
        }
    } while ((nRendLen > 0) && (nFileLen > 0));
 
    fclose(pFile);
}
void HttpService::Cmd_Post(SOCKET sSocket)
{
}
char *HttpService::AnalyExtensive()
{
   struct FileExtension *pType;
 
   for (pType = FileType; pType->pExtensive; pType++)
    {
        if (strcmp(pType->pExtensive, cExtensive) == 0)
			return pType->pType;
    }
 
    return NULL;
}

入口 main
#include "HttpService.h"


void main()
{
	WSADATA wsa_data;
    SOCKET  srv_soc = 0, acpt_soc;  /* socket 控制代碼 */
    struct sockaddr_in serv_addr;   /* 伺服器地址  */
    struct sockaddr_in from_addr;   /* 客戶端地址  */
    unsigned short port = HTTP_PORT;
    int from_len = sizeof(from_addr);
	int result = 0,recv_len = 0;

    WSAStartup(MAKEWORD(2,0), &wsa_data); /* 初始化 WinSock 資源 */
     
    srv_soc = socket(AF_INET, SOCK_STREAM, 0); /* 建立 socket */
    if (srv_soc == INVALID_SOCKET)
    {
        printf("[Web] socket() Fails, error = %d\n", WSAGetLastError());
		system("pause");
        return ; 
    }
     
    /* 伺服器地址 */
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	//inet_pton(AF_INET,"180.173.168.61",&serv_addr.sin_addr.s_addr);
 
    result = bind(srv_soc, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
    if (result == SOCKET_ERROR) /* 繫結失敗 */
    {
        closesocket(srv_soc);
		perror("bind:");
        printf("[Web] Fail to bind, error = %d\n", WSAGetLastError());

		system("pause");
        return ; 
    }
 
    result = listen(srv_soc, SOMAXCONN);
    printf("[Web] The server is running ... ...\n");


	 
    while (1)
    {
        acpt_soc = accept(srv_soc, (struct sockaddr *) &from_addr, &from_len);
        if (acpt_soc == INVALID_SOCKET) /* 接受失敗 */
        {
            printf("[Web] Fail to accept, error = %d\n", WSAGetLastError());
			system("pause");
            break; 
        }
       
        printf("[Web] Accepted address:[%s], port:[%d]\n", 
            inet_ntoa(from_addr.sin_addr), ntohs(from_addr.sin_port));
 
		HttpService hService;
		recv_len = recv(acpt_soc, hService.cRecvBuffer, HTTP_BUF_SIZE, 0);
        if (recv_len == SOCKET_ERROR) /* 接收失敗 */
        {
            closesocket(acpt_soc);
            printf("[Web] Fail to recv, error = %d\n", WSAGetLastError());
			system("pause");
            break; 
        }
 
        hService.cRecvBuffer[recv_len] = 0;
		hService.AnalyRecvBuffer();
		hService.HttpResponse(acpt_soc);
		closesocket(acpt_soc);

	}
	closesocket(srv_soc);
    WSACleanup();
    printf("[Web] The server is stopped.\n");
 
    return ;
 
}