编写简单的WEB服务器

一、背景

    我们都知道使用浏览器访问网址的方法,将网址输入进地址框中就会显示出相应的文字、图片、视频等信息。实际上基于socket的客户/服务器原理是相似的,虽然可能承载信息的载体不同,但其背后的原理是基本一致的。通过理解这些原理(关于socket,我上一篇博客分析过),我们就可以编写一个简单的WEB服务器并做检测。

二、基本步骤

    实际上基于socket的客户/服务器系统的步骤可以简单的概括为3步:

    1、服务器建立服务

    2、客户端连接服务器

    3、客户端与服务器进行数据交换

三、编写socklib.c文件

    这个文件中主要包含了一些常用的函数。便于模块化编程。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<time.h>
#include<strings.h>

#define HOSTLEN 256
#define BACKLOG 1

int make_server_socket_q(int ,int );

int make_server_socket(int portnum)
{
  return make_server_socket_q(portnum,BACKLOG);
}

int make_server_socket_q(int portnum,int backlog)
{
  struct sockaddr_in saddr;
  struct hostent *hp;
  char hostname[HOSTLEN];
  int sock_id;

  sock_id = socket(PF_INET,SOCK_STREAM,0);
  if (sock_id == -1)
    return -1;

  bzero((void *)&saddr,sizeof(saddr));
  gethostname(hostname,HOSTLEN);
  hp = gethostbyname(hostname);

  bcopy((void *)hp->h_addr,(void *)&saddr.sin_addr,hp->h_length);
  saddr.sin_port = htons(portnum);
  saddr.sin_family = AF_INET;
  if(bind(sock_id,(struct sockaddr *)&saddr,sizeof(saddr)) != 0)
    return -1;

  if(listen(sock_id,backlog) != 0)
  return -1;
  return sock_id;
}

int connect_to_server(char *host,int portnum)
{
  int sock;
  struct sockaddr_in servadd;
  struct hostent *hp;

  sock = socket(AF_INET,SOCK_STREAM,0);
  if(sock == -1)
    return -1;

  bzero(&servadd,sizeof(servadd));
  hp = gethostbyname(host);
  if(hp == NULL)
    return -1;
  bcopy(hp->h_addr,(struct sockaddr *)&servadd.sin_addr,hp->h_length);
  servadd.sin_port = htons(portnum);
  servadd.sin_family = AF_INET;

  if(connect(sock,(struct sockaddr *)&servadd,sizeof(servadd)) != 0)
    return -1;
  return sock;
}


make_server_socket函数返回一个服务器的socket。

connect_to_server函数返回一个连接后的socket。

四、编写WEB服务器

#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>

int main(int ac,char *av[])
{
  int sock,fd;
  FILE *fpin;
  char request[BUFSIZ];

  if(ac == 1){
    fprintf(stderr,"usage:ws portnum\n");
    exit(1);
  }
  sock = make_server_socket(atoi(av[1]));
  if(sock == -1)
    exit(2);

  while(1){
    fd = accept(sock,NULL,NULL);
    fpin = fdopen(fd,"r");

    fgets(request,BUFSIZ,fpin);
    printf("got a call:request = %s",request);
    read_til_crnl(fpin);

    process_rq(request,fd);

    fclose(fpin);
  }
}

read_til_crnl(FILE *fp)
{
  char buf[BUFSIZ];
  while(fgets(buf,BUFSIZ,fp) != NULL && strcmp(buf,"\r\n") != 0);
}

process_rq(char *rq,int fd)
{
  char cmd[BUFSIZ],arg[BUFSIZ];

  if(fork() != 0)
    return ;
  strcpy(arg,"./");
  if(sscanf(rq,"%s %s",cmd,arg+2) != 2)
    return ;

  if(strcmp(cmd,"GET") != 0)
    cannot_do(fd);
  else if(not_exist(arg))
    do_404(arg,fd);
  else if(isadir(arg))
    do_ls(arg,fd);
  else if(ends_in_cgi(arg))
    do_exec(arg,fd);
  else
    do_cat(arg,fd);
}

header(FILE *fp,char *content_type)
{
  fprintf(fp,"HTTP/1.0 200 OK\r\n");
  if(content_type)
    fprintf(fp,"Content-type:%s\r\n",content_type);
}

cannot_do(int fd)
{
  FILE *fp = fdopen(fd,"w");

  fprintf(fp,"HTTP/1.0 501 Not Implemented\r\n");
  fprintf(fp,"Content-type:text/plain\r\n");
  fprintf(fp,"\r\n");

  fprintf(fp,"That command is not yet implemented\r\n");
  fclose(fp);
}

do_404(char *item,int fd)
{
  FILE *fp = fdopen(fd,"w");

  fprintf(fp,"HTTP/1.0 404 Not Found\r\n");
  fprintf(fp,"Content-type:text/plain\r\n");
  fprintf(fp,"\r\n");

  fprintf(fp,"The item you requested: %s\r\nis not found\r\n",item);
  fclose(fp);
}

isadir(char *f)
{
  struct stat info;
  return(stat(f,&info) != -1 && S_ISDIR(info.st_mode));
}

not_exist(char *f)
{
  struct stat info;
  return(stat(f,&info) == -1);
}

do_ls(char *dir,int fd)
{
  FILE *fp;

  fp = fdopen(fd,"w");
  header(fp,"text/plain");
  fprintf(fp,"\r\n");
  fflush(fp);

  dup2(fd,1);
  dup2(fd,2);
  close(fd);
  execlp("ls","ls","-l",dir,NULL);
  perror(dir);
  exit(1);
}

char *file_type(char *f)
{
  char *cp;
  if((cp = strrchr(f,'.')) != NULL)
    return cp+1;
  return "";
}

ends_in_cgi(char *f)
{
  return (strcmp(file_type(f),"cgi") == 0);
}

do_exec(char *prog,int fd)
{
  FILE *fp;

  fp = fdopen(fd,"w");
  header(fp,NULL);
  fflush(fp);
  dup2(fd,1);
  dup2(fd,2);
  close(fd);
  execl(prog,prog,NULL);
  perror(prog);
}
 
do_cat(char *f,int fd)
{
  char *extension = file_type(f);
  char *content = "text/plain";
  FILE *fpsock,*fpfile;
  int c;

  if(strcmp(extension,"html") == 0)
    content = "text/html";
  else if(strcmp(extension,"gif") == 0)
    content = "image/gif";
  else if(strcmp(extension,"jpg") == 0)
    content = "image/jpeg";
 
  fpsock = fdopen(fd,"w");
  fpfile = fopen(f,"r");
  if(fpsock != NULL && fpfile != NULL)
  {
    header(fpsock,content);
    fprintf(fpsock,"\r\n");
    while((c = getc(fpfile)) != EOF)
      putc(c,fpsock);
    fclose(fpfile);
    fclose(fpsock);
  }
  exit(0);
}
五、测试WEB服务器

使用gcc webserv.c socklib.c -o webserv进行编译

接着运行webserv,后面跟端口号(12345)

在浏览器中输入你计算机的主机名:12345

然后后面加上你需要打开的文件或者需要运行的文件

六、总结

    WEB服务器与一般的时间查询服务器原理是相通的,都是基于socket的。这里只是简单的编写了WEB服务器。后续还有许多需要接着学习。


  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值