批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程
[批处理文件精品]批处理版照片整理器[批处理文件精品]纯批处理备份&还原驱动在线第三方下载
返回列表 发帖

跨平台web服务器 misv

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

图片均为外部链接,不上传任何附件,使用源码发行。
对HTTP协议粗略看了下,于是就有了这个跨平台web服务器。支持普通的GET、HEAD协议,其他协议将会适当修改,对动态语言如php的支持将会在未来通过修改php源码做成动态加载,敬请期待。代码做到了跨平台编译、兼容gcc、VS,能在linux、win、安卓、各种小型嵌入式上可靠运行。为了取得最好的执行效果,不提供二进制文件、请源码编译。
.
演示效果:编译misv.c,在手机上创建服务器,整个网站跑在misv服务器上,通过电脑端访问手机服务器,可实现1000并发,380多RPS。单台手机服务器可满足日均300万PV点击率。

用法:直接双击即可,网站根目录请自行修改HTTP_SERVER_ROOT的值,网站端口请修改HTTP_SERVER_PORT。
原创代码(使用了大量宏定义、可跨平台编译):
  1. /*
  2. CONSOLE MINI HTTP SERVER, COPYRIGHT@2017~2019 BY HAPPY, VERSION 1.0
  3. MISV.EXE
  4. FRI AUG 18 2017 18:16:01 GMT+0800
  5. ////////////////////////////////////////////////////////////////////
  6. 说明:
  7. 跨平台代码,可在Windows、Linux、安卓等多种嵌入式设备上直译。
  8. 兼容GCC、VS编译器。 如使用VS请先改名为misv.cpp方可正常编译。
  9. 编译时,可通过定义USE_FAST_MODE、USE_SLEEP_MODE 宏实现优化。
  10. 编译:
  11. gcc misv.c -lWs2_32  -o misv.exe       REM Windows下编译
  12. gcc misv.c -lpthread -o misv           REM Linux下编译
  13. gcc misv.c                             REM 安卓及嵌入式编译
  14. cl  misv.cpp /MD /Ox /out:misv.exe     REM VS下编译
  15. ////////////////////////////////////////////////////////////////////
  16. */
  17. ////////////////////////////////////////////////////////////////////
  18. // 服务器名称
  19. #define HTTP_SERVER_NAME "MISV"
  20. // 服务器端口
  21. #define HTTP_SERVER_PORT 80
  22. // 服务器根目录WWW
  23. #define HTTP_SERVER_ROOT "."
  24. // 服务器默认目录主页
  25. #define HTTP_SERVER_HOMEPAGE "index.html"
  26. ////////////////////////////////////////////////////////////////////
  27. ////////////////////////////////////////////////////////////////////
  28. // 是否 启用 高速响应
  29. #define USE_FAST_MODE
  30. // 是否 启用 智能休眠
  31. #define USE_SLEEP_MODE
  32. ////////////////////////////////////////////////////////////////////
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. #include <string.h>
  36. #include <time.h>
  37. #if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  38. #include <direct.h>
  39. #include <Winsock2.h>
  40. #else
  41. #include <sys/socket.h>
  42. #include <sys/types.h>
  43. #include <netinet/in.h>
  44. #include <arpa/inet.h>
  45. #include <pthread.h>
  46. #include <unistd.h>
  47. #endif
  48. #if defined(_MSC_VER)
  49. #pragma comment(lib,"Ws2_32.lib")
  50. #else
  51. #include <stdbool.h>
  52. #endif
  53. // 队列等待最大长度(不超过5)
  54. #define MAX_BACKLOG_SIZE 5
  55. // 服务器最小休眠冲击量(MIN-Requests per second)
  56. #define MIN_HTTP_RPS 1
  57. // 最大字元长
  58. #define MAX_BUFF_SIZE 1024*4
  59. // 最小字元长
  60. #define MIN_BUFF_SIZE 128
  61. // 文件发送错误
  62. #define FILE_SEND_ERROR -1
  63. // 文件发送错误
  64. #define HTTP_GENERAL_ERROR 1
  65. // 文件路径阈值
  66. #define MAX_PATH_SIZE 1024
  67. #if defined(USE_FAST_MODE)
  68. // 定义VS控制台启动方式(对VS编译器,改后缀为.cpp编译时,实现无窗化启动)
  69. #if defined(_MSC_VER)
  70. #pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
  71. #endif
  72. #endif
  73. // 定义服务器默认路径分隔符 "\" 或 "/"
  74. #if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  75. #define HTTP_SERVER_PATHCHARACTER "\\"
  76. #else
  77. #define HTTP_SERVER_PATHCHARACTER "/"
  78. // 定义宏名错误值
  79. #define INVALID_SOCKET -1
  80. #define SOCKET_ERROR -1
  81. // 跨平台数据类型
  82. typedef int SOCKET;
  83. typedef unsigned long DWORD;
  84. typedef void *LPVOID;
  85. typedef const struct sockaddr *LPSOCKADDR;
  86. #ifndef byte
  87. typedef unsigned char byte;
  88. #endif
  89. #endif
  90. #ifndef NULL
  91. #define NULL (void*)0
  92. #endif
  93. // 跨平台宏函数
  94. #if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  95. #define GETCWD _getcwd
  96. #define SLEEP(x) Sleep(x)
  97. #define CLOSESOCKET closesocket
  98. #else
  99. #define GETCWD getcwd
  100. #define SLEEP(x) usleep(x*1000)
  101. #define CLOSESOCKET close
  102. #endif
  103. // 判断小写字母宏函数
  104. #define ISLOWERLETTER(x) ('a'<=(x)&&(x)<='z')
  105. // 声明 服务器当前目录
  106. char currentPath[MAX_PATH_SIZE];
  107. // HTTP协议关键词
  108. static const char* HTTP_KEY_WORDS[] = {"GET", "POST", "HEAD", "EDIT", NULL};
  109. // 定义404返回页面
  110. static const char responsePage404[] =
  111.     "<html>"
  112.     "<head>"
  113.     "</head>"
  114.     "<title>404 Not Found</title>"
  115.     "<body>"
  116.     "  <h1 align='center'><font color=#FF1493>Sorry,404 Error!</font></h1>"
  117.     "    <script>"
  118.     "      document.write('<hr/>' + '<font color=#9400D3>' + new Date() + '</font>' + '<hr/>' + '<font color=#A9A9A9>' + 'File not found.' + '</font>');"
  119.     "  </script>"
  120.     "</body>"
  121.     "</html>";
  122. static const int responsePage404Strlen = sizeof(responsePage404) / sizeof(char);
  123. // 通用 关键词识别函数
  124. int HTTP_IdentifyKey(char* inStr, char** inKeyWords, const char* endChars)
  125. {
  126. int SN = 0;
  127. while(inKeyWords[SN] != NULL)
  128. {
  129. char *op=inStr, *kp=inKeyWords[SN];
  130. while(*kp != '\0')
  131. {
  132. if(
  133.     ((ISLOWERLETTER(*op))?(*op-32):(*op)) != ((ISLOWERLETTER(*kp))?(*kp-32):(*kp))
  134. )
  135. {
  136. break;
  137. }
  138. op++;
  139. kp++;
  140. }
  141. if(*kp == '\0')
  142. {
  143. if(*op == '\0')
  144. {
  145. return SN;
  146. }
  147. while(*endChars != '\0')
  148. {
  149. if(*op == *(endChars++))
  150. {
  151. return SN;
  152. }
  153. }
  154. }
  155. SN ++;
  156. }
  157. return -1;
  158. }
  159. // 错误打印函数
  160. int HTTP_PrintError(char* perr)
  161. {
  162. #if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  163. fprintf(stderr, "%s: %d\n", perr, WSAGetLastError());
  164. #else
  165. perror(perr);
  166. #endif
  167. return 0;
  168. }
  169. // 发送200报头
  170. int HTTP_Response200(SOCKET soc, int contentLength)
  171. {
  172. char* responseBuf = (char*)malloc(MIN_BUFF_SIZE * sizeof(char));
  173. sprintf
  174. (
  175.     responseBuf
  176.     ,
  177.     "HTTP/1.1 200 OK\r\n"
  178.     "Server: %s\r\n"
  179.     "Connection: keep-alive\r\n"
  180.     "Content-Type: text/html\r\n"
  181.     "Content-Length: %d\r\n\r\n"
  182.     ,
  183.     HTTP_SERVER_NAME, contentLength
  184. );
  185. send(soc, responseBuf, strlen(responseBuf), 0);
  186. free(responseBuf);
  187. return 0;
  188. }
  189. // 发送404报头
  190. int HTTP_Response404(SOCKET soc)
  191. {
  192. char* responseBuf = (char*)malloc(MIN_BUFF_SIZE * sizeof(char));
  193. sprintf
  194. (
  195.     responseBuf
  196.     ,
  197.     "HTTP/1.1 404 NOT FOUND\r\n"
  198.     "Server: %s\r\n"
  199.     "Connection: keep-alive\r\n"
  200.     "Content-Type: text/html\r\n\r\n"
  201.     ,
  202.     HTTP_SERVER_NAME
  203. );
  204. send(soc, responseBuf, strlen(responseBuf), 0);
  205. free(responseBuf);
  206. return 0;
  207. }
  208. // 发送请求的资源
  209. int HTTP_SendFile(SOCKET soc, FILE *fp, bool removeUTF8BOM)
  210. {
  211. // 重置文件流位置
  212. rewind(fp);
  213. // 分配数据拾取器
  214. byte* pickUpBuf = (byte*)malloc(MAX_BUFF_SIZE * sizeof(byte*));
  215. size_t freadSize = 0;
  216. if(removeUTF8BOM)
  217. {
  218. // 去除UTF8 BOM头 (必要的兼容性处理)
  219. freadSize = fread(pickUpBuf, sizeof(byte), 3, fp);
  220. if(freadSize == 3)
  221. {
  222. if(
  223.     (*(pickUpBuf+0) != 0xEF) ||
  224.     (*(pickUpBuf+1) != 0xBB) ||
  225.     (*(pickUpBuf+2) != 0xBF)
  226. )
  227. {
  228. rewind(fp);
  229. }
  230. }
  231. }
  232. // 发送文件二进制数据
  233. while (!feof(fp))
  234. {
  235. freadSize = fread(pickUpBuf, sizeof(byte), MAX_BUFF_SIZE, fp);
  236. if (send(soc, (const char*)pickUpBuf, freadSize, 0) == SOCKET_ERROR)
  237. {
  238. #ifndef USE_FAST_MODE
  239. HTTP_PrintError("Send file Failed");
  240. #endif
  241. return FILE_SEND_ERROR;
  242. }
  243. }
  244. free(pickUpBuf);
  245. return 0;
  246. }
  247. // 服务器核心函数
  248. DWORD
  249. #if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  250. WINAPI
  251. #endif
  252. HTTP_ServerCore(LPVOID lpvsoc)
  253. {
  254. DWORD ret = 0;
  255. SOCKET soc = (SOCKET)lpvsoc;
  256. // 分配客户请求接受容器
  257. char* receiveBuf = (char*)malloc(MAX_BUFF_SIZE * sizeof(char));
  258. int receiveCount = recv(soc, receiveBuf, MAX_BUFF_SIZE, 0);
  259. if (receiveCount == SOCKET_ERROR)
  260. {
  261. #ifndef USE_FAST_MODE
  262. HTTP_PrintError("Receive failed");
  263. #endif
  264. ret = HTTP_GENERAL_ERROR;
  265. goto JMP_END;
  266. }
  267. else
  268. {
  269. // 置结束符 '\0'
  270. receiveBuf[receiveCount]='\0';
  271. #ifndef USE_FAST_MODE
  272. // 接收成功,打印客户请求报文
  273. fprintf(stdout, "Receive data from client: \n%s\n", receiveBuf);
  274. #endif
  275. }
  276. // 解析 HTTP协议
  277. char* httpMethod = strtok(receiveBuf, " \t");
  278. if (httpMethod == NULL)
  279. {
  280. #ifndef USE_FAST_MODE
  281. fprintf(stdout, "Receive request data error\n");
  282. #endif
  283. ret = HTTP_GENERAL_ERROR;
  284. goto JMP_END;
  285. }
  286. // 获取 客户端HTTP请求 协议值
  287. int httpMethodValue = HTTP_IdentifyKey(httpMethod, (char**)HTTP_KEY_WORDS, " \t");
  288. // 对于 不支持的协议,则断开连接
  289. if (httpMethodValue == -1)
  290. {
  291. #ifndef USE_FAST_MODE
  292. fprintf(stdout, "Not support this method '%s'\n", httpMethod);
  293. #endif
  294. ret = HTTP_GENERAL_ERROR;
  295. goto JMP_END;
  296. }
  297. // 解析 URL地址
  298. char* httpURL = strtok(NULL, " \t");
  299. if (httpURL == NULL)
  300. {
  301. #ifndef USE_FAST_MODE
  302. fprintf(stdout, "Receive data error\n");
  303. #endif
  304. ret = HTTP_GENERAL_ERROR;
  305. goto JMP_END;
  306. }
  307. // 解析 请求文件路径
  308. char* httpFilePath = strchr(httpURL, '/');
  309. if (httpFilePath == NULL)
  310. {
  311. httpFilePath = httpURL;
  312. }
  313. #if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  314. else
  315. {
  316. char* p = httpFilePath;
  317. while (*p != '\0')
  318. {
  319. if (*p == '/')
  320. {
  321. *p = (HTTP_SERVER_PATHCHARACTER)[0];
  322. }
  323. p++;
  324. }
  325. }
  326. #endif
  327. #ifndef USE_FAST_MODE
  328. // 输出 客户端请求摘要
  329. fprintf
  330. (
  331.     stdout
  332.     ,
  333.     "HTTP method: %s\n"
  334.     "File svpath: %s\n"
  335.     ,
  336.     httpMethod
  337.     ,
  338.     httpFilePath
  339. );
  340. #endif
  341. // 拼接 请求文件路径
  342. char* requestFilePath = (char*)malloc(MAX_PATH_SIZE * sizeof(char));
  343. sprintf
  344. (
  345.     requestFilePath
  346.     ,
  347.     "%s%s%s%s%s"
  348.     ,
  349.     currentPath, HTTP_SERVER_PATHCHARACTER, HTTP_SERVER_ROOT, HTTP_SERVER_PATHCHARACTER, httpFilePath
  350. );
  351. // 如果请求的是目录,则跳转到该目录主页“index.html”
  352. int httpFilePathLen = strlen(httpFilePath);
  353. if (*(httpFilePath + httpFilePathLen - 1) == (HTTP_SERVER_PATHCHARACTER)[0])
  354. {
  355. strcat(requestFilePath, HTTP_SERVER_HOMEPAGE);
  356. }
  357. // 以二进制方式 打开服务器文件流
  358. FILE* fp = fopen(requestFilePath, "rb");
  359. // 如果 读取文件失败
  360. if (fp == NULL)
  361. {
  362. // 如果是HEAD方法,则发送404报头
  363. if (httpMethodValue == 2)
  364. {
  365. HTTP_Response404(soc);
  366. }
  367. // 如果是 GET方法,则发送404页面
  368. else if (httpMethodValue == 0)
  369. {
  370. send(soc, responsePage404, responsePage404Strlen, 0);
  371. }
  372. #ifndef USE_FAST_MODE
  373. // 打印 网站404错误,并执行跳转
  374. fprintf(stdout, "File not found.\n");
  375. #endif
  376. ret = HTTP_GENERAL_ERROR;
  377. goto JMP_END;
  378. }
  379. // 测量 要请求的文件长度
  380. fseek(fp, 0, SEEK_SET);
  381. fseek(fp, 0, SEEK_END);
  382. int fpSize = ftell(fp);
  383. // 如果是HEAD方法,则发送200报头
  384. if (httpMethodValue == 2)
  385. {
  386. HTTP_Response200(soc, fpSize);
  387. goto JMP_END;
  388. }
  389. // 如果是GET方法则发送请求的资源
  390. if (httpMethodValue == 0)
  391. {
  392. #ifndef USE_FAST_MODE
  393. // 显示 要请求的文件长度
  394. fprintf(stdout, "File length: %d\n", fpSize);
  395. #endif
  396. if (HTTP_SendFile(soc, fp, true) != FILE_SEND_ERROR)
  397. {
  398. #ifndef USE_FAST_MODE
  399. fprintf(stdout, "File send OK\n");
  400. }
  401. else
  402. {
  403. fprintf(stdout, "File send failed\n");
  404. #endif
  405. }
  406. }
  407. fclose(fp);
  408. JMP_END:
  409. // 释放 套接口
  410. CLOSESOCKET(soc);
  411. #ifndef USE_FAST_MODE
  412. // 显示 标尾信息
  413. fprintf
  414. (
  415.     stdout
  416.     ,
  417.     "Close socket(%d)\n"
  418.     "[<==]\n"
  419.     ,
  420.     soc
  421. );
  422. #endif
  423. free(receiveBuf);
  424. free(requestFilePath);
  425. return ret;
  426. }
  427. // MAIN主函数
  428. int main()
  429. {
  430. // 获取服务器 当地时间
  431. time_t timeNow = time(&timeNow);
  432. char*  nowTime = ctime(&timeNow);
  433. // 显示 标头信息
  434. fprintf
  435. (
  436.     stdout,
  437.     "%s"
  438.     "[===* Welcome to use %s *===]\n"
  439.     "[PORT: %d]\n"
  440.     "[>>>]\n"
  441.     ,
  442.     nowTime
  443.     ,
  444.     HTTP_SERVER_NAME
  445.     ,
  446.     HTTP_SERVER_PORT
  447. );
  448. //获取当前文件所在目录
  449. GETCWD(currentPath, MAX_PATH_SIZE);
  450. #if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  451. // 启动 安全套接字
  452. WSADATA wsaData;
  453. if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
  454. {
  455. fprintf(stdout, "Failed to create wsocket\n");
  456. return HTTP_GENERAL_ERROR;
  457. }
  458. #endif
  459. // 创建 连接套接口、监听套接口
  460. SOCKET soc, socListen;
  461. socListen =socket(AF_INET, SOCK_STREAM, 0);
  462. if(socListen == INVALID_SOCKET)
  463. {
  464. HTTP_PrintError("Creat listen socket failed");
  465. return HTTP_GENERAL_ERROR;
  466. }
  467. // 声明 服务器地址,客户端地址
  468. struct sockaddr_in serverSockaddrIn, clientSockaddrIn;
  469. // 配置 服务器信息
  470. serverSockaddrIn.sin_family      = AF_INET;
  471. serverSockaddrIn.sin_port        = htons(HTTP_SERVER_PORT);
  472. serverSockaddrIn.sin_addr.s_addr = htonl(INADDR_ANY);
  473. // 绑定 监听套接口 与 服务器地址
  474. if (bind(socListen, (LPSOCKADDR)&serverSockaddrIn, sizeof(serverSockaddrIn)) == SOCKET_ERROR)
  475. {
  476. HTTP_PrintError("Blind server failed");
  477. return HTTP_GENERAL_ERROR;
  478. }
  479. // 通过 监听套接口 监听
  480. if (listen(socListen, MAX_BACKLOG_SIZE) == SOCKET_ERROR)
  481. {
  482. HTTP_PrintError("Listen socket failed");
  483. return HTTP_GENERAL_ERROR;
  484. }
  485. // 声明 计时器变量
  486. clock_t preClock = clock();
  487. int countTimes = 0;
  488. int requestsPerSecond = 1000;
  489. // 监听 网页资源请求
  490. while(true)
  491. {
  492. int clientSockaddrInLen = sizeof(clientSockaddrIn);
  493. // 返回 客户连接套接口
  494. soc = accept(socListen, (struct sockaddr*)&clientSockaddrIn, &clientSockaddrInLen);
  495. if (soc == INVALID_SOCKET)
  496. {
  497. HTTP_PrintError("Accept failed");
  498. break;
  499. }
  500. #if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  501. // 创建 请求线程
  502. DWORD ThreadID;
  503. CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)HTTP_ServerCore, (LPVOID)soc, 0, &ThreadID);
  504. #else
  505. pthread_t newPthreadT;
  506. pthread_create(&newPthreadT, NULL, (void*)HTTP_ServerCore, (void*)(intptr_t)soc);
  507. #endif
  508. #if defined(USE_SLEEP_MODE)
  509. // 计次器
  510. countTimes ++;
  511. // 每隔300毫秒检测一次服务器冲击量
  512. if (clock()-preClock > CLOCKS_PER_SEC*0.3)
  513. {
  514. requestsPerSecond = CLOCKS_PER_SEC * countTimes / (clock()-preClock);
  515. countTimes = 0;
  516. preClock = clock();
  517. }
  518. // 如果冲击量过小,则休眠CPU占用率
  519. if (requestsPerSecond < MIN_HTTP_RPS)
  520. {
  521. SLEEP(3);
  522. }
  523. #endif
  524. }
  525. // 关闭 安全套接口
  526. CLOSESOCKET(socListen);
  527. #if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) || defined(_MSC_VER)
  528. // 关闭 安全套接字
  529. WSACleanup();
  530. #endif
  531. return 0;
  532. }
复制代码
3

评分人数

GCC编译失败
不过VC成功了
感谢分享,好东西~

TOP

本帖最后由 523066680 于 2017-8-18 19:53 编辑

回复 2# 老刘1号


    有没有加 -lws2_32,我这边 mingw gcc 编译通过
综合型编程论坛
Writing Code That Nobody Else Can Read.

TOP

回复 2# 老刘1号
misv只有10kb左右,随便扔一个目录,双击运行,这个目录就成网站了。比如你电脑存了几十部电影,那么把misv.exe扔到电脑目录,直接双击运行。 你就可以在任意一部手机上在线观看你电脑上的所有电影。或者你把你的地址告诉其他人, 那么别人也能通过互联网直接在线浏览你电脑里的视频。
1

评分人数

TOP

本帖最后由 happy886rr 于 2017-8-18 21:15 编辑

目前几乎支持所有格式文件的浏览。但做了安全限定,只能访问misv当前目录文件。 最多支持 1000人同时在线观看。后续版本会加入缓存机制,加入服务器管理员机制,加入增删改查功能,加入一种新的msp动态脚本语言。

TOP

本帖最后由 老刘1号 于 2017-8-18 23:48 编辑

回复 3# 523066680


    这个确实没有……
我的疏忽……

TOP

目前几乎支持所有格式文件的浏览。但做了安全限定,只能访问misv当前目录文件。 最多支持 1000人同时在线观 ...
happy886rr 发表于 2017-8-18 21:13


http文件预览不是主要跟浏览器相关吗?
服务器端除了报头返回了Content-Type:text/html 也没做什么吧
结果还不是靠浏览器自己的MIME配置来预览。

TOP

回复  老刘1号
misv只有10kb左右,随便扔一个目录,双击运行,这个目录就成网站了。比如你电脑存了几十部 ...
happy886rr 发表于 2017-8-18 21:10


为什么在我这里访问 1.avi 就只是提示下载文件呢?

TOP

回复 8# xxbdh

HTML5的video标签好像只支持mp4格式,IE的只支持特殊制式的mp4,其他的格式还需要解码器播放。浏览器并不是播放器,不可能各种视频格式直接播放。
这个misv已经淘汰了,我的新版msp比这个版本更强大,增加抗DDOS攻击,各类安全模式,多并发优化,能抵御1万5000并发,单天800万次访问量,各项指标已略超apache,总代码量超过1000行,目前还在测试阶段,好吧,我一会发布,你去看新帖就行。

TOP

基础html/text的webserver很好些,我能写到1~3kb,
真没什么意义。

那个,对支持asp的研究过嘛。不用asp.dll的isapi实现。
我网上看了一套代码,mfc写的,支持application,session等六个内置对象。
但是复杂一点的asp解析就会出错。
不想用netbox,非常想写一个媲美netbox的aspserver lite

TOP

返回列表