最近工作中需要客户端和服务器使用https协议进行通信,我负责客户端程序的编写,想到以前在深入理解操作系统中看过web服务器的实现,代码整理如:
#include"apue.h" extern char **environ; void clienterror(int fd,char *cause,char *errnum,char *shortmsg,char *longmsg) { char buf[MAXLINE],body[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); rio_writen(fd,buf,strlen(buf)); sprintf(buf,"Content-type:text/html\r\n"); rio_writen(fd,buf,strlen(buf)); sprintf(buf,"Content-length:%d\r\n\r\n",(int)strlen(body)); rio_writen(fd,buf,strlen(buf)); rio_writen(fd,body,strlen(body)); } void read_requesthdrs(rio_t *rp) { char buf[MAXLINE]; rio_readlineb(rp,buf,MAXLINE); while(strcmp(buf,"\r\n")) { rio_readlineb(rp,buf,MAXLINE); printf("%s",buf); } return; } int parse_uri(char *uri,char *filename,char *cgiargs) { char *ptr; printf("parse_uri(uri):%s\n",uri); if(!strstr(uri,"cgi-bin")) { strcpy(cgiargs,""); strcpy(filename,"."); strcat(filename,uri); if(uri[strlen(uri)-1]=='/') strcat(filename,"home.html"); return 1; } else { ptr=index(uri,'?'); if(ptr) { strcpy(cgiargs,ptr+1); *ptr='\0'; } else strcpy(cgiargs,""); strcpy(filename,"."); strcat(filename,uri); return 0; } } 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,".jpg")) strcpy(filetype,"image/jpeg"); else strcpy(filetype,"text/plain"); } void serve_dynamic(int fd,char *filename,char *caiargs) { char buf[MAXLINE],*emptylist[]={NULL}; sprintf(buf,"HTTP/1.0 200 OK\r\n"); rio_writen(fd,buf,strlen(buf)); sprintf(buf,"Server:Tiny Web Server\r\n"); rio_writen(fd,buf,strlen(buf)); if(fork()==0) { setenv("QUERY_STRING",caiargs,1); dup2(fd,STDOUT_FILENO); execve(filename,emptylist,environ); } wait(NULL); } void serve_static(int fd,char *filename,int filesize) { int srcfd; char *srcp,filetype[MAXLINE],buf[MAXLINE]; get_filetype(filename,filetype); sprintf(buf,"HTTP/1.0 200 OK\r\n"); sprintf(buf,"%sServer:Tiny Web Server\r\n",buf); sprintf(buf,"%sContent-length:%d\r\n",buf,filesize); sprintf(buf,"%sContent-type:%s\r\n\r\n",buf,filetype); rio_writen(fd,buf,strlen(buf)); srcfd=open(filename,O_RDONLY,0); srcp=mmap(0,filesize,PROT_READ,MAP_PRIVATE,srcfd,0); close(srcfd); rio_writen(fd,srcp,filesize); munmap(srcp,filesize); } void doit(int fd) { int is_static; struct stat sbuf; char buf[MAXLINE],method[MAXLINE],uri[MAXLINE],version[MAXLINE]; char filename[MAXLINE],cgiargs[MAXLINE]; rio_t rio; rio_readinitb(&rio,fd); rio_readlineb(&rio,buf,MAXLINE); sscanf(buf,"%s %s %s",method,uri,version); if(strcasecmp(method,"GET")) { clienterror(fd,method,"501","Not implemented","Tiny does not implemented this method"); return; } read_requesthdrs(&rio); 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 could'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 could't run the CGI program"); return; } serve_dynamic(fd,filename,cgiargs); } } int main(int argc,char **argv) { int listenfd,connfd,port,clientlen; struct sockaddr_in clientaddr; if(argc!=2) { fprintf(stderr,"usage:%s <port> \n",argv[0]); exit(0); } port=atoi(argv[1]); listenfd=open_listenfd(port); while(1) { clientlen=sizeof(clientaddr); connfd=accept(listenfd,(SA*)&clientaddr,&clientlen); doit(connfd); close(connfd); } }
我们可以直接使用浏览器测试上面的程序,比如当前服务器程序的目录下面有一个index.html,只要我们在浏览器中输入:
localhost:<port>/index.html就可以请求到index.html
服务器端收到的浏览器请求行如下:
时间: 2024-10-21 01:30:53