Board logo

标题: 跨平台安全服务器 msp [打印本页]

作者: happy886rr    时间: 2017-8-25 21:20     标题: 跨平台安全服务器 msp

本帖最后由 happy886rr 于 2017-8-25 22:51 编辑

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限制范围。
  1. /*
  2. WEB SERVER&INTERPRETER, MSP @COPYRIGHT@2017~2019 BY HAPPY, VERSION 1.0
  3. FRI AUG 22 2017 21:10:16 GMT+0800
  4. **********************************************************************
  5. gcc msp.c -lWs2_32  -o msp.exe       REM For Windows
  6. gcc msp.c -lpthread -o msp           REM For Linux
  7. gcc msp.c -o msp                     REM For Android
  8. cl  msp.cpp /MD /Ox /out:msp.exe     REM For VS
  9. **********************************************************************
  10. */
  11. // 服务器模式配置
  12. #define USE_SAFE_MODE           1
  13. #define USE_LOGS_MODE           1
  14. // 服务器参数设置
  15. #define HTTP_SERVER_NAME       "Msp"
  16. #define HTTP_SERVER_PORT        80
  17. #define HTTP_SERVER_ROOT       "www"
  18. #define HTTP_SERVER_PAGE       "index.html"
  19. // 服务器性能配置
  20. #define MAX_BACKLOG_SIZE        1024*4
  21. #define MAX_THREAD_NUMBER       512
  22. #define KEEP_ALIVE_TIMEOUT      3
  23. #define MIN_HTTP_RPS            1
  24. // 服务器建档配置
  25. #define IP_RPS_LEN              1024
  26. #define MAX_REQUESTS_PER_SECOND 128
  27. #define MIN_REQUESTS_PER_SECOND 8
  28. // 服务器静态标量
  29. #define MAX_BUFF_SIZE           1024*4
  30. #define MIN_BUFF_SIZE           1024
  31. #define MAX_PATH_SIZE           1024
  32. #define HTTP_GENERAL_ERROR      1
  33. #define FILE_SEND_ERROR         1
  34. // 服务器网段屏蔽(IP黑名单)
  35. static const char* HTTP_SHILEDING_IPRange[][2] =
  36. {
  37.     {"192.168.0.5", ""},
  38.     {"192.168.0.1", "192.168.0.8"}
  39. };
  40. #if defined(_MSC_VER)
  41. #pragma comment(lib,"Ws2_32.lib")
  42. #else
  43. #include <stdbool.h>
  44. #endif
  45. #if defined(_MSC_VER)
  46. // 定义VS控制台启动方式(对VS编译器,改后缀为.cpp编译时,实现无窗化启动)
  47. #pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
  48. #endif
  49. #include <stdio.h>
  50. #include <stdlib.h>
  51. #include <string.h>
  52. #include <time.h>
  53. #if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  54. #include <direct.h>
  55. #include <Winsock2.h>
  56. #include <sys\stat.h>
  57. #define HTTP_SERVER_PATHCHARACTER "\\"
  58. #else
  59. #include <sys/socket.h>
  60. #include <sys/types.h>
  61. #include <sys/stat.h>
  62. #include <netinet/in.h>
  63. #include <arpa/inet.h>
  64. #include <pthread.h>
  65. #include <unistd.h>
  66. #define HTTP_SERVER_PATHCHARACTER "/"
  67. #define INVALID_SOCKET -1
  68. #define SOCKET_ERROR -1
  69. typedef int SOCKET;
  70. typedef unsigned long DWORD;
  71. typedef void *LPVOID;
  72. typedef const struct sockaddr *LPSOCKADDR;
  73. #endif
  74. #ifndef NULL
  75. #define NULL (void*)0
  76. #endif
  77. #ifndef byte
  78. typedef unsigned char byte;
  79. #endif
  80. // 跨平台宏函数
  81. #if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  82. #if defined(_MSC_VER)
  83. #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
  84. #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
  85. #endif
  86. #define SLEEP(x) Sleep(x)
  87. #define CLOSESOCKET closesocket
  88. #else
  89. #define SLEEP(x) usleep(x*1000)
  90. #define CLOSESOCKET close
  91. #endif
  92. // 判断小写字母宏函数
  93. #define ISLOWERLETTER(x) ('a'<=(x)&&(x)<='z')
  94. // 反转UINT宏函数
  95. #define REVUINT(x) (unsigned int)(((x)&0x000000FF)<<24|((x)&0x0000FF00)<<8|((x)&0x00FF0000)>>8|((x)&0xFF000000)>>24)
  96. // HTTP请求头关键词
  97. static const char* HEADER_KEY_WORDS[] = {"GET", "POST", "HEAD", "EDIT", "If-Modified-Since", "Cache-Control", "Accept-Language", "Connection", "Keep-Alive", "Close", "Timeout", NULL};
  98. #if defined(USE_SAFE_MODE)
  99. // 在开启安全模式下,服务器允许的文件后缀
  100. 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};
  101. 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};
  102. 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};
  103. // 独立IP 建档数组
  104. static unsigned int IPRequestsPerSecond[IP_RPS_LEN][2];
  105. // IPulong辅助数组
  106. static unsigned long HTTP_SHILEDING_IPulongRange[sizeof(HTTP_SHILEDING_IPRange) / sizeof(HTTP_SHILEDING_IPRange[0])][2];
  107. #define SHILEDING_IPRANGE_LEN (sizeof(HTTP_SHILEDING_IPRange)/sizeof(HTTP_SHILEDING_IPRange[0]))
  108. #endif
  109. // 时间戳全局变量
  110. static char nowTime[MIN_BUFF_SIZE];
  111. static const char* timeFormatGMT = "%a, %d %b %Y %H:%M:%S GMT";
  112. // 当前线程数全局变量
  113. static unsigned int nowThreadNumber = 0;
  114. // 定义通用返回页面
  115. static const char responseErrorPage[] =
  116.     "<html>"
  117.     "<head>"
  118.     "</head>"
  119.     "<title>%d %s</title>"
  120.     "<body>"
  121.     "  <h1 align='center'><font color=#FF1493>Sorry,%d Error!</font></h1>"
  122.     "    <script>"
  123.     "      document.write('<hr/>' + '<font color=#9400D3>' + new Date() + '</font>' + '<hr/>' + '<font color=#FF1493>' + 'The %s server responded \"%s\".' + '</font>');"
  124.     "  </script>"
  125.     "</body>"
  126.     "</html>";
  127. // 通用关键词识别函数
  128. int HTTP_IdentifyKey(char* inStr, char** inKeyWords, const char* endChars, int SN)
  129. {
  130.     if (inStr == NULL)
  131.     {
  132.         return -1;
  133.     }
  134.     while(inKeyWords[SN] != NULL)
  135.     {
  136.         char *op=inStr, *kp=inKeyWords[SN];
  137.         while(*kp != '\0')
  138.         {
  139.             if(
  140.                 ((ISLOWERLETTER(*op))?(*op-32):(*op)) != ((ISLOWERLETTER(*kp))?(*kp-32):(*kp))
  141.             )
  142.             {
  143.                 break;
  144.             }
  145.             op++;
  146.             kp++;
  147.         }
  148.         if(*kp == '\0')
  149.         {
  150.             if(*op == '\0')
  151.             {
  152.                 return SN;
  153.             }
  154.             while(*endChars != '\0')
  155.             {
  156.                 if(*op == *(endChars++))
  157.                 {
  158.                     return SN;
  159.                 }
  160.             }
  161.         }
  162.         SN ++;
  163.     }
  164.     return -1;
  165. }
  166. // 错误打印函数
  167. int HTTP_PrintError(char* perr)
  168. {
  169. #if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  170.     fprintf(stderr, "%s: %d\n", perr, WSAGetLastError());
  171. #else
  172.     perror(perr);
  173. #endif
  174.     return 0;
  175. }
  176. // HTTP通用错误报头模板
  177. int HTTP_ResponseGeneral(SOCKET soc, int responseNumber, const char* responseText, int httpMethodValue)
  178. {
  179.     char responseBuf[MIN_BUFF_SIZE];
  180.     sprintf
  181.     (
  182.         responseBuf
  183.         ,
  184.         "HTTP/1.1 %d %s\r\n"
  185.         "Date: %s\r\n"
  186.         "Server: %s\r\n\r\n"
  187.         ,
  188.         responseNumber, responseText, nowTime, HTTP_SERVER_NAME
  189.     );
  190.     send(soc, responseBuf, strlen(responseBuf), 0);
  191.     if (httpMethodValue == 0)
  192.     {
  193.         sprintf
  194.         (
  195.             responseBuf
  196.             ,
  197.             responseErrorPage
  198.             ,
  199.             responseNumber, responseText, responseNumber, HTTP_SERVER_NAME, responseText
  200.         );
  201.         send(soc, responseBuf, strlen(responseBuf), 0);
  202.     }
  203.     return 0;
  204. }
  205. // 发送200 成功报头
  206. int HTTP_Response200(SOCKET soc, int contentLength, int contentTypeValue, bool connectionFlag, const char* serverModifiDateStr)
  207. {
  208.     char responseBuf[MIN_BUFF_SIZE];
  209.     sprintf
  210.     (
  211.         responseBuf
  212.         ,
  213.         "HTTP/1.1 200 OK\r\n"
  214.         "Date: %s\r\n"
  215.         "Server: %s\r\n"
  216.         "Content-Length: %d\r\n"
  217.         "Connection: %s\r\n"
  218.         "Last-Modified: %s\r\n"
  219. #if defined(USE_SAFE_MODE)
  220.         "Content-Type: %s\r\n\r\n"
  221.         ,
  222.         nowTime, HTTP_SERVER_NAME, contentLength, (connectionFlag)?"Keep-Alive":"Close", serverModifiDateStr, HTTP_CONTENT_TYPE[HTTP_SAFE_CTYPEV[contentTypeValue]]
  223. #else
  224.         "Content-Type: text/html\r\n\r\n"
  225.         ,
  226.         nowTime, HTTP_SERVER_NAME, contentLength, (connectionFlag)?"Keep-Alive":"Close", serverModifiDateStr
  227. #endif
  228.     );
  229.     send(soc, responseBuf, strlen(responseBuf), 0);
  230.     return 0;
  231. }
  232. // 发送301 重定向报头
  233. int HTTP_Response301(SOCKET soc, const char* newURL, const char* suffixStr, bool connectionFlag)
  234. {
  235.     char responseBuf[MIN_BUFF_SIZE];
  236.     sprintf
  237.     (
  238.         responseBuf
  239.         ,
  240.         "HTTP/1.1 301 Moved Permanently\r\n"
  241.         "Date: %s\r\n"
  242.         "Server: %s\r\n"
  243.         "Location: %s%s\r\n"
  244.         "Connection: %s\r\n"
  245.         "Content-Type: text/html\r\n\r\n"
  246.         ,
  247.         nowTime, HTTP_SERVER_NAME, newURL, suffixStr, (connectionFlag)?"Keep-Alive":"Close"
  248.     );
  249.     send(soc, responseBuf, strlen(responseBuf), 0);
  250.     return 0;
  251. }
  252. // 发送304 未修改报头
  253. int HTTP_Response304(SOCKET soc, const char* serverModifiDateStr, bool connectionFlag)
  254. {
  255.     char responseBuf[MIN_BUFF_SIZE];
  256.     sprintf
  257.     (
  258.         responseBuf
  259.         ,
  260.         "HTTP/1.1 304 Not Modified\r\n"
  261.         "Date: %s\r\n"
  262.         "Server: %s\r\n"
  263.         "Connection: %s\r\n"
  264.         "Last-Modified: %s\r\n\r\n"
  265.         ,
  266.         nowTime, HTTP_SERVER_NAME,  (connectionFlag)?"Keep-Alive":"Close", serverModifiDateStr
  267.     );
  268.     send(soc, responseBuf, strlen(responseBuf), 0);
  269.     return 0;
  270. }
  271. // 发送请求的资源
  272. int HTTP_SendFile(SOCKET soc, FILE *fp)
  273. {
  274.     // 重置文件流位置
  275.     rewind(fp);
  276.     // 分配数据拾取器
  277.     byte pickUpBuf[MIN_BUFF_SIZE];
  278.     size_t freadSize = 0;
  279.     // 发送文件二进制数据
  280.     while (!feof(fp))
  281.     {
  282.         freadSize = fread(pickUpBuf, sizeof(byte), MIN_BUFF_SIZE, fp);
  283.         if (send(soc, (const char*)pickUpBuf, freadSize, 0) == SOCKET_ERROR)
  284.         {
  285.             return FILE_SEND_ERROR;
  286.         }
  287.     }
  288.     return 0;
  289. }
  290. // 服务器核心函数
  291. DWORD
  292. #if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  293. WINAPI
  294. #endif
  295. HTTP_ServerCore(LPVOID lpvsoc)
  296. {
  297.     // 线程计数器
  298.     nowThreadNumber ++;
  299.     DWORD ret = 0;
  300.     SOCKET soc = (SOCKET)lpvsoc;
  301.     // 是否启用Keep-Alive模式
  302.     bool connectionFlag = true, timeoutAlready = false;
  303.     // 分配接受缓存容器
  304.     char receiveBuf[MIN_BUFF_SIZE];
  305. // 设置Keep-Alive 超时时间
  306. struct timeval timeoutKeepAlive = {KEEP_ALIVE_TIMEOUT, 0};
  307.     // 设置soc超时时间
  308.     setsockopt(soc, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeoutKeepAlive, sizeof(struct timeval));
  309.     while(true)
  310.     {
  311.         // 接受客户端请求
  312.         int receiveCount = recv(soc, receiveBuf, MIN_BUFF_SIZE-1, 0);
  313.         if (receiveCount == SOCKET_ERROR)
  314.         {
  315.             // 发送100报头,客户必须继续发出请求
  316.             HTTP_ResponseGeneral(soc, 100, "Needs Request", 2);
  317.             ret = HTTP_GENERAL_ERROR;
  318.             break;
  319.         }
  320.         else
  321.         {
  322.             // 置结束符 '\0'
  323.             receiveBuf[receiveCount]='\0';
  324.         }
  325.         // 要提取的参数
  326.         int  httpMethodValue = -1;
  327.         char httpURL[MIN_BUFF_SIZE];
  328.         char clientModifiDateStr[MIN_BUFF_SIZE];
  329.         // 字符数组置零
  330.         httpURL[0]='\0', clientModifiDateStr[0]='\0';
  331.         // 解析 HTTP请求头
  332.         int headerKeyBeginIndex = 0;
  333.         char cacheLine[MIN_BUFF_SIZE], *pBuf = receiveBuf, *tPer = NULL;
  334.         while((*pBuf != '\0') && (*pBuf != '\r') && (*pBuf != '\n'))
  335.         {
  336.             char *pLine=(char*)cacheLine;
  337.             while((*pBuf != '\0') && (*pBuf != '\r') && (*pBuf != '\n'))
  338.             {
  339.                 *(pLine ++) = *(pBuf ++);
  340.             }
  341.             // 置结束符
  342.             *pLine = '\0';
  343.             // 过滤尾部"\r\n"
  344.             if (*pBuf=='\r')
  345.             {
  346.                 pBuf ++;
  347.             }
  348.             if (*pBuf=='\n')
  349.             {
  350.                 pBuf ++;
  351.             }
  352.             int headerKeyValue = HTTP_IdentifyKey(cacheLine, (char**)HEADER_KEY_WORDS, ": \t", headerKeyBeginIndex);
  353.             if (headerKeyValue == -1 && headerKeyBeginIndex == 0)
  354.             {
  355.                 // 发送400报头,请求格式错误
  356.                 HTTP_ResponseGeneral(soc, 400, "Bad request", 2);
  357.                 // 释放 套接口,结束连接
  358.                 CLOSESOCKET(soc);
  359.                 nowThreadNumber --;
  360.                 return HTTP_GENERAL_ERROR;
  361.             }
  362.             else if (headerKeyValue < 3 && headerKeyBeginIndex == 0)
  363.             {
  364.                 // 获取HTTP协议值
  365.                 httpMethodValue = headerKeyValue;
  366.                 // 识别到请求协议,则后续比对关键词从第5个关键词开始,
  367.                 headerKeyBeginIndex = 4;
  368.                 // 分离请求的URL
  369.                 strtok(cacheLine, " \t");
  370.                 tPer = strtok(NULL, " \t");
  371.                 if(tPer != NULL)
  372.                 {
  373.                     strcpy(httpURL, tPer);
  374.                 }
  375.                 else
  376.                 {
  377.                     // 发送400报头,请求格式错误
  378.                     HTTP_ResponseGeneral(soc, 400, "Bad request", 2);
  379.                     // 释放套接口,结束连接
  380.                     CLOSESOCKET(soc);
  381.                     nowThreadNumber --;
  382.                     return HTTP_GENERAL_ERROR;
  383.                 }
  384.             }
  385.             else if (headerKeyValue == 4 && headerKeyBeginIndex != 0)
  386.             {
  387.                 tPer = cacheLine;
  388.                 if(*tPer != '\0')
  389.                 {
  390.                     while(*tPer!=':' && *tPer!='\0')
  391.                     {
  392.                         tPer ++;
  393.                     }
  394.                     if(*tPer==':')
  395.                     {
  396.                         tPer ++;
  397.                     }
  398.                     while(*tPer==' ' && *tPer!='\t')
  399.                     {
  400.                         tPer ++;
  401.                     }
  402.                     strcpy(clientModifiDateStr, tPer);
  403.                 }
  404.             }
  405.             else if (headerKeyValue == 7 && headerKeyBeginIndex != 0)
  406.             {
  407.                 tPer = cacheLine;
  408.                 if(*tPer != '\0')
  409.                 {
  410.                     while(*tPer!=':' && *tPer!='\0')
  411.                     {
  412.                         tPer ++;
  413.                     }
  414.                     if(*tPer==':')
  415.                     {
  416.                         tPer ++;
  417.                     }
  418.                     while(*tPer==' ' && *tPer!='\t')
  419.                     {
  420.                         tPer ++;
  421.                     }
  422.                     // 验证Connection: 后边跟的是不是Close
  423.                     if (HTTP_IdentifyKey(tPer, (char**)HEADER_KEY_WORDS, " \t,\0", 9) == 9)
  424.                     {
  425.                         connectionFlag = false;
  426.                     }
  427.                 }
  428.             }
  429.             else if (!timeoutAlready && connectionFlag && headerKeyValue == 8 && headerKeyBeginIndex != 0)
  430.             {
  431.                 tPer = cacheLine;
  432.                 if(*tPer != '\0')
  433.                 {
  434.                     while(*tPer!=':' && *tPer!='\0')
  435.                     {
  436.                         tPer ++;
  437.                     }
  438.                     if(*tPer==':')
  439.                     {
  440.                         tPer ++;
  441.                     }
  442.                     while(*tPer==' ' && *tPer!='\t')
  443.                     {
  444.                         tPer ++;
  445.                     }
  446.                     // 验证Keep-Alive,提取timeout值
  447.                     if (HTTP_IdentifyKey(tPer, (char**)HEADER_KEY_WORDS, " \t=", 10) == 10)
  448.                     {
  449.                         while(*tPer!='=' && *tPer!='\0')
  450.                         {
  451.                             tPer ++;
  452.                         }
  453.                         if(*tPer == '=')
  454.                         {
  455.                             // 设置Keep-Alive超时时间,不得大于KEEP_ALIVE_TIMEOUT
  456.                             timeoutKeepAlive.tv_sec = atoi(++ tPer) % KEEP_ALIVE_TIMEOUT;
  457.                             // 设置soc超时时间
  458.                             setsockopt(soc, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeoutKeepAlive, sizeof(struct timeval));
  459.                            
  460.                             //设置布尔标记
  461.                             timeoutAlready = true;
  462.                         }
  463.                     }
  464.                 }
  465.             }
  466.         }
  467.         // 置换指针
  468.         char* httpFilePath = (char*)httpURL;
  469.         char* p = httpFilePath;
  470. #if defined(USE_SAFE_MODE) || defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  471.         while (*p != '\0')
  472.         {
  473. #if defined(USE_SAFE_MODE)
  474.             if (*p == '.' && *(p+1) == '.')
  475.             {
  476.                 // 发送403报头 服务器拒绝目录渗透
  477.                 HTTP_ResponseGeneral(soc, 403, "Forbidden Directory Penetration", httpMethodValue);
  478.                 // 释放 套接口,结束连接
  479.                 CLOSESOCKET(soc);
  480.                 nowThreadNumber --;
  481.                 return HTTP_GENERAL_ERROR;
  482.             }
  483. #endif
  484. #if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  485.             if (*p == '/')
  486.             {
  487.                 *p = (HTTP_SERVER_PATHCHARACTER)[0];
  488.             }
  489. #endif
  490.             p++;
  491.         }
  492. #endif
  493.         // 拼接 请求文件路径
  494.         char requestFilePath[MAX_PATH_SIZE];
  495.         sprintf
  496.         (
  497.             requestFilePath
  498.             ,
  499.             "%s%s%s%s%s"
  500.             ,
  501.             ".", HTTP_SERVER_PATHCHARACTER, HTTP_SERVER_ROOT, HTTP_SERVER_PATHCHARACTER, httpFilePath
  502.         );
  503.         // 对目录请求,则在路径之后追加默认主页(index.html)
  504. #if defined(USE_SAFE_MODE) || defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  505.         if(*(p-1) == (HTTP_SERVER_PATHCHARACTER)[0])
  506.         {
  507. #else
  508.         if(*(requestFilePath + strlen(requestFilePath) -1) == (HTTP_SERVER_PATHCHARACTER)[0])
  509.         {
  510. #endif
  511.             strcat(requestFilePath, HTTP_SERVER_PAGE);
  512.         }
  513.         // 获取URI在服务器上的对应文件信息
  514.         struct stat fileStatBuf;
  515.         int rStat = stat(requestFilePath, &fileStatBuf);
  516.         // 如果请求的是目录
  517.         if (rStat == 0 && S_ISDIR(fileStatBuf.st_mode))
  518.         {
  519.             // 页面重定向到目录默认主页
  520.             HTTP_Response301(soc, (const char*)httpURL, "/", connectionFlag);
  521.             // 如果是Connection: close模式
  522.             if (connectionFlag == false)
  523.             {
  524.                 break;
  525.             }
  526.             continue;
  527.         }
  528.         // 声明 文件类型值变量
  529.         int contentTypeValue = 0;
  530. #if defined(USE_SAFE_MODE)
  531.         // 获取文件后缀
  532.         char* pSuffix = strrchr(requestFilePath, '.');
  533.         // 根据文件后缀 获取文件类型值
  534.         contentTypeValue = HTTP_IdentifyKey(pSuffix, (char**)HTTP_SAFE_SUFFIX, "\0", 0);
  535.         // 过滤文件类型,对非授权的文件格式禁止发送,以保证服务器资料安全
  536.         if (contentTypeValue == -1)
  537.         {
  538.             // 发送403报头 服务器拒绝请求该文件
  539.             HTTP_ResponseGeneral(soc, 403, "Forbidden", httpMethodValue);
  540.             // 结束连接
  541.             ret = HTTP_GENERAL_ERROR;
  542.             break;
  543.         }
  544. #endif
  545.         // 客户端缓存方案
  546.         char* serverModifiDateStr = nowTime;
  547.         // 如果请求的是文件
  548.         if (rStat == 0 && S_ISREG(fileStatBuf.st_mode))
  549.         {
  550.             char tmpModifiDate[MIN_BUFF_SIZE];
  551.             strftime(tmpModifiDate, sizeof(tmpModifiDate), timeFormatGMT, gmtime(&fileStatBuf.st_mtime));
  552.             serverModifiDateStr = (char*)tmpModifiDate;
  553.             // 比对客户端时间戳 与 服务器最后修改的时间戳
  554.             if(strcmp(clientModifiDateStr, serverModifiDateStr) == 0)
  555.             {
  556.                 // 如果时间戳一致,返回304未修改报头
  557.                 HTTP_Response304(soc, clientModifiDateStr, connectionFlag);
  558.                 // 如果是Connection: close模式
  559.                 if (connectionFlag == false)
  560.                 {
  561.                     break;
  562.                 }
  563.                 continue;
  564.             }
  565.         }
  566.         // 以二进制方式 打开服务器上被请求的文件
  567.         FILE* fp = fopen(requestFilePath, "rb");
  568.         // 如果 读取文件失败
  569.         if (fp == NULL)
  570.         {
  571.             // 发送404报头 找不到文件
  572.             HTTP_ResponseGeneral(soc, 404, "Not Found", httpMethodValue);
  573.             // 执行跳转,关闭连接
  574.             ret = HTTP_GENERAL_ERROR;
  575.             break;
  576.         }
  577.         // 提取要请求的文件长度
  578.         int fpSize = fileStatBuf.st_size;
  579.         // 发送200报头
  580.         HTTP_Response200(soc, fpSize, contentTypeValue, connectionFlag, serverModifiDateStr);
  581.         // 如果是GET方法则发送请求的资源
  582.         if (httpMethodValue == 0)
  583.         {
  584.             if (HTTP_SendFile(soc, fp) == FILE_SEND_ERROR)
  585.             {
  586.                 // 发送500报头,服务器内部错误
  587.                 // HTTP_ResponseGeneral(soc, 500, "Server Read Error", httpMethodValue);
  588.             }
  589.         }
  590.         // 释放文件流
  591.         fclose(fp);
  592. // 针对安卓非阻塞套接字,直接断开连接
  593. #if defined(ANDROID) || defined(_ANDROID) || defined(__ANDROID__)
  594.         break;
  595. #endif
  596.     }
  597.     // 释放 套接口
  598.     CLOSESOCKET(soc);
  599.     nowThreadNumber --;
  600.     return ret;
  601. }
  602. #if defined(USE_SAFE_MODE)
  603. // IPulong 初始化函数
  604. int initHTTP_SHILEDING_IPulongRange()
  605. {
  606.     // 转屏蔽网段为 ulong
  607.     struct in_addr tmpIPaddr;
  608.     unsigned int ulongTmp;
  609.     int i;
  610.     for(i=0; i<SHILEDING_IPRANGE_LEN; i++)
  611.     {
  612.         if(HTTP_SHILEDING_IPRange[i][0] == NULL)
  613.         {
  614.             return HTTP_GENERAL_ERROR;
  615.         }
  616.         tmpIPaddr.s_addr = inet_addr(HTTP_SHILEDING_IPRange[i][0]);
  617.         if (tmpIPaddr.s_addr == INADDR_NONE)
  618.         {
  619.             return HTTP_GENERAL_ERROR;
  620.         }
  621.         HTTP_SHILEDING_IPulongRange[i][0] = REVUINT(tmpIPaddr.s_addr);
  622.         if(*(HTTP_SHILEDING_IPRange[i][1]) == '\0')
  623.         {
  624.             HTTP_SHILEDING_IPulongRange[i][1] = 0;
  625.         }
  626.         else
  627.         {
  628.             tmpIPaddr.s_addr = inet_addr(HTTP_SHILEDING_IPRange[i][1]);
  629.             if
  630.             (
  631.                 tmpIPaddr.s_addr == INADDR_NONE ||
  632.                 HTTP_SHILEDING_IPulongRange[i][0] > REVUINT(tmpIPaddr.s_addr)
  633.             )
  634.             {
  635.                 return HTTP_GENERAL_ERROR;
  636.             }
  637.             HTTP_SHILEDING_IPulongRange[i][1] = REVUINT(tmpIPaddr.s_addr);
  638.         }
  639.     }
  640.     return 0;
  641. }
  642. // 检测IP是否包含在 IPulong数组值域内
  643. bool isBelongHTTP_SHILEDING_IPulongRange(unsigned long clientIPulong)
  644. {
  645.     int i;
  646.     for(i=0; i<SHILEDING_IPRANGE_LEN; i++)
  647.     {
  648.         if
  649.         (
  650.             (
  651.                 HTTP_SHILEDING_IPulongRange[i][1] == 0              &&
  652.                 clientIPulong == HTTP_SHILEDING_IPulongRange[i][0]
  653.             ) ||
  654.             (
  655.                 HTTP_SHILEDING_IPulongRange[i][0] <= clientIPulong  &&
  656.                 clientIPulong <= HTTP_SHILEDING_IPulongRange[i][1]
  657.             )
  658.         )
  659.         {
  660.             return true;
  661.         }
  662.     }
  663.     return false;
  664. }
  665. #endif
  666. // MAIN主函数
  667. int main()
  668. {
  669.     // 主程序返回值
  670.     int ret = 0;
  671.     // 获取服务器 当地时间
  672.     time_t timeServerStart = time((time_t*)0);
  673.     strcpy(nowTime, ctime(&timeServerStart));
  674.     // 显示 标头信息
  675.     fprintf
  676.     (
  677.         stdout,
  678.         "%s"
  679.         "[===* Welcome to use %s *===]\n"
  680.         "[>>>]\n"
  681.         ,
  682.         nowTime, HTTP_SERVER_NAME
  683.     );
  684. #if defined(USE_SAFE_MODE)
  685.     // 初始化 IPulong数组
  686.     if (initHTTP_SHILEDING_IPulongRange() == HTTP_GENERAL_ERROR)
  687.     {
  688.         fprintf(stdout, "Failed to init shileding IP\n");
  689.         ret = HTTP_GENERAL_ERROR;
  690.         return ret;
  691.     }
  692. #endif
  693. #if defined(USE_LOGS_MODE)
  694.     // 追加日志文件
  695.     FILE* fp = fopen("." HTTP_SERVER_PATHCHARACTER "server" ".log", "a");
  696.     if (fp == NULL)
  697.     {
  698.         fprintf(stdout, "Failed to create server log\n");
  699.         ret = HTTP_GENERAL_ERROR;
  700.         return ret;
  701.     }
  702.     unsigned long preClientSockS_addr = 0;
  703.     time_t preClientSockVistTime = 0;
  704. #endif
  705. #if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  706.     // 启动 安全套接字
  707.     WSADATA wsaData;
  708.     if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
  709.     {
  710.         fprintf(stdout, "Failed to create win socket\n");
  711.         ret = HTTP_GENERAL_ERROR;
  712.         goto MAIN_END3;
  713.     }
  714. #endif
  715.     // 创建 监听套接口
  716.     SOCKET socListen;
  717.     socListen = socket(AF_INET, SOCK_STREAM, 0);
  718.     if(socListen == INVALID_SOCKET)
  719.     {
  720.         HTTP_PrintError("Creat listen socket failed");
  721.         ret = HTTP_GENERAL_ERROR;
  722.         goto MAIN_END2;
  723.     }
  724.     // 声明 服务器地址,客户端地址
  725.     struct sockaddr_in serverSockaddrIn, clientSockaddrIn;
  726.     int clientSockaddrInLen = sizeof(clientSockaddrIn);
  727.     // 配置 服务器信息
  728.     serverSockaddrIn.sin_family      = AF_INET;
  729.     serverSockaddrIn.sin_port        = htons(HTTP_SERVER_PORT);
  730.     serverSockaddrIn.sin_addr.s_addr = htonl(INADDR_ANY);
  731.     // 绑定 监听套接口 与 服务器地址
  732.     if (bind(socListen, (LPSOCKADDR)&serverSockaddrIn, sizeof(serverSockaddrIn)) == SOCKET_ERROR)
  733.     {
  734.         HTTP_PrintError("Blind server failed");
  735.         ret = HTTP_GENERAL_ERROR;
  736.         goto MAIN_END1;
  737.     }
  738.     // 通过 监听套接口监听
  739.     if (listen(socListen, MAX_BACKLOG_SIZE) == SOCKET_ERROR)
  740.     {
  741.         HTTP_PrintError("Listen socket failed");
  742.         ret = HTTP_GENERAL_ERROR;
  743.         goto MAIN_END1;
  744.     }
  745. #if defined(USE_SAFE_MODE) || defined(ANDROID) || defined(_ANDROID) || defined(__ANDROID__)
  746.     // 声明 计时器变量
  747.     clock_t preClock = clock();
  748.     int countTimes = 0;
  749.     int requestsPerSecond = 0;
  750. #endif
  751. #if defined(USE_LOGS_MODE)
  752.     // 打印启动日志
  753.     fprintf(fp, "[*]Server start at %s", nowTime);
  754.     fflush(fp);
  755. #endif
  756.     // 监听 网页资源请求
  757.     while(true)
  758.     {
  759. #if defined(USE_SAFE_MODE)
  760.         // 智能休眠,均衡线程
  761.         while(nowThreadNumber > MAX_THREAD_NUMBER)
  762.         {
  763.             SLEEP(1);
  764.         }
  765. #endif
  766.         // 建立客户连接套接口
  767.         SOCKET soc = accept(socListen, (struct sockaddr*)&clientSockaddrIn, &clientSockaddrInLen);
  768.         if (soc == INVALID_SOCKET)
  769.         {
  770.             HTTP_PrintError("Accept client failed");
  771.             // 防止恶意发包占用CPU
  772.             SLEEP(1);
  773.             continue;
  774.         }
  775.         // 更新时间戳
  776.         time_t timeNow = time((time_t*)0);
  777.         strftime(nowTime, sizeof(nowTime), timeFormatGMT, gmtime(&timeNow));
  778. #if defined(USE_SAFE_MODE)
  779.         // 为独立IP建档
  780.         bool alreadyFindClientIP = false, allowAcceptSocket = true, isBetterMarkI = false;
  781.         int i, markI=-1;
  782.         for(i=IP_RPS_LEN-1; i; i--)
  783.         {
  784.             if
  785.             (
  786.                 (markI == -1           && IPRequestsPerSecond[i][1] < MIN_REQUESTS_PER_SECOND) ||
  787.                 (isBetterMarkI = false && IPRequestsPerSecond[i][1] ==0)
  788.             )
  789.             {
  790.                 if (IPRequestsPerSecond[i][1] == 0)
  791.                 {
  792.                     isBetterMarkI = true;
  793.                 }
  794.                 markI = i;
  795.             }
  796.             if (IPRequestsPerSecond[i][0] == clientSockaddrIn.sin_addr.s_addr)
  797.             {
  798.                 alreadyFindClientIP = true;
  799.                 if (IPRequestsPerSecond[i][1] > MAX_REQUESTS_PER_SECOND)
  800.                 {
  801.                     allowAcceptSocket = false;
  802.                     if (IPRequestsPerSecond[i][1] != 0xFFFFFFFF)
  803.                     {
  804.                         IPRequestsPerSecond[i][1] = 0xFFFFFFFF;
  805. #if defined(USE_LOGS_MODE)
  806.                         // 打印黑名单日志
  807.                         fprintf(fp, "[%s] [Blacklist user: %s]\n", nowTime, inet_ntoa(clientSockaddrIn.sin_addr));
  808.                         fflush(fp);
  809. #endif
  810.                     }
  811.                 }
  812.                 else
  813.                 {
  814.                     IPRequestsPerSecond[i][1] ++;
  815.                 }
  816.                 break;
  817.             }
  818.         }
  819.         // 如果没有该IP记录,则可能覆盖之前的用户并为其建立档案
  820.         if((! alreadyFindClientIP) && (markI != -1))
  821.         {
  822.             IPRequestsPerSecond[markI][0] = clientSockaddrIn.sin_addr.s_addr;
  823.             IPRequestsPerSecond[markI][1] = 1;
  824.         }
  825.         // 判断该IP是否属于限制级IP
  826.         if
  827.         (
  828.             isBelongHTTP_SHILEDING_IPulongRange(REVUINT(clientSockaddrIn.sin_addr.s_addr)) ||
  829.             allowAcceptSocket == false
  830.         )
  831.         {
  832.             // 对于限制级IP网段,或恶意DDOS客户端,直接断开连接,不予提供服务
  833.             CLOSESOCKET(soc);
  834.             continue;
  835.         }
  836. #endif
  837. #if defined(USE_LOGS_MODE)
  838.         if
  839.         (
  840.             preClientSockS_addr   != clientSockaddrIn.sin_addr.s_addr ||
  841.             preClientSockVistTime != timeNow
  842.         )
  843.         {
  844.             preClientSockS_addr    = clientSockaddrIn.sin_addr.s_addr;
  845.             preClientSockVistTime  = timeNow;
  846.             // 打印服务器日志
  847.             fprintf(fp, "[%s] [Client: %s]\n", nowTime, inet_ntoa(clientSockaddrIn.sin_addr));
  848.             fflush(fp);
  849.         }
  850. #endif
  851. #if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  852.         // 创建请求线程
  853.         DWORD threadID;
  854.         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)HTTP_ServerCore, (LPVOID)soc, 0, &threadID);
  855. #else
  856.         pthread_t newPthreadT;
  857.         pthread_create(&newPthreadT, NULL, (void*)HTTP_ServerCore, (void*)(intptr_t)soc);
  858. #endif
  859. #if defined(USE_SAFE_MODE) || defined(ANDROID) || defined(_ANDROID) || defined(__ANDROID__)
  860.         // 计次器
  861.         countTimes ++;
  862.         // 每隔秒检测一次服务器冲击量
  863.         if (clock()-preClock > CLOCKS_PER_SEC*0.618)
  864.         {
  865.             requestsPerSecond = CLOCKS_PER_SEC * countTimes / (clock()-preClock);
  866.             countTimes = 0;
  867.             preClock = clock();
  868. #if defined(USE_SAFE_MODE)
  869.             // 部分重置IP_RPS数组
  870.             for(i=IP_RPS_LEN-1; i; i--)
  871.             {
  872.                 if (IPRequestsPerSecond[i][1] <= MAX_REQUESTS_PER_SECOND)
  873.                 {
  874.                     IPRequestsPerSecond[i][1] = 0;
  875.                 }
  876.             }
  877. #endif
  878.         }
  879. // 针对安卓非阻塞休眠优化
  880. #if defined(ANDROID) || defined(_ANDROID) || defined(__ANDROID__)
  881.         // 如果冲击量过小,则休眠CPU
  882.         if (requestsPerSecond < MIN_HTTP_RPS)
  883.         {
  884.             SLEEP(3);
  885.         }
  886. #endif
  887. #endif
  888.     }
  889. MAIN_END1:
  890.     // 关闭监听套接口
  891.     CLOSESOCKET(socListen);
  892. MAIN_END2:
  893. #if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  894.     // 关闭安全套接字
  895.     WSACleanup();
  896. #endif
  897. MAIN_END3:
  898. #if defined(USE_LOGS_MODE)
  899.     // 关闭日志输出
  900.     fclose(fp);
  901. #endif
  902.     return ret;
  903. }
复制代码

作者: Batcher    时间: 2017-8-26 13:09

性能是misv的10倍,可承受15000并发,单日800万次访问量的繁重任务,性能略强于apache,不及nginx水平。

对这部分感兴趣,能否把你的测试环境、测试工具、测试数据详细介绍一下?
作者: happy886rr    时间: 2017-8-26 18:38

本帖最后由 happy886rr 于 2017-8-26 18:53 编辑

回复 2# Batcher

前辈,我在windows下做了测试,用ab软件跑了结果,msp服务器存放的是一个2M的婚纱网站。针对这个网站做1万5并发,10万次请求(仅一个请求失败,其余全部成功)。测试结果如下:
  1. 测试环境: 赛扬g550 2.1GHz
  2. Server Software:        Msp
  3. Server Hostname:        192.168.1.4
  4. Server Port:            80
  5. Document Path:          /index.html
  6. Document Length:        3227 bytes
  7. Concurrency Level:      15000
  8. Time taken for tests:   148.963 seconds
  9. Complete requests:      100000
  10. Failed requests:        1
  11.    (Connect: 0, Receive: 0, Length: 1, Exceptions: 0)
  12. Total transferred:      341299896 bytes
  13. HTML transferred:       322699954 bytes
  14. Requests per second:    671.31 [#/sec] (mean)
  15. Time per request:       22344.410 [ms] (mean)
  16. Time per request:       1.490 [ms] (mean, across all concurrent requests)
  17. Transfer rate:          2237.48 [Kbytes/sec] received
  18. Connection Times (ms)
  19.               min  mean[+/-sd] median   max
  20. Connect:        0    1  17.0      0     598
  21. Processing:  3570 20891 3865.4  22222   23293
  22. Waiting:     1175 10966 5642.1  11007   21571
  23. Total:       3585 20892 3865.4  22223   23294
  24. Percentage of the requests served within a certain time (ms)
  25.   50%  22223
  26.   66%  22315
  27.   75%  22542
  28.   80%  22584
  29.   90%  22747
  30.   95%  22804
  31.   98%  22860
  32.   99%  23078
  33. 100%  23294 (longest request)
复制代码
备注:这是在关闭服务器安全防御下才能测试的。因为服务器代码默认会对每个访问IP建立档案,发现恶意刷流量、DDOS都将主动拉黑。在开启安全模式下,任何测试软件都会被瞬间识别出来,并禁封IP。
要是添加php的支持,解析速度还会更慢。单日1000万次的访问,完全没问题,另外手机上的性能也不错可以达到PC端性能的50%。
默认是阉割了post方法,任何上传都会被禁止,服务器设置的安全文件类型才能被访问。
作者: Batcher    时间: 2017-8-26 23:42

回复 3# happy886rr


    相同环境下Apache和Nginx的测试数据有吗?
作者: happy886rr    时间: 2017-8-27 11:26

本帖最后由 happy886rr 于 2017-8-27 11:31 编辑

回复 4# Batcher

相同环境下Apache直接奔溃,因为windows下的apache根本承受不了1万5并发,这个已经远超apache的负载能力。nginx勉强能经得住1万5的并发,再多也会不干。而msp测试过2万并发,依然完成,因为msp不做任何限制,允许cpu接近100%工作,当然这只是低端双核处理器上的测试结果。
以下为nginx的测试结果:
  1. Server Software:        nginx/1.13.4
  2. Server Hostname:        192.168.1.4
  3. Server Port:            80
  4. Document Path:          /index.html
  5. Document Length:        3152 bytes
  6. Concurrency Level:      15000
  7. Time taken for tests:   116.318 seconds
  8. Complete requests:      100000
  9. Failed requests:        0
  10. Total transferred:      338600000 bytes
  11. HTML transferred:       315200000 bytes
  12. Requests per second:    859.71 [#/sec] (mean)
  13. Time per request:       17447.737 [ms] (mean)
  14. Time per request:       1.163 [ms] (mean, across all concurrent requests)
  15. Transfer rate:          2842.75 [Kbytes/sec] received
  16. Connection Times (ms)
  17.               min  mean[+/-sd] median   max
  18. Connect:        0    1  11.5      0     601
  19. Processing:  3465 15650 3603.8  15525   25104
  20. Waiting:     1123 8637 4837.5   8219   23743
  21. Total:       3465 15651 3604.1  15525   25104
  22. Percentage of the requests served within a certain time (ms)
  23.   50%  15525
  24.   66%  16639
  25.   75%  16766
  26.   80%  17133
  27.   90%  19160
  28.   95%  22655
  29.   98%  24098
  30.   99%  24652
  31. 100%  25104 (longest request)
复制代码

作者: Batcher    时间: 2017-8-27 12:54

回复 5# happy886rr


    确实,Windows里面Apache和Nginx的性能比Linux差很多。
作者: xxbdh    时间: 2017-8-31 16:40

本帖最后由 xxbdh 于 2017-8-31 16:46 编辑

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

回复 7# xxbdh
建议不错,下次更新时可以添加。
作者: freesoft00    时间: 2017-11-6 13:33

本帖最后由 freesoft00 于 2017-11-6 21:10 编辑

回复 8# happy886rr


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

回复 9# freesoft00
可以是可以,但是C语言太底层了,用C写比较累,需要很多代码才能实现。
作者: freesoft00    时间: 2017-11-6 21:10

回复 10# happy886rr


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

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




欢迎光临 批处理之家 (http://www.bathome.net/) Powered by Discuz! 7.2