刚接触linux,参考了《深入理解计算机系统》中网络编程的内容,再ubuntu下用C实现了一个简单的web服务器----代码写得比较乱,还不是很熟悉socket编程,可能会有bug。
——
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXLINE 8192
#define MAXLEN 8192
#define IP "127.0.0.1"
#define PORT 8080
#define WebRoot "/home/wjh/myweb"
typedef struct sockaddr SA;
typedef struct sockaddr_in SA_IN;
const int clientNumber=1024;
typedef struct
{
int rio_fd;
int rio_cnt;
char *rio_bufptr;
char rio_buf[MAXLEN];
} rio_t;
//Rio函数
ssize_t rio_readn(int fd,void *buf,size_t n);
ssize_t rio_writen(int fd,void *buf,size_t n);
void rio_readinitb(rio_t *rp,int fd);
ssize_t rio_readlineb(rio_t *rp,void *buf,size_t n);
ssize_t rio_readnb(rio_t *rp,void *buf,size_t n);
//返回错误信息
void showerror(int fd,char *cause,char*errnum,char *shortmsg,char *longmsg);
int main(int argc,char **argv)
{
int listenfd,fd,len;
SA_IN serveraddr,clientaddr;
//建立服务器端socket
if((listenfd=socket(AF_INET,SOCK_STREAM,0))<0){
printf("Fail to create a server socket.\n");
exit(1);
}
//设置IP与PORT
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_addr.s_addr=inet_addr(IP);
serveraddr.sin_port=htons(PORT);
//绑定
if(bind(listenfd,(SA*)&serveraddr,sizeof(serveraddr))<0){
printf("Fail to bind.\n");
exit(1);
}
//监听
if(listen(listenfd,clientNumber)<0){
printf("Fail to listen.\n");
exit(1);
}
//服务器启动完毕
printf("The WJH Web Server is listening on %d.\n",PORT);
len=sizeof(clientaddr);
while(1)
{
//客户端请求连接
fd=accept(listenfd,(SA*)&clientaddr,&len);
//printf("%d\n",fd);
struct stat sbuf; //储存文件属性的结构
char buf[MAXLINE],method[MAXLINE],uri[MAXLINE],version[MAXLINE];
char filename[MAXLINE];
rio_t rio;
//读取请求行
rio_readinitb(&rio,fd);
rio_readlineb(&rio,buf,MAXLINE);
//printf("%s\n",buf);
sscanf(buf,"%s %s %s",method,uri,version);
//输出请求关键信息
printf("%s %s %s\n",method,uri,version);
if(strcasecmp(method,"GET")){
showerror(fd,method,"501","Not Implemented","WJH does not implement this method");
close(fd);
exit(1);
}
rio_readlineb(&rio,buf,MAXLINE);
//打印协议内容
while(strcmp(buf,"\r\n")){
rio_readlineb(&rio,buf,MAXLINE);
printf("%s",buf);
}
//默认路径为WebRoot
strcpy(filename,WebRoot);
strcat(filename,uri);
//如果用户请求中不包含文件名,默认为index.html
if(strcmp(uri,"/")==0){
strcat(filename,"index.html");
}
//printf("%s\n",filename);
if(stat(filename,&sbuf)<0){ //文件是否存在?
showerror(fd,filename,"404","Not found","WJH couldn't find this file");
close(fd);
exit(1);
}
if(!(S_ISREG(sbuf.st_mode))||!(S_IRUSR&sbuf.st_mode)){ //普通可读文件?
showerror(fd,filename,"403","Forbidden","WJH couldn't read the file");
close(fd);
exit(1);
}
int srcfd;
char *srcp,filetype[MAXLINE];
int filesize=sbuf.st_size;
//服务器暂时只实现两text/html类型文件的请求响应
strcpy(filetype,"text/html");
sprintf(buf,"HTTP/1.0 200 OK\r\n");
sprintf(buf,"%sServer: WJH 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);//解除文件与内存的映射关系
close(fd);
}
return 0;
}
ssize_t rio_readn(int fd,void *buf,size_t n)
{
ssize_t nread;
size_t nleft=n;
char *bp=(char *)buf;
while(nleft>0)
{
if((nread=read(fd,bp,nleft))<0)
{
if(errno==EINTR)
nread=0;
else
return -1;
}
else if(nread==0)
break;
nleft-=nread;
bp+=nread;
}
return (n-nleft);
}
ssize_t rio_writen(int fd,void *buf,size_t n)
{
ssize_t nwritten;
size_t nleft=n;
char *bp=(char *)buf;
while(nleft>0)
{
if((nwritten=write(fd,bp,nleft))<=0)
{
if(errno==EINTR)
nwritten=0;
else
return -1;
}
nleft-=nwritten;
bp+=nwritten;
}
return n;
}
void rio_readinitb(rio_t *rp,int fd)
{
rp->rio_fd=fd;
rp->rio_cnt=0;
rp->rio_bufptr=rp->rio_buf;
}
static ssize_t rio_read(rio_t *rp,void *buf,size_t n)
{
ssize_t cnt;
while(rp->rio_cnt<=0)
{
rp->rio_cnt=read(rp->rio_fd,rp->rio_buf,sizeof(rp->rio_buf));
if(rp->rio_cnt<0)
{
if(errno!=EINTR)
return -1;
}
else if(rp->rio_cnt==0)
return 0;
else
rp->rio_bufptr=rp->rio_buf;
}
cnt=n;
if(rp->rio_cnt
cnt=rp->rio_cnt;
memcpy(buf,rp->rio_bufptr,cnt);
rp->rio_cnt-=cnt;
rp->rio_bufptr+=cnt;
return cnt;
}
ssize_t rio_readlineb(rio_t *rp,void *buf,size_t n)
{
size_t i,ret;
char c;
char *bp=(char *)buf;
for(i=1;i
{
if((ret=rio_read(rp,&c,1))==1)
{
*bp++=c;
if(c=='\n')
break;
}
else if(ret==0)
{
if(i==1)
return 0;
else
break;
}
else
return -1;
}
*bp='\0';
return i;
}
ssize_t rio_readnb(rio_t *rp,void *buf,size_t n)
{
ssize_t nread;
size_t nleft=n;
char *bp=(char *)buf;
while(nleft>0)
{
if((nread=rio_read(rp,bp,nleft))<0)
{
if(errno==EINTR)
nread=0;
else
return -1;
}
else if(nread==0)
break;
nleft-=nread;
bp+=nread;
}
return (n-nleft);
}
void showerror(int fd,char *cause,char *errnum,char *shortmsg,char *longmsg)
{
char buf[MAXLINE],body[MAXLINE];
sprintf(body,"WJH Error");
sprintf(body,"%s
\r\n",body);sprintf(body,"%s%s: %s\r\n", body,errnum,shortmsg);
sprintf(body,"%s
%s: %s\r\n",body,longmsg,cause);
sprintf(body,"%s
The WJH Web server\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));
}
编译并运行:
浏览器发出请求: