最近学习了linux网络编程,自己写了一给非常简单的web服务器,他只能回应客户端(browser)取页面命令(GET),能支持 ".html",".jpeg",".png",".gif"格式的文件,没有对任何的服务器脚本语言作处理。以下是源代码:
#include
#include
#include
#include
#include
#include
#include
#define Debug
#define HEADER\
"HTTP/1.0 %s\r\n"\
"Server: Redhat\r\n"\
"Content-Length: %ld\r\n"
#define CONTENT_TEXT\
"Content-Type: text/html\r\n\r\n"
#define CONTENT_JPEG\
"Content-Type: image/jpeg\r\n\r\n"
#define CONTENT_PNG\
"Content-type: image/png\r\n\r\n"
#define CONTENT_GIF\
"Content-type: image/gif\r\n\r\n"
#define HTML_NOTFOUND\
"\n"\
"Error 404\n"\
"\n"\
"Redhat Server can't find document
"\
"
\r\n"
#define DEFAULT_DOC "index.html"
#define WEB_ROOT "webroot/"
#define PORT 8000
void get_path_and_cmd(const char *msg, char *path, char *cmd)
{
int i;
char s[512];
i = 0;
memset(path, 0, sizeof(path));
memset(cmd, 0, sizeof(cmd));
if(msg != NULL){
while(msg != ' ')
i++;
if(msg == ' ')
strncpy(cmd, msg, i);
strncpy(s, &msg, strlen(msg)-i);
i = 0;
while(s != ' ')
i++;
if(s == ' '){
strncpy(path, s, i);
path = 0;
}
}
}
static int send_head(const char *msg, off_t len, const char *path, int fd)
{
char buf[1024], *dot;
int nwrite;
snprintf(buf, sizeof(buf), HEADER, msg, (long)len);
nwrite = strlen(buf);
if(nwrite != write(fd, buf, nwrite))
return -1;
dot = strrchr(path, '.');
if(dot != NULL && (strcasecmp(dot, ".jpg") == 0 || strcasecmp(dot, ".jpeg") == 0)){
nwrite = strlen(CONTENT_JPEG);
if(nwrite != write(fd, CONTENT_JPEG, nwrite))
return -1;
}
else if(dot != NULL && (strcasecmp(dot, ".png") == 0)){
nwrite = strlen(CONTENT_PNG);
if(nwrite !=(fd, CONTENT_PNG, nwrite))
return -1;
}
else if(dot != NULL && (strcasecmp(dot, ".gif")) == 0){
nwrite = strlen(CONTENT_GIF);
if(nwrite != (fd, CONTENT_GIF, nwrite))
return -1;
}
else{
nwrite = strlen(CONTENT_TEXT);
if(nwrite != write(fd, CONTENT_TEXT, nwrite))
return -1;
}
return 0;
}
static int hand_request(char *cmd, char *path, int fd)
{
char buf[1024], pathname[512];
FILE *in;
struct stat statbuf;
int size, flags;
ssize_t n;
if(strcasecmp(cmd, "get") != 0){
printf("Unknown request\n");
return -1;
}
if(strcasecmp(path, "") == 0){
flags = 0;
size = strlen(DEFAULT_DOC);
}
else{
size = strlen(path);
flags = 1;
}
snprintf(pathname, sizeof(pathname)-1-size, "%s", WEB_ROOT);
if(stat(pathname, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)){
if(flags)
strcat(pathname, path);
else
strcat(pathname, DEFAULT_DOC);
}
if(stat(pathname, &statbuf) == -1 || (in = fopen(pathname, "rb")) == NULL){
n = strlen(HTML_NOTFOUND);
send_head("404 Not Found", n, "", fd);
if(n != write(fd, HTML_NOTFOUND, n))
return -1;
}
#ifdef Debug
fprintf(stdout, "path: %s\n", pathname);
#endif
send_head("200 OK", statbuf.st_size, pathname, fd);
while((n = fread(buf, 1, sizeof(buf), in)) > 0){
if(n != write(fd, buf, n))
return -1;
}
return 0;
}
int main(void)
{
char msg[4096], cmd[8], path[512];
ssize_t nrcv;
int listenfd, connfd;
struct sockaddr_in servaddr;
char host[128] = "//";
if(gethostname(&host[2], sizeof(host)-6) == -1)
return -1;
strcat(host, "8000");
printf("Connect to host \"%s\"\n",host);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if(listenfd < 0){
fprintf(stderr, "Soket error\n");
exit(1);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0){
fprintf(stderr, "Bind error\n");
exit(1);
}
if(listen(listenfd, 5) < 0){
fprintf(stderr, "Listen error\n");
exit(1);
}
for(;;){
fprintf(stdout, "waiting....\n");
connfd = accept(listenfd, NULL, NULL);
if(connfd < 0){
fprintf(stderr, "accept error\n");
exit(1);
}
if(read(connfd, msg, sizeof(msg)-1) <= 0){
#ifdef Debug
fprintf(stderr, "\nUnkown request\t\t");
#endif
return -1;
}
msg[strlen(msg)] = 0;
#ifdef Debug
fprintf(stderr, "\nheader: %s\t", msg);
#endif
get_path_and_cmd(msg, path, cmd);
hand_request(cmd, path, connfd);
close(connfd);
}
close(listenfd);
return 0;
}
里面基本没有对错误进行处理,只是简单的返回-1,要便于调试,可以用条件编译,打印出相关的信息,或许你们有更好的方法来实现一个简单的wen server,不妨拿出来大家一起分享。
我的运行环境是Windows xp 上VMware workstation中的 Redhat9.0。
Redhat9.0的ip地址是192.168.37.128 选择了8000 端口
webroot 的工作目录是/home/wanghui/netinet
用gcc编译,并后台运行
$ gcc -owebserver webserver.c
$ ./webserver&
在Windows xp 中打开IE browser 在地址栏中输入 回车
即可以看到/home/wanghui/network/webroot/中的index.html页面了(如果有的话)。在
Redhat9.0的终端中可以看到browser发送的请求如下:
header: GET / HTTP/1.1
Accept: */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: 192.168.37.128:8000
Connection: Keep-Alive
t: 192.168.37.128:8000
Connection: Keep-Alive
;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer:
rer:
no-cache
path: webroot/index.html
waiting....
本来是要在我们宿舍建一个局域网的,以我的机子作为服务器,但我得机子比较烂,不知是主板上的南桥有问题还是那几个pci槽全都不管用
了,插上网线,本地连接还是一把红叉,很无赖,以前是能上网的。
本人没有学习jsp,php, asp等服务器脚本语言,服务器是怎样去解释这些脚本的,我是一点都不清楚,所以也就无法要它支持他们
了,有兴趣的可以一起学习讨论,一起来完善这个服务程序,本来想去看apache的源码,太大了,代码组织也很复杂,估计很难看懂
的(:-)。
阅读(2410) | 评论(0) | 转发(1) |