- 论坛徽章:
- 0
|
服务器端实现平台与工具:Redhat9, vim-6.1.320, gcc-3.2.2
客服端测试平台:windows xp , IE6.0
测试过程:在redhat9下运行服务器,在windows下用IE6.0文件的进行访问
web服务器是基于tcp/ip协议的面向连接的一种请求/应答模式的服务其工作原理与流程图可以到网上参考均有详细介绍:本程序大概分为3个模块:
模块一:
主函数模块,在主函数中每有一次连接请求便创建一个子进程用于应答client的请求,并发送数据,数据发送完成进程关闭,父进程用于侦听80端口。主函数模块如下:
//================================================================
//文件名称:main.c
//功能描述:简单的实现webserver功能
//包含头文件: senddata.h
//维护日期:
//=================================================================
#include stdio.h>
#include stdlib.h>
#include string.h>
#include unistd.h>
#include sys/socket.h>
#include netinet/in.h>
#include arpa/inet.h>
#include fcntl.h>
#include "senddata.h"
//==================================================================
//函数名称: main
//功能描述: 主程序创建2个进程,父进程侦听80端口,子进程处理数据发送,
// 实现多进程的并发web服务器。
//入口参数:无
//出口参数: 无
//==================================================================
int main(int argc,char *argv[])
{
char recvbuf[2048];
int sockfd;
struct sockaddr_in servAddr;
unsigned short port=80;
char mycmd[20];
char client_info[50];
if(argc>1)
{
port=atoi(argv[1]);
}
printf("tcp server start at port %d\n",port);
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd0)
{
perror("Invalid socket\n");
exit(1);
}
//=======================================服务器地址初始化
bzero(&servAddr,sizeof(servAddr));
servAddr.sin_family=AF_INET;
servAddr.sin_port=htons(port);
servAddr.sin_addr.s_addr=htonl(INADDR_ANY); //设置本机的IP地址作为服务器地址
printf("Binding server to port %d\n",port);
//==========ip绑定
if(bind(sockfd,(struct sockaddr*)&servAddr,sizeof(struct sockaddr))!=0)
{
close(sockfd);
perror("binding err!\n");
exit(1);
}
int listenfd;
//======开始侦听
if((listenfd=listen(sockfd,1))!=0)
{
close(sockfd);
perror("listen err!\n");
exit(1);
}
printf("waiting client...\n");
//-----接收连接,没有连接accept则阻塞
char cliIp[INET_ADDRSTRLEN];
//size_t recvLen;
struct sockaddr_in cliAddr;
size_t cliAddrLen=sizeof(cliAddr);
while(1)
{
int connfd=accept(sockfd,(struct sockaddr*)&cliAddr,&cliAddrLen);
if(connfd0)
{
close(sockfd);
perror("accept err!\n");
exit(1);
}
//--------子进程处理client端的连接------------
int pid_child;
printf("begin fork()\n");
if((pid_child=fork())==0)
{
printf("in child \n");
close(sockfd);
inet_ntop(AF_INET,&cliAddr.sin_addr.s_addr,cliIp,INET_ADDRSTRLEN);
printf("client ip=%s\n",cliIp);
//
get_client_request(connfd,client_info);//得到客服端请求的信息
printf("in 80 line client_info=%s\n",client_info);
send_data(connfd,client_info);//向服务器发送数据文件
memset(client_info,0,sizeof(client_info));
//----------
close(connfd);
printf("client closed\n");
exit(0);
printf("client closed\n");
}
//---------父进程
close(connfd);
printf("before wait pid_child process\n");
//waitpid(pid_child,NULL,0);
printf("after wait pid_child process\n");
}
close(sockfd);
return 0;
}
模块二:数据分析和发送模块
数据发送模块code实现,报文头信息的分析和数据发送
//==========================================================
//文件名称:senddata.c
//功能描述:实现客服端信息的预处理,与数据发送
//包含文件:senddata.h
//维护日期:
//===========================================================
#include "senddata.h"
char request_file[100];
//===================================================================================
//函数名称:get_client_request(int connfd,char *request)
//功能描述:读取client段的请求报文,并获得报文的第一行
//函数参数:connfd 接收到的client端的socket文件描述符,request 保存报文第一行的指针变量
//返回值: 无
//=====================================================================================
void get_client_request(int connfd,char *request)
{
char recvbuf[4096];
char *pos;
memset(recvbuf,0,sizeof(recvbuf));
if(read(connfd,recvbuf,sizeof(recvbuf))=0)
{
exit(0);
}
if((pos=strstr(recvbuf,"\n"))!=NULL)
{
*pos='\0';
}
strcpy(request,recvbuf);
}
//============================================================================
//函数名称:file_is_found
//功能描述:查找当前目录下,文件是否为请求文件,此函数为ftw函数服务的被动型函数
//函数参数:cur_path ftw函数存放当前路径的指针变量,file_stat ftw存放文件信息的
// 文件状态结构体指针,falg ftw存放文件类型的整形变量
//返回值: int找到文件返回1,没找到返回0
//===========================================================================
int file_is_found(char *cur_path,struct stat *file_stat,int flag)
{
if(flag==FTW_F)
{
if(strcmp( cur_path+(strlen(cur_path)-strlen(request_file)),request_file)==0 )
{
//printf("in 20 file=%s\n",cur_path);
strcpy(request_file,cur_path);
return 1;
}
}
return 0;
}
//===================================================================
//函数名称:serv_resource_find
//功能描述:遍历server当前路径下的所有目录,寻找client请求的资源文件,
// 并将文件大小保存到指针变量file_size中
//函数参数:serv_resource_path资源搜索路径指针,保存文件大小的指针变量
//返回值: int,找到请求文件返回1,没找到返回0;
//======================================================================
int serv_resource_find(char *serv_resource_path,off_t *file_size)
{
int ret=0;
struct stat file_stat;
ret=ftw(serv_resource_path,(int (*)())file_is_found,2);//遍历目录树,参考unix函数手册
if(ret==1)
{
*file_size=file_stat.st_size;
return 1;
}
else
{
return 0;
}
}
//====================================================================================
//函数名称:send_data
//功能描述:根据client_info的内容分析报文格式,并发送数据
//函数参数:connfd 已连接到的客服端socket文件描述符,client_info报文第一行内容指针变量
//返回值: 无
//====================================================================================
void send_data(int connfd,char *client_info)
{
char *resp_head = "HTTP/1.1 200 OK\r\n" \
"Content-Type: text/html\r\n" \
"Content-Length:%ld\r\n" \
"\r\n";
char *not_found = "HTTP/1.1 404 Not Found\r\n" \
"Content-Type: text/html\r\n" \
"Content-Length: 40\r\n" \
"\r\n" \
"File not found";
char *bad_request = "HTTP/1.1 400 Bad Request\r\n" \
"Content-Type: text/html\r\n" \
"Content-Length: 39\r\n" \
"\r\n" \
"Bad Request (Invalid Hostname)";
char *moved_permanently =
"HTTP/1.1 301 Moved Permanently\r\n"\
"Content-Length: 147\r\n" \
"Content-Type: text/html\r\n" \
"Location: %s\r\n" \
"\r\n" \
"Document MovedObject MovedThis document may be found here";
char *cli_info_arg[5];
char temp[30];
char buf[2048];
char respbuf[1024];
int cli_fd;
unsigned long int file_lenth=0;
memset(temp,0,sizeof(temp));
memset(request_file,0,sizeof(request_file));
splitinfo(client_info," ",cli_info_arg);//分割信息得到客户端要访问的文件名
strcpy(request_file,cli_info_arg[1]);
strcpy(temp,cli_info_arg[1]);
temp[0]='\0';
if(!strcmp(request_file,"/"))
{
strcpy(request_file,"index.htm");
}
else //去掉文件前的"/"
{
strcpy(request_file,temp+1);
}
//==========判断当前路径是否有client要访问的文件
//if(access(cli_file,F_OK)==0)
off_t file_size;
//if(serv_resource_find(cli_file,&file_size))
if(serv_resource_find("./",&file_size))
{
//char *fp=cli_file;
//struct stat request_file;
//stat(fp,&request_file);
//-------发送应答报头
//sprintf(respbuf,resp_head,request_file.st_size);
sprintf(respbuf,resp_head,file_size);
write(connfd,respbuf,strlen(respbuf));
//-------用fprintf类的函数式,因为f*类函数是带有缓冲,所以必须用fflush才能使数据从缓冲区输出。
//FILE *newfp = fdopen(connfd, "w");//相当于把整数文件描述符,转换为文件指针
//fprintf(newfp, resp_head, len);
//fflush(newfp);
memset(buf,0,sizeof(buf));
if((cli_fd=open(request_file,O_RDONLY))0)
{
perror("can not open file in send_data()\n");
exit(0);
}
int len;
while((len = read(cli_fd,buf,sizeof(buf)))>0)
{
//printf("%s",buf);
write(connfd,buf,len);
memset(buf,0,sizeof(buf));
}
close(cli_fd);
}
else
{
printf("in is not!\n");
write(connfd,not_found,strlen(not_found));
}
}
模块三:
字符串处理模块:将一个被字符串split,按分割符sour,分割成段,每段首地址存放于result
//=================================================================================
//文件名称:splitinfo.c
//功能描述:按照sour的内容作为分割符实现字符分割
//包含文件:splitinfo.h
//维护日期:
//=================================================================================
#include "splitinfo.h"
//=================================================================================
//函数名称:splitinfo
//功能描述:将一个被字符串split,按分割符sour,分割成段,每段首地址存放于指针数组result
//函数参数:split被分割字符串的指针变量,sour分割符指针变量,result分割结果存放的指针数组
//返回值: int 分割的段数;
//==================================================================================
int splitinfo(char *split,const char *sour,char *result[])
{
char *pos,*start;
int i=0,count=0;
pos=start=split;
while(pos!=NULL)
{
pos=strstr(start,sour);//ls |
if(pos==NULL)
{
result=start;
count++;
break;
}
*pos='\0';
if(*start)
{
result=start;
i++;
count++;
}
start=pos+strlen(sour);
}
result[count]=NULL;
return count;
}
几个头文件senddata.h:
#ifndef __senddata_h_
#define __senddata_h_
#include unistd.h>
#include sys/types.h>
#include sys/stat.h>
#include fcntl.h>
#include ftw.h>
#include "splitinfo.h"
#endif
splitinfo.h
#ifndef __splitinfo_h_
#define __splitinfo_h_
#include stdio.h>
#include stdlib.h>
#include string.h>
extern int splitinfo(char *split,const char *sour,char *result[]);
#endif
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/94835/showart_1918923.html |
|