免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1207 | 回复: 0
打印 上一主题 下一主题

linux下基于tcp/ip的多进程并发web服务器及文件浏览的实现 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-05-05 17:21 |只看该作者 |倒序浏览
服务器端实现平台与工具: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
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP