开学前两周硬件实验,虽说是硬件,但软件的味道算更浓些。选了个题目——嵌入式linux简单Web服务器,算是很经典的了,源码什么的都有,只是一直想学学网络编程。之前学MFC的简单接触了些,简单的层面上说是没多少区别的了。至于嵌入式板接触不少了,就相当于linux下c编程,只是编译器用得没有vs爽~ 用eclipse写的,linux更懒开了,开也不开真机的,而是虚拟机(实盘安装,虚拟真机都可双开),后来觉得慢干脆在Windows装了个cywin,移植了上来~直接在Windows编了,更爽了~~ 总之不是不喜欢linux,只是linux少了很多常用的东西,只拿来编程就太枯燥了.
恩,说说主题吧,好像跑题了,汗啊……源码仿造了经典例子httpd写的,算是国人改的吧。那源码问题不是没有……内存泄漏什么的,有点汗,不过03年挺古老的现在还有那么大的参考价值也算不错了~
重写一下,不过挺懒,ctrl+c, ctrl+v多。还参照网上的多路复用改了一下,改成多路复用的了。传说可以并发?不过好像改了没多大效果?或许是概念不清,多路复用原理没理解很清楚。
问题如下:
用自写的暴力访问器来访问,新accept的client socket都是同一个值,不知道为什么。按理说不应该的呀。这样就有不是并发的感觉了,因为每次请求都是被处理完了的才到下一个,没有体现得并发性。除非因为本地访问是太快了,导致这样。但是慢的又不好测试,总不能让我把一个网页做成好几十M的吧?…
不过也发现可能是另一个原因,每一次新accept的client socket只请求一个文件,那么,而多路复用是以传送单个文件完毕来作为轮换指标的,这样一次轮换就处理完了一个socket。而一个网页上有多个文件(图片、html等),这样就可以轮流处理请求?这才是多路复用的本质思想?我倒是没注意到不同机子访问时有没有在请求文件时是轮流处理的。但如果真这样用不用多路复用不是一样的吗?我直接一个while(1)得了。
另外一个想不通的问题是,阻塞传输是什么时候被阻塞的? accept时? TCP协议下对client socket写东西时,调用写文件函数都是立即返回的吗?不然多路复用的核心函数select怎么能去收集这些信息?——如果是对client socket写东西时是处理完了程序才继续执行,那么select还有它的“判断IO写入完成才返回”功效? 早都写完了…更更不解的地方是,如果在select之前,socket的IO写入就完成了,那么它还会在FD_SET里吗? select好像很神奇的样子,《linux函数大全》居然没有写清楚…都意识流的…网上也是,关于多路复用的代码怎么样都有,天花乱坠的,我是昏花了……
好了,不说了,代码如下,希望熟悉多路复用的大侠能给我解惑一下(关于多路复用的代码在WaitForRequest()里)~不胜感谢……
#include #include #include #include #include #include #include #include #include #include #include #include #include #define WEB_SERVER_PORT 80 #define SOCKET_FD int //#define DEBUG SOCKET_FD g_svrSockfd; struct sockaddr_in g_svrAddr; int g_nSockCount; char g_referrer[128]; int g_content_length; static char g_copybuf[16384]; int ParseArg(int argc, char *argv[]) { if (argc != 2) return 10; else return atoi(argv[1]); } int PrintHeader(FILE *pFile, int content_type) { fprintf(pFile, "HTTP/1.0 200 OK/n"); switch (content_type) { case 't': fprintf(pFile, "Content-type: text/plain/n"); break; case 'g': fprintf(pFile, "Content-type: image/gif/n"); break; case 'j': fprintf(pFile, "Content-type: image/jpeg/n"); break; case 'h': fprintf(pFile, "Content-type: text/html/n"); break; } fprintf(pFile, "Server: Linux - WebSvr 1.0/n"); fprintf(pFile, "Expires: 0/n/n"); return 1; } int DoDir(FILE *pFile, char *dirName) { DIR * dir; struct dirent * pDirent; if ((dir = opendir(dirName)) == 0) { fprintf(stderr, "Unable to open directory %s, %d/n", dirName, errno); fflush(pFile); return 0; } PrintHeader(pFile, 'h'); fprintf(pFile, "Index of %s
/n/n", dirName); if (dirName[strlen(dirName) - 1] != '/') strcat(dirName, "/"); while (pDirent = readdir(dir)) fprintf(pFile, "%s
/n", dirName, pDirent->d_name, pDirent->d_name); closedir(dir); return 1; } int DoWrite(FILE* pFile, char* fileName, int content_type) { FILE * infile; int n; int wrote; if (!(infile = fopen(fileName, "r"))) { fprintf(stderr, "Unable to open %s, %d/n", fileName, errno); fflush(pFile); return 0; } PrintHeader(pFile, content_type); // copy while (n = fread(g_copybuf, 1, sizeof(g_copybuf), infile)) { wrote = fwrite(g_copybuf, n, 1, pFile); if (wrote < 1) { fclose(infile); return 0; } } fclose(infile); return 1; } int ParseReq(FILE *pFile, char *strReq) { char* pBegin; struct stat stbuf; //#ifdef DEBUG printf("REQ FOR : %s/n", strReq); //#endif // strReq移动到第一个空格字符 while (*(++strReq) != ' ') ; /*skip non-white space*/ // 将strReq移动到第一个非空字符 while (isspace((int)(*strReq))) strReq++; // strReq移动到 '/' 后第一个字符 while (*strReq == '/') strReq++; // pBegin 指向 '/' 后第一个字符 pBegin = strReq; // 将strReq移动到 '/' 后第一个为 ' ' 或 '?' 或为'/0'的字符 while (*strReq && (*(strReq) != ' ') && (*(strReq) != '?')) strReq++; if (*strReq == '?') {// 如果当前strReq指向字符为 '?' // 将 '?' 置为 '/0', // 将其后面第一个 ' ' 置为 '/0' char * e; *strReq = '/0'; if (e = strchr(strReq + 1, ' ')) { *e = '/0'; } } else {// 如果当前strReq指向字符不为 '?' // 将strReq指向字符置为 '/0', *strReq = '/0'; } // 如果pBegin指向字符为 空格字符,连继两个字符改为"./0" if (pBegin[0] == ' ') { pBegin[0] = '.'; pBegin[1] = '/0'; } // 如果pBegin没有内容,将pBegin改为"./0" if (pBegin[0] == '/0') strcat(pBegin, "."); //检查状态pBegin指向的文件状态(是否可读) if (pBegin && !stat(pBegin, &stbuf)) { int type = 0; if (S_ISDIR(stbuf.st_mode)) { char * end = pBegin + strlen(pBegin); strcat(pBegin, "/index.html"); if (!stat(pBegin, &stbuf)) { type = 'h'; } else { *end = '/0'; // type == 0, still } } else if (!strcmp(strReq - 4, ".gif")) type = 'g'; else if (!strcmp(strReq - 4, ".jpg") || !strcmp(strReq - 5, ".jpeg")) type = 'j'; else if (!strcmp(strReq - 4, ".htm") || !strcmp(strReq - 5, ".html")) type = 'h'; else type = 't'; if (!type) return DoDir(pFile, pBegin); else return DoWrite(pFile, pBegin, type); } else { PrintHeader(pFile, 'h'); fprintf(pFile, "404 File Not Found" "/n"); fprintf(pFile, "The requested URL was not found" "on this server/n"); } return 1; } int HandleConnect(SOCKET_FD sockfd) { FILE *pFile; char buf[256]; char buf1[256]; pFile = fdopen(sockfd, "a+"); if (!pFile) { fprintf(stderr, "#### httpd: Unable to open httpd input fd," "error %d/n", errno); close(sockfd); return 0; } setbuf(pFile, 0); if (!fgets(buf, 256, pFile)) { fprintf(stderr, "#### httpd: Error reading connection, error %d/n", errno); fclose(pFile); return 0; } #ifdef DEBUG printf("* Got MSG head : %s", buf); #endif g_referrer[0] = '/0'; g_content_length = -1; while ((NULL != fgets(buf1, 256, pFile)) && (strlen(buf1) > 2)) { #ifdef DEBUG printf("* Got MSG : %s", buf1); #endif //read other line to parse Rrferrer and g_content_length infomation //本段程序无用 /* if (!strncasecmp(buf1, "Referer:", 8)) { char* c = buf1+8; while (isspace((int)(*c))) c++; strcpy(g_referrer, c); } else if (!strncasecmp(buf1, "g_referrer:", 9)) { char* c = buf1+9; while (isspace((int)(*c))) c++; strcpy(g_referrer, c); } else if (!strncasecmp(buf1, "Content-length:", 15)) { g_content_length = atoi(buf1+15); } */ } if (ferror(pFile)) { fprintf(stderr, "#### http: Error continuing reading" "connection, error %d/n", errno); fclose(pFile); return 0; } if (!ParseReq(pFile, buf)) { fclose(pFile); return 0; } fclose(pFile); return 1; } void WaitForRequest() { fd_set fdSet; int i, addrLen; SOCKET_FD handleSockfd; struct sockaddr_in clientAddr; int selres; int* bIsConnected = malloc(sizeof(int) * g_nSockCount); for (i = 0; i < g_nSockCount; i++) bIsConnected[i] = 0; #ifdef DEBUG printf("> %d is svr!/n", i); #endif while (1) { FD_ZERO(&fdSet); FD_SET(g_svrSockfd, &fdSet); for (i = 3; i < g_nSockCount; i++) if (bIsConnected[i]) FD_SET(i, &fdSet); selres = select(g_nSockCount, &fdSet, NULL, NULL, NULL); if (selres == 0) continue; for (i = 3; i < g_nSockCount; i++) { if (FD_ISSET(i, &fdSet)) { #ifdef DEBUG printf("socket %d ready now!/n", i); #endif if (i == g_svrSockfd) { SOCKET_FD newSockfd; addrLen = sizeof(struct sockaddr); while (-1 == (newSockfd = accept(g_svrSockfd, (struct sockaddr*) &clientAddr, &addrLen))) { close(newSockfd); continue; } printf("---------------------------------------------" "-------/nCLIENT VISIT: %s/n", inet_ntoa( clientAddr.sin_addr)); if (newSockfd >= g_nSockCount) { printf("#### Request too much, refuse./n"); close(newSockfd); continue; } printf("> ACCEPT./n"); #ifdef DEBUG printf("> new socket is %d /n", newSockfd); #endif bIsConnected[newSockfd] = 1; handleSockfd = newSockfd; } else handleSockfd = i; #ifdef DEBUG printf("Handle %d now!/n", handleSockfd); #endif if (!HandleConnect(handleSockfd)) ; { bIsConnected[handleSockfd] = 0; close(handleSockfd); } } } } free(bIsConnected); } int WebServerStart() { int sockopt_value = 1; printf("starting http server.../n/n"); printf("create socket.../n"); // create socket if ((g_svrSockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("#### create socket failed./n"); return 0; } // set socket if ((setsockopt(g_svrSockfd, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt_value, sizeof(sockopt_value))) == -1) { perror("#### setsockopt failed.//n"); return 0; } // set server address bzero(&g_svrAddr, sizeof(g_svrAddr)); g_svrAddr.sin_family = AF_INET; g_svrAddr.sin_port = htons(WEB_SERVER_PORT); g_svrAddr.sin_addr.s_addr = htonl(INADDR_ANY); printf("bind address.../n"); // bind if (bind(g_svrSockfd, (struct sockaddr *) (&g_svrAddr), sizeof(g_svrAddr)) == -1) { perror("#### bind socket failed./n"); return 0; } printf("listen.../n"); //listen if (listen(g_svrSockfd, g_nSockCount) == -1) { perror("#### Unable to listen"); return 0; } WaitForRequest(); return 1; } int main(int argc, char *argv[]) { g_nSockCount = ParseArg(argc, argv) + 3; signal(SIGCHLD, SIG_IGN); signal(SIGPIPE, SIG_IGN); WebServerStart(); return 1; }