Chinaunix首页 | 论坛 | 博客
  • 博客访问: 96812
  • 博文数量: 20
  • 博客积分: 777
  • 博客等级: 军士长
  • 技术积分: 287
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-22 18:53
文章存档

2011年(1)

2009年(19)

我的朋友

分类: LINUX

2009-05-05 17:21:06

服务器端实现平台与工具: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(sockfd<0)
    {
        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(connfd<0)
            {
                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 Moved

Object Moved

This 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[i]=start;
            count++;
            break;
        }
        
        *pos='\0';
        if(*start)
        {
            result[i]=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

阅读(2571) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~