简易HTTP服务器(多进程版本)

来源:互联网 发布:怎么设计数据库的表 编辑:程序博客网 时间:2024/05/16 02:03

入职公司的试用期小内容,这个是多进程的版本,其中支持了图片,网页,大致结构比较清晰,拿出来跟大家分享下。

server.c源码如下:

#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <assert.h>#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <sys/stat.h>#define SERVER_NAME "IAMCAP-SERVER"#define PROTOCOL    "HTTP/1.1"#define REQUESTLEN 4096#define RESPONSELEN  1024000#define TIMELEN 55#define TEMPLEN 100#define BUFFERLEN 1024#define HOMEPATH  "/Users/iamcap/http/" static char* add_header(int , char *, char *, off_t , char *, int );void parsedata(char*,int);int main(int argc, char **argv){        if(argc <=2){                printf("usage: %s ip_address port_number\n", basename(argv[0]));                return 1;        }        const char *ip = argv[1];        int port = atoi(argv[2]);        //初始化通信结构体        struct sockaddr_in address;        bzero(&address, sizeof(address));        address.sin_family = AF_INET;        inet_pton(AF_INET, ip, &address.sin_addr);        address.sin_port = htons(port);        //端口重复利用        int sock = socket(PF_INET, SOCK_STREAM, 0);        assert( sock >= 0);        int opt = 1;        setsockopt(sock, SOL_SOCKET,SO_REUSEADDR, &opt, sizeof(opt));          int ret = bind(sock, (struct sockaddr*)&address, sizeof(address));        assert(ret != -1);        printf("listening ...\n");        ret = listen(sock, 5);        assert(ret != -1);                         while(1){            //保存客户端的信息            struct sockaddr_in client;                  socklen_t client_addrlength = sizeof(client);             int connectfd = accept(sock,(struct sockaddr*)&client,&client_addrlength);            if(connectfd < 0){                  printf("errno is: %d\n", errno);            }else{                  //创建worker进程                  pid_t pid = fork();                    if(-1 == pid){                        printf("wrong!!!\n");                  }else if(0 == pid){                        //在子进程中                        char remote[INET_ADDRSTRLEN];                        memset(remote,'\0',INET_ADDRSTRLEN);                        printf("\nconnected with ip: %s and port: %d\n", inet_ntoa(client.sin_addr),ntohs(client.sin_port));                        char recvdata[REQUESTLEN];                                                          memset(recvdata,'\0',REQUESTLEN);                        //从建立的socket中接收浏览器的请求信息                        int ret = recv(connectfd, recvdata,REQUESTLEN-1, 0);                        printf("\n%s\n",recvdata);                        //处理接收到的信息                        parsedata(recvdata,connectfd);                                                    }else{                        //在父进程中                        close(connectfd);                   }             }        }        close(sock);        return 0;}// send header to the  browser  static char* add_header(int status, char *title, char *mime_type, off_t length, char *buffer , int buffersize) {      char temp[TEMPLEN];      memset(buffer, 0, sizeof(buffersize));   sprintf(temp, "%s %d %s\r\n", PROTOCOL, status, title);   //将所有的头部数据格式都按照协议的标准填充到buffer   strcat(buffer, temp);     sprintf(temp, "Server: %s\r\n", SERVER_NAME);   strcat(buffer, temp);   sprintf(temp, "Connection: Keep-Alive\r\n");   strcat(buffer, temp);   sprintf(temp, "Content-Type: %s\r\n", mime_type);   strcat(buffer, temp);   sprintf(temp, "Content-Length: %d\r\n", (int)length);   strcat(buffer, temp);   sprintf(temp, "Content-Language: zh-CN\r\n");   strcat(buffer, temp);   strcat(buffer, "\r\n");   return buffer; }static char* get_mime_type(char *filename){  char *postfix;   postfix = strrchr(filename, '.');   if (!strcasecmp(postfix, ".html") || !strcasecmp(postfix, ".htm")){     return "text/html; charset=UTF-8";   }else if (!strcasecmp(postfix, ".jpg") || !strcasecmp(postfix, ".jpeg")){     return "image/jpeg";   }else{     return "text/plain; charset=UTF-8";   }}void parsedata(char *recvdata, int connectfd){    //切换到主页目录    if(chdir(HOMEPATH)<0){        printf("chdir error\n");        exit(0);    }    char method[BUFFERLEN];    char path[BUFFERLEN];    char protocol[BUFFERLEN];    memset(method,'\0',BUFFERLEN);    memset(path,'\0',BUFFERLEN);    memset(protocol,'\0',BUFFERLEN);    sscanf(recvdata, "%[^ ] %[^ ] %[^ ]", method, path, protocol);    //打开文件或文件夹    struct stat property;    stat(path,&property);    if(S_ISDIR(property.st_mode)){        //如果是文件夹                if(0 == strcmp(path,"/")){ //是主页目录            //返回index.html            FILE *fp;            if(fp = fopen("index.html","r")){                char head[BUFFERLEN];                memset(head,'\0',BUFFERLEN);                stat(path+1,&property);                stat("index.html",&property);                add_header(200, "OK", "text/html", property.st_size, head, BUFFERLEN);                char temp[RESPONSELEN];                memset(temp,'\0',RESPONSELEN);                strcpy(temp,head);                fread(temp+strlen(head),1,property.st_size,fp);                send(connectfd, temp, strlen(temp),0);                //将应答的html报文回发过去,浏览器可能还会发送图片显示请求报文                close(connectfd);            }        }else{                    //其他目录,直接返回            exit(0);        }    }else{                               //如果是文件        if(0 == strcasecmp(method,"GET")){            FILE *fp;            if(fp = fopen(path+1,"rb")){                char head[BUFFERLEN];                memset(head,'\0',BUFFERLEN);                stat(path+1,&property);                add_header(200, "OK", get_mime_type((path + 1)), property.st_size, head, BUFFERLEN);                char temp[RESPONSELEN];                memset(temp,'\0',RESPONSELEN);                strcpy(temp,head);                fread(temp+strlen(head),1,property.st_size,fp);                send(connectfd, temp, strlen(head)+property.st_size,0);                close(connectfd);            }        }else if(0 == strcasecmp(method,"POST")){        }else if(0 == strcasecmp(method,"HEAD")){        }else if(0 == strcasecmp(method,"PUT")){        }else{            exit(0);        }    }}

index.html代码如下:

<html><body><H1>this is a page</H1><img src="mypic.jpg" /></body></html>

mypic.jpg 是 /Users/iamcap/http/路径下的一张图片。


这里需要注意的有以下几点:

           1. http的本身就是一个文本文件,你需要知道http协议的格式,可以http://blog.csdn.net/gueter/article/details/1524447

           2. 在显示图片的处理过程中,记得用fread函数,不要企图使用strlen函数来获取长度,可以借助struct  stat { } 和 HTTP头部共同拼接出所发内容的长度

           3. 浏览器在浏览的过程中,可能会发送GET请求favicon.ico,这个其实就是页面显示的那个小图标,可以不用管。

           4. 在使用数组之前,尽量用memset进行初始化,免得垃圾收据会影响内容发送。

0 0
原创粉丝点击