您的位置:首页 > 运维架构 > Linux

嵌入式linux简单Web服务器 之 课设一周总结

2010-08-31 13:16 162 查看
开学前两周硬件实验,虽说是硬件,但软件的味道算更浓些。选了个题目——嵌入式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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/stat.h>
#include <dirent.h>
#include <signal.h>
#include <unistd.h>
#include <ctype.h>

#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, "<H1>Index of %s</H1>/n/n", dirName);

if (dirName[strlen(dirName) - 1] != '/')
strcat(dirName, "/");

while (pDirent = readdir(dir))
fprintf(pFile, "<p><a href=/"/%s%s/">%s</a></p>/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, "<html><head><title>404 File Not Found"
"</title></head>/n");
fprintf(pFile, "<body>The requested URL was not found"
"on this server</body></html>/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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: