标题: 跨平台安全服务器 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限制范围。- /*
- 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;
- }
复制代码
作者: 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万次请求(仅一个请求失败,其余全部成功)。测试结果如下:- 测试环境: 赛扬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)
复制代码
备注:这是在关闭服务器安全防御下才能测试的。因为服务器代码默认会对每个访问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的测试结果:- 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)
复制代码
作者: 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://bbs.bathome.net/) |
Powered by Discuz! 7.2 |