首先创建几个函数以方便我们调用
- open_clinetfd: 创建一个网络socket,作为客户端,并且尝试连接主机
- open_listenfd: 创建socket,作为主机,并开始监听
#include "mynet.h"
int open_clientfd(char *hostname, char *port){
int clientfd;
struct addrinfo hints, *listp, *p;
memset(&hints, 0,sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICSERV;
hints.ai_flags |= AI_ADDRCONFIG;
getaddrinfo(hostname, port, &hints, &listp);
for(p=listp;p;p=p->ai_next){
if((clientfd = socket(p->ai_family, p->ai_socktype,p->ai_protocol))<0)
continue;
if(connect(clientfd,p->ai_addr,p->ai_addrlen)!=-1)
break;
close(clientfd);
}
freeaddrinfo(listp);
if(!p) return -1;
else return clientfd;
}
int open_listenfd(char *port)
{
struct addrinfo hints, *listp, *p;
int listenfd, optval=1;
memset(&hints, 0 ,sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
hints.ai_flags |= AI_NUMERICSERV;
getaddrinfo(NULL, port, &hints, &listp);
for(p=listp;p;p=p->ai_next){
if((listenfd = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) < 0)
continue;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,(const void *)&optval,sizeof(int));
if(bind(listenfd,p->ai_addr,p->ai_addrlen)==0)
break; //success
close(listenfd); //bind fail, try next
}
freeaddrinfo(listp);
if(!p)
return -1;
if(listen(listenfd,LISTENQ)<0){
close(listenfd);
return -1;
}
return listenfd;
}
我们的服务器有以下几个模块
- int main(int argc, char** argv):从shell读取参数作为端口,使用上述open_listenfd创建一个监听端口,使用accept创建一个连接,进入do_it开始处理事务
- void doit(int fd):do_it的参数是connfd,读取请求,调用parse_uri分析请求是静态请求还是动态请求,分别调用serve_static和serve_dynamic进行响应
- int parse_uri(char *uri, char *filename, char *cgiargs):url不包含cgi-bin则认为是静态请求,读取请求的文件,若没有,默认返回home.html;否则认为是动态请求
- void serve_static(int fd, char *filename, int filesize):发送header,再发送文件
- void serve_dynamic(int fd, char *filename, char *cgiargs):服务动态内容,在cgi-bin找运行程序
#include "mynet.h"
void doit(int fd);
void read_requesthdrs(int fd);
int parse_uri(char *uri, char *filename, char *cgiargs);
void serve_static(int fd, char *filename, int filesize);
void get_filetype(char *filename, char *filetype);
void serve_dynamic(int fd, char *filename, char *cgiargs);
void clienterror(int fd, char*cause, char *errnum, char *shortmsg, char *longmsg);
int main(int argc, char** argv)
{
int listenfd,connfd;
char hostname[MAXLINE], port[MAXLINE];
socklen_t clientlen;
struct sockaddr_storage clientaddr;
if(argc!=2){
fprintf(stderr, "usage: %s <port>\n",argv[0]);
exit(1);
}
listenfd = open_listenfd(argv[1]);
while(1){
clientlen = sizeof(clientaddr);
connfd = accept(listenfd,(SA*)&clientaddr,&clientlen);
getnameinfo((SA*)&clientaddr,clientlen,hostname,MAXLINE,port,MAXLINE,0);
printf("Accepted connection from (%s, %s)\n",hostname,port);
doit(connfd);
close(connfd);
}
}
void doit(int fd){
int is_static;
struct stat sbuf;
char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
char filename[MAXLINE], cgiargs[MAXLINE];
read(fd, buf, MAXLINE);
printf("Requset hears:\n");
printf("%s",buf);
sscanf(buf, "%s %s %s", method, uri, version);
if(strcasecmp(method, "GET")){
clienterror(fd, method, "501", "Not implementedd","Tiny does not implement this method");
return;
}
//read_requesthdrs(fd);
is_static = parse_uri(uri,filename,cgiargs);
if(stat(filename,&sbuf)<0){
clienterror(fd, filename, "404", "Not found", "Tiny couldn't find this file");
return;
}
if(is_static){
if(!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)){
clienterror(fd, filename, "403", "Forbidden", "Tiny couldn't read the file");
return;
}
serve_static(fd,filename,sbuf.st_size);
}
else {
if(!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)){
clienterror(fd, filename, "403", "Forbidden", "Tiny couldn't run the CGI program");
return;
}
serve_dynamic(fd,filename,cgiargs);
}
}
void read_requesthdrs(int fd){
char buf[MAXLINE];
read(fd,buf,MAXLINE);
while(strcmp(buf, "\r\n")){
read(fd,buf,MAXLINE);
printf("%s",buf);
}
return;
}
int parse_uri(char *uri, char *filename, char *cgiargs){
char *ptr;
if(!strstr(uri, "cgi-bin")) {
strcpy(cgiargs,"");
strcpy(filename,".");
strcat(filename,uri);
if(uri[strlen(uri)-1] == '/')
strcat(filename,"home.html");
return 1;
}
else{ //dynammic content
ptr = index(uri,'?');
if (ptr) {
strcpy(cgiargs, ptr+1);
*ptr = '\0';
}
else
strcpy(cgiargs, ptr+1);
strcpy(filename,".");
strcat(filename,uri);
return 0;
}
}
void serve_static(int fd, char *filename, int filesize){
int srcfd;
char *srcp, filetype[MAXLINE], buf[MAXLINE*10];
get_filetype(filename, filetype);
sprintf(buf, "HTTP/1.0 200 OK\r\n");
sprintf(buf, "%sServer: Tiny Web Server\r\n",buf);
sprintf(buf, "%sConnection: close\r\n",buf);
sprintf(buf, "%sContent-length: %d\r\n",buf,filesize);
sprintf(buf, "%sContent-type: %s\r\n\r\n",buf,filetype);
write(fd,buf,strlen(buf));
printf("Response headers:\n");
printf("%s",buf);
srcfd = open(filename,O_RDONLY, 0);
srcp = mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
close(srcfd);
write(fd,srcp,filesize);
munmap(srcp,filesize);
}
void get_filetype(char *filename, char *filetype){
if(strstr(filename,".html"))
strcpy(filetype, "text/html");
else if (strstr(filename,".gif"))
strcpy(filetype, "image/gif");
else if (strstr(filename,".png"))
strcpy(filetype, "image/png");
else if (strstr(filename,".jpg"))
strcpy(filetype, "image/jpeg");
else
strcpy(filetype, "image/plain");
}
void serve_dynamic(int fd, char *filename, char *cgiargs){
char buf[MAXLINE],*emptylist[] = {NULL};
sprintf(buf,"HTTP/1.0 200 OK\r\n");
write(fd,buf,strlen(buf));
sprintf(buf,"Server: Tiny Web Server\r\n");
write(fd,buf,strlen(buf));
if(fork()==0) {
setenv("QUERY_STRING",cgiargs,1);
dup2(fd,STDOUT_FILENO);
execve(filename,emptylist,environ);
}
wait(NULL);
}
void clienterror(int fd, char*cause, char *errnum, char *shortmsg, char *longmsg){
char buf[MAXLINE], body[100*MAXLINE];
sprintf(body,"<html><title>Tiny Error</title>");
sprintf(body,"%s<body bgcolor=""ffffff"">\r\n",body);
sprintf(body,"%s%s: %s\r\n",body,errnum,shortmsg);
sprintf(body,"%s<p>%s: %s\r\n",body,longmsg,cause);
sprintf(body,"%s<hr><em>The Tiny Web server</em>\r\n",body);
sprintf(buf, "HTTP/1.0 %s %s\r\n",errnum,shortmsg);
write(fd,buf,strlen(buf));
sprintf(buf, "Content-type: text/html\r\n");
write(fd,buf,strlen(buf));
sprintf(buf, "Content-length: %d\r\n\r\n",(int)strlen(body));
write(fd,buf,strlen(buf));
write(fd,body,strlen(body));
}
网友评论