批处理之家's Archiver

happy886rr 发表于 2017-8-25 21:20

跨平台安全服务器 msp

[i=s] 本帖最后由 happy886rr 于 2017-8-25 22:51 编辑 [/i]

msp安全服务器重构的内核架构,几乎实现了完整的http各类返回值200、404、403、301、304、500、100,包括对多种http请求头If-Modified-Since", "Cache-Control", "Accept-Language", "Connection", "Keep-Alive", "Close", "Timeout的参数支持。增加安全模式、日志打印模式。增加抗DDOS攻击,IP黑名单等智能拉黑技术。可设置IP网段限制、各类参数都已开放修改。性能是misv的10倍,可承受15000并发,单日800万次访问量的繁重任务,性能略强于apache,不及nginx水平。但已经达到商用级别。

msp意在打造安全牢固的web服务器,这是最难被黑客攻破的服务器。对路径渗透,文件渗透都坚决拒之门外。除非你在服务器上操作,否则任何远程操作都无法攻破改服务器的安全壁垒。任何恶意请求,恶意DDOS,瞬间封杀IP,为每个访问的IP建立点击档案,如果被系统认定为黑客行为、或恶意频繁请求,自动永久封禁IP。

源码发行,跨平台代码,各平台均可编译。推荐使用gcc编译,windows下可用vs编译。
默认限制IP网段 192.168.0.1~8.可自行修改ip限制范围。[code]/*
WEB SERVER&INTERPRETER, MSP @COPYRIGHT@2017~2019 BY HAPPY, VERSION 1.0
FRI AUG 22 2017 21:10:16 GMT+0800
**********************************************************************
gcc msp.c -lWs2_32  -o msp.exe       REM For Windows
gcc msp.c -lpthread -o msp           REM For Linux
gcc msp.c -o msp                     REM For Android
cl  msp.cpp /MD /Ox /out:msp.exe     REM For VS
**********************************************************************
*/
// 服务器模式配置
#define USE_SAFE_MODE           1
#define USE_LOGS_MODE           1

// 服务器参数设置
#define HTTP_SERVER_NAME       "Msp"
#define HTTP_SERVER_PORT        80
#define HTTP_SERVER_ROOT       "www"
#define HTTP_SERVER_PAGE       "index.html"

// 服务器性能配置
#define MAX_BACKLOG_SIZE        1024*4
#define MAX_THREAD_NUMBER       512
#define KEEP_ALIVE_TIMEOUT      3
#define MIN_HTTP_RPS            1

// 服务器建档配置
#define IP_RPS_LEN              1024
#define MAX_REQUESTS_PER_SECOND 128
#define MIN_REQUESTS_PER_SECOND 8

// 服务器静态标量
#define MAX_BUFF_SIZE           1024*4
#define MIN_BUFF_SIZE           1024
#define MAX_PATH_SIZE           1024
#define HTTP_GENERAL_ERROR      1
#define FILE_SEND_ERROR         1

// 服务器网段屏蔽(IP黑名单)
static const char* HTTP_SHILEDING_IPRange[][2] =
{
    {"192.168.0.5", ""},
    {"192.168.0.1", "192.168.0.8"}
};

#if defined(_MSC_VER)
#pragma comment(lib,"Ws2_32.lib")
#else
#include <stdbool.h>
#endif

#if defined(_MSC_VER)
// 定义VS控制台启动方式(对VS编译器,改后缀为.cpp编译时,实现无窗化启动)
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
#include <direct.h>
#include <Winsock2.h>
#include <sys\stat.h>
#define HTTP_SERVER_PATHCHARACTER "\\"
#else
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#define HTTP_SERVER_PATHCHARACTER "/"
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
typedef int SOCKET;
typedef unsigned long DWORD;
typedef void *LPVOID;
typedef const struct sockaddr *LPSOCKADDR;
#endif

#ifndef NULL
#define NULL (void*)0
#endif

#ifndef byte
typedef unsigned char byte;
#endif

// 跨平台宏函数
#if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
#if defined(_MSC_VER)
#define        S_ISDIR(m)        (((m) & S_IFMT) == S_IFDIR)
#define        S_ISREG(m)        (((m) & S_IFMT) == S_IFREG)
#endif
#define SLEEP(x) Sleep(x)
#define CLOSESOCKET closesocket
#else
#define SLEEP(x) usleep(x*1000)
#define CLOSESOCKET close
#endif

// 判断小写字母宏函数
#define ISLOWERLETTER(x) ('a'<=(x)&&(x)<='z')

// 反转UINT宏函数
#define REVUINT(x) (unsigned int)(((x)&0x000000FF)<<24|((x)&0x0000FF00)<<8|((x)&0x00FF0000)>>8|((x)&0xFF000000)>>24)

// HTTP请求头关键词
static const char* HEADER_KEY_WORDS[] = {"GET", "POST", "HEAD", "EDIT", "If-Modified-Since", "Cache-Control", "Accept-Language", "Connection", "Keep-Alive", "Close", "Timeout", NULL};

#if defined(USE_SAFE_MODE)
// 在开启安全模式下,服务器允许的文件后缀
static const char* HTTP_SAFE_SUFFIX[] = {".html", ".htm", ".shtml", ".css", ".js", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".bmp", ".swf", ".mp3", ".wma", ".wav", ".mp4", ".txt", ".xml", ".pdf", ".doc", ".xls", ".ppt", NULL};
static const int   HTTP_SAFE_CTYPEV[] = {      0,      0,        0,      1,     2,      3,      4,       4,      5,      6,      7,     8,       9,     10,     11,     12,     13,     14,     15,     16,     17,     18,     19,    0};
static const char* HTTP_CONTENT_TYPE[] = {"text/html", "text/css", "application/javascript", "image/png", "image/jpeg", "image/gif", "image/svg+xml", "image/x-icon", "application/x-bmp", "application/x-shockwave-flash", "audio/mp3", "audio/x-ms-wma", "audio/wav", "video/mpeg4", "text/plain", "text/xml", "application/pdf", "application/msword", "application/x-xls", "application/x-ppt", NULL};
// 独立IP 建档数组
static unsigned int IPRequestsPerSecond[IP_RPS_LEN][2];
// IPulong辅助数组
static unsigned long HTTP_SHILEDING_IPulongRange[sizeof(HTTP_SHILEDING_IPRange) / sizeof(HTTP_SHILEDING_IPRange[0])][2];
#define SHILEDING_IPRANGE_LEN (sizeof(HTTP_SHILEDING_IPRange)/sizeof(HTTP_SHILEDING_IPRange[0]))
#endif

// 时间戳全局变量
static char nowTime[MIN_BUFF_SIZE];
static const char* timeFormatGMT = "%a, %d %b %Y %H:%M:%S GMT";

// 当前线程数全局变量
static unsigned int nowThreadNumber = 0;

// 定义通用返回页面
static const char responseErrorPage[] =
    "<html>"
    "<head>"
    "</head>"
    "<title>%d %s</title>"
    "<body>"
    "  <h1 align='center'><font color=#FF1493>Sorry,%d Error!</font></h1>"
    "    <script>"
    "      document.write('<hr/>' + '<font color=#9400D3>' + new Date() + '</font>' + '<hr/>' + '<font color=#FF1493>' + 'The %s server responded \"%s\".' + '</font>');"
    "  </script>"
    "</body>"
    "</html>";

// 通用关键词识别函数
int HTTP_IdentifyKey(char* inStr, char** inKeyWords, const char* endChars, int SN)
{
    if (inStr == NULL)
    {
        return -1;
    }

    while(inKeyWords[SN] != NULL)
    {
        char *op=inStr, *kp=inKeyWords[SN];

        while(*kp != '\0')
        {
            if(
                ((ISLOWERLETTER(*op))?(*op-32):(*op)) != ((ISLOWERLETTER(*kp))?(*kp-32):(*kp))
            )
            {
                break;
            }
            op++;
            kp++;
        }

        if(*kp == '\0')
        {
            if(*op == '\0')
            {
                return SN;
            }
            while(*endChars != '\0')
            {
                if(*op == *(endChars++))
                {
                    return SN;
                }
            }
        }

        SN ++;
    }
    return -1;
}

// 错误打印函数
int HTTP_PrintError(char* perr)
{
#if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
    fprintf(stderr, "%s: %d\n", perr, WSAGetLastError());
#else
    perror(perr);
#endif
    return 0;
}

// HTTP通用错误报头模板
int HTTP_ResponseGeneral(SOCKET soc, int responseNumber, const char* responseText, int httpMethodValue)
{
    char responseBuf[MIN_BUFF_SIZE];
    sprintf
    (
        responseBuf
        ,
        "HTTP/1.1 %d %s\r\n"
        "Date: %s\r\n"
        "Server: %s\r\n\r\n"
        ,
        responseNumber, responseText, nowTime, HTTP_SERVER_NAME
    );
    send(soc, responseBuf, strlen(responseBuf), 0);

    if (httpMethodValue == 0)
    {
        sprintf
        (
            responseBuf
            ,
            responseErrorPage
            ,
            responseNumber, responseText, responseNumber, HTTP_SERVER_NAME, responseText
        );
        send(soc, responseBuf, strlen(responseBuf), 0);

    }
    return 0;
}

// 发送200 成功报头
int HTTP_Response200(SOCKET soc, int contentLength, int contentTypeValue, bool connectionFlag, const char* serverModifiDateStr)
{
    char responseBuf[MIN_BUFF_SIZE];
    sprintf
    (
        responseBuf
        ,
        "HTTP/1.1 200 OK\r\n"
        "Date: %s\r\n"
        "Server: %s\r\n"
        "Content-Length: %d\r\n"
        "Connection: %s\r\n"
        "Last-Modified: %s\r\n"
#if defined(USE_SAFE_MODE)
        "Content-Type: %s\r\n\r\n"
        ,
        nowTime, HTTP_SERVER_NAME, contentLength, (connectionFlag)?"Keep-Alive":"Close", serverModifiDateStr, HTTP_CONTENT_TYPE[HTTP_SAFE_CTYPEV[contentTypeValue]]
#else
        "Content-Type: text/html\r\n\r\n"
        ,
        nowTime, HTTP_SERVER_NAME, contentLength, (connectionFlag)?"Keep-Alive":"Close", serverModifiDateStr
#endif
    );

    send(soc, responseBuf, strlen(responseBuf), 0);
    return 0;
}

// 发送301 重定向报头
int HTTP_Response301(SOCKET soc, const char* newURL, const char* suffixStr, bool connectionFlag)
{
    char responseBuf[MIN_BUFF_SIZE];

    sprintf
    (
        responseBuf
        ,
        "HTTP/1.1 301 Moved Permanently\r\n"
        "Date: %s\r\n"
        "Server: %s\r\n"
        "Location: %s%s\r\n"
        "Connection: %s\r\n"
        "Content-Type: text/html\r\n\r\n"
        ,
        nowTime, HTTP_SERVER_NAME, newURL, suffixStr, (connectionFlag)?"Keep-Alive":"Close"
    );

    send(soc, responseBuf, strlen(responseBuf), 0);
    return 0;
}

// 发送304 未修改报头
int HTTP_Response304(SOCKET soc, const char* serverModifiDateStr, bool connectionFlag)
{
    char responseBuf[MIN_BUFF_SIZE];

    sprintf
    (
        responseBuf
        ,
        "HTTP/1.1 304 Not Modified\r\n"
        "Date: %s\r\n"
        "Server: %s\r\n"
        "Connection: %s\r\n"
        "Last-Modified: %s\r\n\r\n"
        ,
        nowTime, HTTP_SERVER_NAME,  (connectionFlag)?"Keep-Alive":"Close", serverModifiDateStr
    );

    send(soc, responseBuf, strlen(responseBuf), 0);
    return 0;
}

// 发送请求的资源
int HTTP_SendFile(SOCKET soc, FILE *fp)
{
    // 重置文件流位置
    rewind(fp);

    // 分配数据拾取器
    byte pickUpBuf[MIN_BUFF_SIZE];

    size_t freadSize = 0;

    // 发送文件二进制数据
    while (!feof(fp))
    {
        freadSize = fread(pickUpBuf, sizeof(byte), MIN_BUFF_SIZE, fp);
        if (send(soc, (const char*)pickUpBuf, freadSize, 0) == SOCKET_ERROR)
        {
            return FILE_SEND_ERROR;
        }
    }

    return 0;
}

// 服务器核心函数
DWORD
#if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
WINAPI
#endif
HTTP_ServerCore(LPVOID lpvsoc)
{
    // 线程计数器
    nowThreadNumber ++;

    DWORD ret = 0;
    SOCKET soc = (SOCKET)lpvsoc;

    // 是否启用Keep-Alive模式
    bool connectionFlag = true, timeoutAlready = false;

    // 分配接受缓存容器
    char receiveBuf[MIN_BUFF_SIZE];

        // 设置Keep-Alive 超时时间
        struct timeval timeoutKeepAlive = {KEEP_ALIVE_TIMEOUT, 0};

    // 设置soc超时时间
    setsockopt(soc, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeoutKeepAlive, sizeof(struct timeval));

    while(true)
    {
        // 接受客户端请求
        int receiveCount = recv(soc, receiveBuf, MIN_BUFF_SIZE-1, 0);
        if (receiveCount == SOCKET_ERROR)
        {
            // 发送100报头,客户必须继续发出请求
            HTTP_ResponseGeneral(soc, 100, "Needs Request", 2);
            ret = HTTP_GENERAL_ERROR;
            break;
        }
        else
        {
            // 置结束符 '\0'
            receiveBuf[receiveCount]='\0';
        }

        // 要提取的参数
        int  httpMethodValue = -1;
        char httpURL[MIN_BUFF_SIZE];
        char clientModifiDateStr[MIN_BUFF_SIZE];

        // 字符数组置零
        httpURL[0]='\0', clientModifiDateStr[0]='\0';

        // 解析 HTTP请求头
        int headerKeyBeginIndex = 0;
        char cacheLine[MIN_BUFF_SIZE], *pBuf = receiveBuf, *tPer = NULL;

        while((*pBuf != '\0') && (*pBuf != '\r') && (*pBuf != '\n'))
        {
            char *pLine=(char*)cacheLine;
            while((*pBuf != '\0') && (*pBuf != '\r') && (*pBuf != '\n'))
            {
                *(pLine ++) = *(pBuf ++);
            }
            // 置结束符
            *pLine = '\0';

            // 过滤尾部"\r\n"
            if (*pBuf=='\r')
            {
                pBuf ++;
            }
            if (*pBuf=='\n')
            {
                pBuf ++;
            }

            int headerKeyValue = HTTP_IdentifyKey(cacheLine, (char**)HEADER_KEY_WORDS, ": \t", headerKeyBeginIndex);
            if (headerKeyValue == -1 && headerKeyBeginIndex == 0)
            {
                // 发送400报头,请求格式错误
                HTTP_ResponseGeneral(soc, 400, "Bad request", 2);

                // 释放 套接口,结束连接
                CLOSESOCKET(soc);
                nowThreadNumber --;
                return HTTP_GENERAL_ERROR;
            }
            else if (headerKeyValue < 3 && headerKeyBeginIndex == 0)
            {
                // 获取HTTP协议值
                httpMethodValue = headerKeyValue;

                // 识别到请求协议,则后续比对关键词从第5个关键词开始,
                headerKeyBeginIndex = 4;

                // 分离请求的URL
                strtok(cacheLine, " \t");
                tPer = strtok(NULL, " \t");
                if(tPer != NULL)
                {
                    strcpy(httpURL, tPer);
                }
                else
                {
                    // 发送400报头,请求格式错误
                    HTTP_ResponseGeneral(soc, 400, "Bad request", 2);

                    // 释放套接口,结束连接
                    CLOSESOCKET(soc);
                    nowThreadNumber --;
                    return HTTP_GENERAL_ERROR;
                }
            }
            else if (headerKeyValue == 4 && headerKeyBeginIndex != 0)
            {
                tPer = cacheLine;
                if(*tPer != '\0')
                {
                    while(*tPer!=':' && *tPer!='\0')
                    {
                        tPer ++;
                    }
                    if(*tPer==':')
                    {
                        tPer ++;
                    }
                    while(*tPer==' ' && *tPer!='\t')
                    {
                        tPer ++;
                    }
                    strcpy(clientModifiDateStr, tPer);
                }
            }
            else if (headerKeyValue == 7 && headerKeyBeginIndex != 0)
            {
                tPer = cacheLine;
                if(*tPer != '\0')
                {
                    while(*tPer!=':' && *tPer!='\0')
                    {
                        tPer ++;
                    }
                    if(*tPer==':')
                    {
                        tPer ++;
                    }
                    while(*tPer==' ' && *tPer!='\t')
                    {
                        tPer ++;
                    }

                    // 验证Connection: 后边跟的是不是Close
                    if (HTTP_IdentifyKey(tPer, (char**)HEADER_KEY_WORDS, " \t,\0", 9) == 9)
                    {
                        connectionFlag = false;
                    }
                }
            }
            else if (!timeoutAlready && connectionFlag && headerKeyValue == 8 && headerKeyBeginIndex != 0)
            {
                tPer = cacheLine;
                if(*tPer != '\0')
                {
                    while(*tPer!=':' && *tPer!='\0')
                    {
                        tPer ++;
                    }
                    if(*tPer==':')
                    {
                        tPer ++;
                    }
                    while(*tPer==' ' && *tPer!='\t')
                    {
                        tPer ++;
                    }

                    // 验证Keep-Alive,提取timeout值
                    if (HTTP_IdentifyKey(tPer, (char**)HEADER_KEY_WORDS, " \t=", 10) == 10)
                    {
                        while(*tPer!='=' && *tPer!='\0')
                        {
                            tPer ++;
                        }
                        if(*tPer == '=')
                        {
                            // 设置Keep-Alive超时时间,不得大于KEEP_ALIVE_TIMEOUT
                            timeoutKeepAlive.tv_sec = atoi(++ tPer) % KEEP_ALIVE_TIMEOUT;

                            // 设置soc超时时间
                            setsockopt(soc, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeoutKeepAlive, sizeof(struct timeval));
                           
                            //设置布尔标记
                            timeoutAlready = true;
                        }
                    }
                }
            }
        }

        // 置换指针
        char* httpFilePath = (char*)httpURL;
        char* p = httpFilePath;

#if defined(USE_SAFE_MODE) || defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
        while (*p != '\0')
        {
#if defined(USE_SAFE_MODE)
            if (*p == '.' && *(p+1) == '.')
            {
                // 发送403报头 服务器拒绝目录渗透
                HTTP_ResponseGeneral(soc, 403, "Forbidden Directory Penetration", httpMethodValue);

                // 释放 套接口,结束连接
                CLOSESOCKET(soc);
                nowThreadNumber --;
                return HTTP_GENERAL_ERROR;
            }
#endif

#if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
            if (*p == '/')
            {
                *p = (HTTP_SERVER_PATHCHARACTER)[0];
            }
#endif
            p++;
        }
#endif

        // 拼接 请求文件路径
        char requestFilePath[MAX_PATH_SIZE];
        sprintf
        (
            requestFilePath
            ,
            "%s%s%s%s%s"
            ,
            ".", HTTP_SERVER_PATHCHARACTER, HTTP_SERVER_ROOT, HTTP_SERVER_PATHCHARACTER, httpFilePath
        );

        // 对目录请求,则在路径之后追加默认主页(index.html)
#if defined(USE_SAFE_MODE) || defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
        if(*(p-1) == (HTTP_SERVER_PATHCHARACTER)[0])
        {
#else
        if(*(requestFilePath + strlen(requestFilePath) -1) == (HTTP_SERVER_PATHCHARACTER)[0])
        {
#endif
            strcat(requestFilePath, HTTP_SERVER_PAGE);
        }

        // 获取URI在服务器上的对应文件信息
        struct stat fileStatBuf;
        int rStat = stat(requestFilePath, &fileStatBuf);

        // 如果请求的是目录
        if (rStat == 0 && S_ISDIR(fileStatBuf.st_mode))
        {
            // 页面重定向到目录默认主页
            HTTP_Response301(soc, (const char*)httpURL, "/", connectionFlag);

            // 如果是Connection: close模式
            if (connectionFlag == false)
            {
                break;
            }
            continue;
        }

        // 声明 文件类型值变量
        int contentTypeValue = 0;

#if defined(USE_SAFE_MODE)
        // 获取文件后缀
        char* pSuffix = strrchr(requestFilePath, '.');

        // 根据文件后缀 获取文件类型值
        contentTypeValue = HTTP_IdentifyKey(pSuffix, (char**)HTTP_SAFE_SUFFIX, "\0", 0);

        // 过滤文件类型,对非授权的文件格式禁止发送,以保证服务器资料安全
        if (contentTypeValue == -1)
        {
            // 发送403报头 服务器拒绝请求该文件
            HTTP_ResponseGeneral(soc, 403, "Forbidden", httpMethodValue);

            // 结束连接
            ret = HTTP_GENERAL_ERROR;
            break;
        }
#endif

        // 客户端缓存方案
        char* serverModifiDateStr = nowTime;

        // 如果请求的是文件
        if (rStat == 0 && S_ISREG(fileStatBuf.st_mode))
        {
            char tmpModifiDate[MIN_BUFF_SIZE];
            strftime(tmpModifiDate, sizeof(tmpModifiDate), timeFormatGMT, gmtime(&fileStatBuf.st_mtime));
            serverModifiDateStr = (char*)tmpModifiDate;

            // 比对客户端时间戳 与 服务器最后修改的时间戳
            if(strcmp(clientModifiDateStr, serverModifiDateStr) == 0)
            {
                // 如果时间戳一致,返回304未修改报头
                HTTP_Response304(soc, clientModifiDateStr, connectionFlag);

                // 如果是Connection: close模式
                if (connectionFlag == false)
                {
                    break;
                }
                continue;
            }
        }

        // 以二进制方式 打开服务器上被请求的文件
        FILE* fp = fopen(requestFilePath, "rb");

        // 如果 读取文件失败
        if (fp == NULL)
        {
            // 发送404报头 找不到文件
            HTTP_ResponseGeneral(soc, 404, "Not Found", httpMethodValue);

            // 执行跳转,关闭连接
            ret = HTTP_GENERAL_ERROR;
            break;
        }

        // 提取要请求的文件长度
        int fpSize = fileStatBuf.st_size;

        // 发送200报头
        HTTP_Response200(soc, fpSize, contentTypeValue, connectionFlag, serverModifiDateStr);

        // 如果是GET方法则发送请求的资源
        if (httpMethodValue == 0)
        {
            if (HTTP_SendFile(soc, fp) == FILE_SEND_ERROR)
            {
                // 发送500报头,服务器内部错误
                // HTTP_ResponseGeneral(soc, 500, "Server Read Error", httpMethodValue);
            }
        }
        // 释放文件流
        fclose(fp);

// 针对安卓非阻塞套接字,直接断开连接
#if defined(ANDROID) || defined(_ANDROID) || defined(__ANDROID__)
        break;
#endif
    }

    // 释放 套接口
    CLOSESOCKET(soc);
    nowThreadNumber --;
    return ret;
}

#if defined(USE_SAFE_MODE)
// IPulong 初始化函数
int initHTTP_SHILEDING_IPulongRange()
{
    // 转屏蔽网段为 ulong
    struct in_addr tmpIPaddr;
    unsigned int ulongTmp;

    int i;
    for(i=0; i<SHILEDING_IPRANGE_LEN; i++)
    {
        if(HTTP_SHILEDING_IPRange[i][0] == NULL)
        {
            return HTTP_GENERAL_ERROR;
        }

        tmpIPaddr.s_addr = inet_addr(HTTP_SHILEDING_IPRange[i][0]);
        if (tmpIPaddr.s_addr == INADDR_NONE)
        {
            return HTTP_GENERAL_ERROR;
        }

        HTTP_SHILEDING_IPulongRange[i][0] = REVUINT(tmpIPaddr.s_addr);

        if(*(HTTP_SHILEDING_IPRange[i][1]) == '\0')
        {
            HTTP_SHILEDING_IPulongRange[i][1] = 0;
        }
        else
        {
            tmpIPaddr.s_addr = inet_addr(HTTP_SHILEDING_IPRange[i][1]);
            if
            (
                tmpIPaddr.s_addr == INADDR_NONE ||
                HTTP_SHILEDING_IPulongRange[i][0] > REVUINT(tmpIPaddr.s_addr)
            )
            {
                return HTTP_GENERAL_ERROR;
            }
            HTTP_SHILEDING_IPulongRange[i][1] = REVUINT(tmpIPaddr.s_addr);
        }
    }
    return 0;
}

// 检测IP是否包含在 IPulong数组值域内
bool isBelongHTTP_SHILEDING_IPulongRange(unsigned long clientIPulong)
{
    int i;
    for(i=0; i<SHILEDING_IPRANGE_LEN; i++)
    {
        if
        (
            (
                HTTP_SHILEDING_IPulongRange[i][1] == 0              &&
                clientIPulong == HTTP_SHILEDING_IPulongRange[i][0]
            ) ||
            (
                HTTP_SHILEDING_IPulongRange[i][0] <= clientIPulong  &&
                clientIPulong <= HTTP_SHILEDING_IPulongRange[i][1]
            )
        )
        {
            return true;
        }
    }
    return false;
}
#endif

// MAIN主函数
int main()
{
    // 主程序返回值
    int ret = 0;

    // 获取服务器 当地时间
    time_t timeServerStart = time((time_t*)0);
    strcpy(nowTime, ctime(&timeServerStart));

    // 显示 标头信息
    fprintf
    (
        stdout,
        "%s"
        "[===* Welcome to use %s *===]\n"
        "[>>>]\n"
        ,
        nowTime, HTTP_SERVER_NAME
    );

#if defined(USE_SAFE_MODE)
    // 初始化 IPulong数组
    if (initHTTP_SHILEDING_IPulongRange() == HTTP_GENERAL_ERROR)
    {
        fprintf(stdout, "Failed to init shileding IP\n");
        ret = HTTP_GENERAL_ERROR;
        return ret;
    }
#endif

#if defined(USE_LOGS_MODE)
    // 追加日志文件
    FILE* fp = fopen("." HTTP_SERVER_PATHCHARACTER "server" ".log", "a");
    if (fp == NULL)
    {
        fprintf(stdout, "Failed to create server log\n");
        ret = HTTP_GENERAL_ERROR;
        return ret;
    }
    unsigned long preClientSockS_addr = 0;
    time_t preClientSockVistTime = 0;
#endif

#if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
    // 启动 安全套接字
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
    {
        fprintf(stdout, "Failed to create win socket\n");
        ret = HTTP_GENERAL_ERROR;
        goto MAIN_END3;
    }
#endif

    // 创建 监听套接口
    SOCKET socListen;
    socListen = socket(AF_INET, SOCK_STREAM, 0);
    if(socListen == INVALID_SOCKET)
    {
        HTTP_PrintError("Creat listen socket failed");
        ret = HTTP_GENERAL_ERROR;
        goto MAIN_END2;
    }

    // 声明 服务器地址,客户端地址
    struct sockaddr_in serverSockaddrIn, clientSockaddrIn;
    int clientSockaddrInLen = sizeof(clientSockaddrIn);

    // 配置 服务器信息
    serverSockaddrIn.sin_family      = AF_INET;
    serverSockaddrIn.sin_port        = htons(HTTP_SERVER_PORT);
    serverSockaddrIn.sin_addr.s_addr = htonl(INADDR_ANY);

    // 绑定 监听套接口 与 服务器地址
    if (bind(socListen, (LPSOCKADDR)&serverSockaddrIn, sizeof(serverSockaddrIn)) == SOCKET_ERROR)
    {
        HTTP_PrintError("Blind server failed");
        ret = HTTP_GENERAL_ERROR;
        goto MAIN_END1;
    }

    // 通过 监听套接口监听
    if (listen(socListen, MAX_BACKLOG_SIZE) == SOCKET_ERROR)
    {
        HTTP_PrintError("Listen socket failed");
        ret = HTTP_GENERAL_ERROR;
        goto MAIN_END1;
    }

#if defined(USE_SAFE_MODE) || defined(ANDROID) || defined(_ANDROID) || defined(__ANDROID__)
    // 声明 计时器变量
    clock_t preClock = clock();
    int countTimes = 0;
    int requestsPerSecond = 0;
#endif

#if defined(USE_LOGS_MODE)
    // 打印启动日志
    fprintf(fp, "[*]Server start at %s", nowTime);
    fflush(fp);
#endif

    // 监听 网页资源请求
    while(true)
    {

#if defined(USE_SAFE_MODE)
        // 智能休眠,均衡线程
        while(nowThreadNumber > MAX_THREAD_NUMBER)
        {
            SLEEP(1);
        }
#endif

        // 建立客户连接套接口
        SOCKET soc = accept(socListen, (struct sockaddr*)&clientSockaddrIn, &clientSockaddrInLen);
        if (soc == INVALID_SOCKET)
        {
            HTTP_PrintError("Accept client failed");
            // 防止恶意发包占用CPU
            SLEEP(1);
            continue;
        }

        // 更新时间戳
        time_t timeNow = time((time_t*)0);
        strftime(nowTime, sizeof(nowTime), timeFormatGMT, gmtime(&timeNow));

#if defined(USE_SAFE_MODE)
        // 为独立IP建档
        bool alreadyFindClientIP = false, allowAcceptSocket = true, isBetterMarkI = false;
        int i, markI=-1;
        for(i=IP_RPS_LEN-1; i; i--)
        {
            if
            (
                (markI == -1           && IPRequestsPerSecond[i][1] < MIN_REQUESTS_PER_SECOND) ||
                (isBetterMarkI = false && IPRequestsPerSecond[i][1] ==0)
            )
            {
                if (IPRequestsPerSecond[i][1] == 0)
                {
                    isBetterMarkI = true;
                }
                markI = i;
            }

            if (IPRequestsPerSecond[i][0] == clientSockaddrIn.sin_addr.s_addr)
            {
                alreadyFindClientIP = true;
                if (IPRequestsPerSecond[i][1] > MAX_REQUESTS_PER_SECOND)
                {
                    allowAcceptSocket = false;
                    if (IPRequestsPerSecond[i][1] != 0xFFFFFFFF)
                    {
                        IPRequestsPerSecond[i][1] = 0xFFFFFFFF;
#if defined(USE_LOGS_MODE)
                        // 打印黑名单日志
                        fprintf(fp, "[%s] [Blacklist user: %s]\n", nowTime, inet_ntoa(clientSockaddrIn.sin_addr));
                        fflush(fp);
#endif
                    }
                }
                else
                {
                    IPRequestsPerSecond[i][1] ++;
                }
                break;
            }
        }

        // 如果没有该IP记录,则可能覆盖之前的用户并为其建立档案
        if((! alreadyFindClientIP) && (markI != -1))
        {
            IPRequestsPerSecond[markI][0] = clientSockaddrIn.sin_addr.s_addr;
            IPRequestsPerSecond[markI][1] = 1;
        }

        // 判断该IP是否属于限制级IP
        if
        (
            isBelongHTTP_SHILEDING_IPulongRange(REVUINT(clientSockaddrIn.sin_addr.s_addr)) ||
            allowAcceptSocket == false
        )
        {
            // 对于限制级IP网段,或恶意DDOS客户端,直接断开连接,不予提供服务
            CLOSESOCKET(soc);
            continue;
        }
#endif

#if defined(USE_LOGS_MODE)
        if
        (
            preClientSockS_addr   != clientSockaddrIn.sin_addr.s_addr ||
            preClientSockVistTime != timeNow
        )
        {
            preClientSockS_addr    = clientSockaddrIn.sin_addr.s_addr;
            preClientSockVistTime  = timeNow;

            // 打印服务器日志
            fprintf(fp, "[%s] [Client: %s]\n", nowTime, inet_ntoa(clientSockaddrIn.sin_addr));
            fflush(fp);
        }
#endif

#if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
        // 创建请求线程
        DWORD threadID;
        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)HTTP_ServerCore, (LPVOID)soc, 0, &threadID);
#else
        pthread_t newPthreadT;
        pthread_create(&newPthreadT, NULL, (void*)HTTP_ServerCore, (void*)(intptr_t)soc);
#endif


#if defined(USE_SAFE_MODE) || defined(ANDROID) || defined(_ANDROID) || defined(__ANDROID__)
        // 计次器
        countTimes ++;

        // 每隔秒检测一次服务器冲击量
        if (clock()-preClock > CLOCKS_PER_SEC*0.618)
        {
            requestsPerSecond = CLOCKS_PER_SEC * countTimes / (clock()-preClock);
            countTimes = 0;
            preClock = clock();

#if defined(USE_SAFE_MODE)
            // 部分重置IP_RPS数组
            for(i=IP_RPS_LEN-1; i; i--)
            {
                if (IPRequestsPerSecond[i][1] <= MAX_REQUESTS_PER_SECOND)
                {
                    IPRequestsPerSecond[i][1] = 0;
                }
            }
#endif
        }

// 针对安卓非阻塞休眠优化
#if defined(ANDROID) || defined(_ANDROID) || defined(__ANDROID__)
        // 如果冲击量过小,则休眠CPU
        if (requestsPerSecond < MIN_HTTP_RPS)
        {
            SLEEP(3);
        }
#endif

#endif
    }

MAIN_END1:
    // 关闭监听套接口
    CLOSESOCKET(socListen);

MAIN_END2:
#if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
    // 关闭安全套接字
    WSACleanup();
#endif

MAIN_END3:
#if defined(USE_LOGS_MODE)
    // 关闭日志输出
    fclose(fp);
#endif

    return ret;
}
[/code]

Batcher 发表于 2017-8-26 13:09

[quote]性能是misv的10倍,可承受15000并发,单日800万次访问量的繁重任务,性能略强于apache,不及nginx水平。[/quote]
对这部分感兴趣,能否把你的测试环境、测试工具、测试数据详细介绍一下?

happy886rr 发表于 2017-8-26 18:38

[i=s] 本帖最后由 happy886rr 于 2017-8-26 18:53 编辑 [/i]

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=202476&ptid=45166]2#[/url] [i]Batcher[/i] [/b]

前辈,我在windows下做了测试,用ab软件跑了结果,msp服务器存放的是一个2M的婚纱网站。针对这个网站做1万5并发,10万次请求(仅一个请求失败,其余全部成功)。测试结果如下:[code]
测试环境: 赛扬g550 2.1GHz
Server Software:        Msp
Server Hostname:        192.168.1.4
Server Port:            80

Document Path:          /index.html
Document Length:        3227 bytes

Concurrency Level:      15000
Time taken for tests:   148.963 seconds
Complete requests:      100000
Failed requests:        1
   (Connect: 0, Receive: 0, Length: 1, Exceptions: 0)
Total transferred:      341299896 bytes
HTML transferred:       322699954 bytes
Requests per second:    671.31 [#/sec] (mean)
Time per request:       22344.410 [ms] (mean)
Time per request:       1.490 [ms] (mean, across all concurrent requests)
Transfer rate:          2237.48 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1  17.0      0     598
Processing:  3570 20891 3865.4  22222   23293
Waiting:     1175 10966 5642.1  11007   21571
Total:       3585 20892 3865.4  22223   23294

Percentage of the requests served within a certain time (ms)
  50%  22223
  66%  22315
  75%  22542
  80%  22584
  90%  22747
  95%  22804
  98%  22860
  99%  23078
100%  23294 (longest request)
[/code]备注:这是在关闭服务器安全防御下才能测试的。因为服务器代码默认会对每个访问IP建立档案,发现恶意刷流量、DDOS都将主动拉黑。在开启安全模式下,任何测试软件都会被瞬间识别出来,并禁封IP。
要是添加php的支持,解析速度还会更慢。单日1000万次的访问,完全没问题,另外手机上的性能也不错可以达到PC端性能的50%。
默认是阉割了post方法,任何上传都会被禁止,服务器设置的安全文件类型才能被访问。

Batcher 发表于 2017-8-26 23:42

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=202485&ptid=45166]3#[/url] [i]happy886rr[/i] [/b]


    相同环境下Apache和Nginx的测试数据有吗?

happy886rr 发表于 2017-8-27 11:26

[i=s] 本帖最后由 happy886rr 于 2017-8-27 11:31 编辑 [/i]

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=202493&ptid=45166]4#[/url] [i]Batcher[/i] [/b]

相同环境下Apache直接奔溃,因为windows下的apache根本承受不了1万5并发,这个已经远超apache的负载能力。nginx勉强能经得住1万5的并发,再多也会不干。而msp测试过2万并发,依然完成,因为msp不做任何限制,允许cpu接近100%工作,当然这只是低端双核处理器上的测试结果。
以下为nginx的测试结果:[code]Server Software:        nginx/1.13.4
Server Hostname:        192.168.1.4
Server Port:            80

Document Path:          /index.html
Document Length:        3152 bytes

Concurrency Level:      15000
Time taken for tests:   116.318 seconds
Complete requests:      100000
Failed requests:        0
Total transferred:      338600000 bytes
HTML transferred:       315200000 bytes
Requests per second:    859.71 [#/sec] (mean)
Time per request:       17447.737 [ms] (mean)
Time per request:       1.163 [ms] (mean, across all concurrent requests)
Transfer rate:          2842.75 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1  11.5      0     601
Processing:  3465 15650 3603.8  15525   25104
Waiting:     1123 8637 4837.5   8219   23743
Total:       3465 15651 3604.1  15525   25104

Percentage of the requests served within a certain time (ms)
  50%  15525
  66%  16639
  75%  16766
  80%  17133
  90%  19160
  95%  22655
  98%  24098
  99%  24652
100%  25104 (longest request)[/code]

Batcher 发表于 2017-8-27 12:54

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=202500&ptid=45166]5#[/url] [i]happy886rr[/i] [/b]


    确实,Windows里面Apache和Nginx的性能比Linux差很多。

xxbdh 发表于 2017-8-31 16:40

[i=s] 本帖最后由 xxbdh 于 2017-8-31 16:46 编辑 [/i]

简单测试了一下
URL不支持带空格或者中文等多字节字符
应该是需要增加UTF8的识别
另外日志里最好加上request与reponse的摘要信息

happy886rr 发表于 2017-9-1 00:50

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=202629&ptid=45166]7#[/url] [i]xxbdh[/i] [/b]
建议不错,下次更新时可以添加。

freesoft00 发表于 2017-11-6 13:33

[i=s] 本帖最后由 freesoft00 于 2017-11-6 21:10 编辑 [/i]

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=202641&ptid=45166]8#[/url] [i]happy886rr[/i] [/b]


这个是否可以再增加些功能。
例如,windows下面,运行可以生成二维码,手机扫描直接连接。
手机中可以创建无线热点,电脑连接热点,可以互传文件。
吾爱破解的几个网友做的:
[url]https://www.52pojie.cn/thread-624702-1-1.html[/url]
[url]https://www.52pojie.cn/thread-605649-1-1.html[/url]
[url]https://www.52pojie.cn/thread-598043-1-1.html[/url]
还有一些手机app也做的不错的。
AirMore
AirDroid

happy886rr 发表于 2017-11-6 20:56

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=204118&ptid=45166]9#[/url] [i]freesoft00[/i] [/b]
可以是可以,但是C语言太底层了,用C写比较累,需要很多代码才能实现。

freesoft00 发表于 2017-11-6 21:10

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=204126&ptid=45166]10#[/url] [i]happy886rr[/i] [/b]


安卓系统的还有一个Send Anywhere
我测试使用过的就这么几个。
主要就是局域网设备间互传文件。
多谢回复!
你看吧。看你的个人时间和精力。

gfwlxx 发表于 2019-2-9 08:12

可惜不支持asp, php
我有支持这些脚本的, 跨平台服务器代码.

页: [1]

Powered by Discuz! Archiver 7.2  © 2001-2009 Comsenz Inc.