Web Server程序编写学习笔记(四)源代码

        本程序是一个提供特殊服务的Web Server。该程序是一个Daemon程序,用单进程+I/O多路转换(select)的方式接收Http的Get请求,请求中是一个字符串(可以是数字或者字母),返回一个PNG格式的图片,整个过程符合Http 1.0协议。
        本程序类似于网站上常用的生成“验证码”的程序。
        本程序在FreeBSD环境下开发完成,并可同时在FreeBSD和Windows (Cygwin)下编译运行。FreeBSD环境安装有gd-1.8.4库,Cygwin环境安装有libpng-1.2.8和gd-2.0.33库,用于生成PNG图片。
        整个程序用C语言完成,源代码如下:
/***************************************************************************
 * Copyright (C) 2004-2005 XiongBin Xiong  All rights reserved
 * References: Stevens,W.R. 1992. Advanced Programming in the UNIX Environment. Addison-Wesley.
 *                    Stevens,W.R. 1998. UNIX Network Programming Volum1.Prentice Hall PTR
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
#include <stdio.h>
#include <time.h>
#include <syslog.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <gd.h>
#include <gdfontg.h>  /* 定义了"gdFontGiant"字体结构 */

#define MAXCLIENT 1024  /* 数组client的最大值,即此服务器所能处理的最大客户数 */
#define BUFFSIZE 4096

int daemon_init(const char *); /* daemon进程初始化函数 */
int getpng(char *,char *,int); /* png图片生成函数 */
void sendFile(int, char *); /* 发送文件到客户端 */
void log_sys(const char *, ...);
static void log_doit(int, int, const char *, va_list ap);

int
main(int argc, char **argv)
{
 int   i,maxi,maxfd,listenfd,connfd,hfd,nready,client[MAXCLIENT];
 char   *p,*pp, recvbuf[BUFFSIZE];
 ssize_t   recvlen;
 fd_set   rset, allset;
 struct sockaddr_in servaddr;
 
 daemon_init(argv[0]);    /* 成为daemon进程 */
 listenfd=socket(AF_INET, SOCK_STREAM, 0);

 bzero(&servaddr, sizeof(servaddr));  /* 初始化套借口地址结构 */
 servaddr.sin_family      =AF_INET;
 servaddr.sin_addr.s_addr =htonl(INADDR_ANY);
 servaddr.sin_port        =htons(80);

 bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
 listen(listenfd,5);

 maxfd=listenfd;
 maxi=-1;
 for(i=0;i<MAXCLIENT;i++)
  client[i]=-1;  /* 用-1初始化数组client[] */
 FD_ZERO(&allset);
 FD_SET(listenfd, &allset); /* 初始化allset */

 for(;;){
  rset=allset;
  nready=select(maxfd+1, &rset, NULL, NULL, NULL);

  if(FD_ISSET(listenfd, &rset)){  /* 新用户连接 */
   connfd=accept(listenfd,(struct sockaddr *)NULL,NULL);

   for(i=0;i<FD_SETSIZE;i++)
    if(client[i]<0){
     client[i]=connfd; /* 保存描述符 */
     break;
    }
   if(i==MAXCLIENT)  /* 连接过多 */
    log_sys("connection overflow");

   FD_SET(connfd, &allset); /* 将新描述符加入allset中 */
   if(connfd>maxfd)
    maxfd=connfd;  /* 最大描述符的值 */
   if(i>maxi)
    maxi=i;   /* client[]数组的最大元素标号 */
   if(--nready<=0)
    continue;  /* 没有可读的描述符了 */
  }

  for(i=0;i<=maxi;i++){ /* check all clients for data */
   if((hfd=client[i])<0)
    continue;
   if(FD_ISSET(hfd, &rset)){
    if((recvlen=recv(hfd,recvbuf,sizeof(recvbuf),0))==0){
      /* 用户断开连接 */
     close(hfd);
     FD_CLR(hfd, &allset);
     client[i]=-1;
    }else
     if(strncmp(recvbuf,"GET",3)==0){ /* 判断请求

类型是否为GET */
      p=strchr(recvbuf+3,'/'); /* 找请求

中'/'的匹配之处 */
      if(p!=0){
       pp=strstr(recvbuf+3,"HTTP/1."); /*

找请求中"HTTP/1."的匹配之处 */
       if(pp!=0){
        p++;
        pp--;
        *pp='/0'; /* 获取用户

输入的字符或数字串 */
        getpng

("/tmp/pass.png",p,strlen(p)); /* 生成PNG图片 */
        sendFile

(hfd,"/tmp/pass.png"); /* 发送PNG图片给用户 */
        close(hfd);
        FD_CLR(hfd, &allset);
        client[i]=-1;
       }
      }
     }
    if(--nready<=0)
     break;  /* 没有可读的描述符了 */
   }
  }
 }
}

/*-----------------------------------------*/
/* png图片生成函数 */
int
getpng(char *filename,char *string,int strlen)
{
 int back,word,front,len,sx,sy,i;
 char *str;
 gdImagePtr newimg;
 FILE *pngfp;

 str=string;
 len=strlen;
 sx=10+len*9;
 sy=20;
 newimg=gdImageCreate(sx,sy); /* 创建一变量存放空白图像,像素sx*sy */
 pngfp=fopen(filename,"wb");
 back=gdImageColorAllocate(newimg,0,255,128); /* 匹配背景的颜色 */
 word=gdImageColorAllocate(newimg,255,0,128); /* 匹配字符的颜色 */
 front=gdImageColorAllocate(newimg,255,64,128); /* 匹配干扰线的颜色 */
 gdImageFill(newimg,0,0,back);   /* 为图像填充以上颜色 */
 gdImageString(newimg,gdFontGiant,5,1,str,word); /* 生成字符图象 */
 for(i=0;i<len;(i=i+2))    /* 生成干扰线 */
  gdImageLine(newimg,(5+i*9),0,(23+i*9),20,front);
 gdImagePng(newimg,pngfp);   /* 创建PNG图像 */
 gdImageDestroy(newimg);    /* 破坏图像流以释放内存 */

 fclose(pngfp);
 return;
}

/*---------------------------------------------*/
/* 发送文件到客户端 */
void
sendFile(int sock, char *filename)
{
 char pngbuf[65535],clenth[32],*sendDate;
 char sendbuf[8192]="HTTP/1.0 200 OK/nServer: mypngserver (FreeBSD)/nMime-Version:

1.0/nDate: ";
 int n;
 time_t tp;
 FILE *fp;
 fp=fopen(filename,"r");
 n=fread(&pngbuf,sizeof(char),65535,fp); /* 以二进制结构读取PNG图片文件 */

 tp=time(NULL);
 sendDate=ctime(&tp);  /* 生成当前时间 */
 strcat(sendbuf,sendDate);
 strcat(sendbuf,"Content-Type: image/png/nContent-Length: ");
 sprintf(clenth,"%d/n",n); /* 当前PNG图片的大小 */
 strcat(sendbuf,clenth);
 strcat(sendbuf,"Connection: close/n/n");

 send(sock,sendbuf,strlen(sendbuf),0); /* 发送服务器的Respond */
 write(sock,pngbuf,n);   /* 发送PNG图片 */
 fclose(fp);
}

/*-----------------------------------------------------*/
/* daemon进程初始化函数 */
int
daemon_init(const char *pname)
{
 int i;
 pid_t pid;

 if((pid=fork())!=0)
  exit(0);
 setsid();  /* 进程成为会话首进程,不再有控制终端 */
 signal(SIGHUP,SIG_IGN); /* 忽略SIGHUP信号并再次fork */
 if((pid=fork())!=0)
  exit(0);

 chdir("/");  /* 改变工作目录 */
 umask(0);  /* 清文件创建屏蔽字 */

 for(i=0;i<64;i++) /* 关闭所有打开的文件描述符 */
  close(i);
 openlog(pname,LOG_PID,0);
}

/*----------------------------------------------------------*/
/* daemon进程出错处理的两个函数 */
void
log_sys(const char *fmt, ...)
{
 va_list ap;

 va_start(ap,fmt);
 log_doit(1,LOG_ERR,fmt,ap);
 va_end(ap);
 exit(2);
}

static void
log_doit(int errnoflag, int priority, const char *fmt, va_list ap)
{
 int errno_save;
 char buf[BUFFSIZE];

 errno_save=errno;
 vsprintf(buf,fmt,ap);
 if(errnoflag)
  sprintf(buf+strlen(buf),":%s",strerror(errno_save));
 strcat(buf,"/n");
 syslog(priority,buf);
 return;
}
/* end all*/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值