本程序是一個提供特殊服務的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*/