跨平台安全服务器 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] [quote]性能是misv的10倍,可承受15000并发,单日800万次访问量的繁重任务,性能略强于apache,不及nginx水平。[/quote]
对这部分感兴趣,能否把你的测试环境、测试工具、测试数据详细介绍一下? [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方法,任何上传都会被禁止,服务器设置的安全文件类型才能被访问。 [b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=202485&ptid=45166]3#[/url] [i]happy886rr[/i] [/b]
相同环境下Apache和Nginx的测试数据有吗? [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] [b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=202500&ptid=45166]5#[/url] [i]happy886rr[/i] [/b]
确实,Windows里面Apache和Nginx的性能比Linux差很多。 [i=s] 本帖最后由 xxbdh 于 2017-8-31 16:46 编辑 [/i]
简单测试了一下
URL不支持带空格或者中文等多字节字符
应该是需要增加UTF8的识别
另外日志里最好加上request与reponse的摘要信息 [b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=202629&ptid=45166]7#[/url] [i]xxbdh[/i] [/b]
建议不错,下次更新时可以添加。 [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 [b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=204118&ptid=45166]9#[/url] [i]freesoft00[/i] [/b]
可以是可以,但是C语言太底层了,用C写比较累,需要很多代码才能实现。 [b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=204126&ptid=45166]10#[/url] [i]happy886rr[/i] [/b]
安卓系统的还有一个Send Anywhere
我测试使用过的就这么几个。
主要就是局域网设备间互传文件。
多谢回复!
你看吧。看你的个人时间和精力。 可惜不支持asp, php
我有支持这些脚本的, 跨平台服务器代码.
页:
[1]