网络服务器【一】多进程Httpserver

流程图

在这里插入图片描述

关键socket

	// socket()
    sock_id=socket(PF_INET,SOCK_STREAM,0);
    if(sock_id==-1)
    {
        return -1;
    }
   // 初始化地址 sockaddr_in
	bzero(&saddr,sizeof(saddr));
    gethostname(hostname,HOSTLEN);
    printf("hostname:%s\n",hostname);
    hp=gethostbyname(hostname);
    bcopy((void*)hp->h_addr,(void*)&saddr.sin_addr,hp->h_length);
    saddr.sin_port=htons(portnum);
    saddr.sin_family=AF_INET;
	// bind()
    if(bind(sock_id,(struct sockaddr*)&saddr,sizeof(saddr))!=0)
    {
        return -1;
    }
 	// listen()
    if(listen(sock_id,backlog)!=0)
    {
        return -1;
    }
	// accept()
     fd=accept(sock,NULL,NULL);

文件描述符转文件指针(带缓冲区),读取

    // 文件描述符转文件指针(带缓冲区),读取
    fpin=fdopen(fd,"r");
    // 读取
    fgets(request,BUFSIZ,fpin);
    printf("got a call: request = %s",request);
    read_til_crnl(fpin);
// 读清
void read_til_crnl(FILE* fp)
{
    char buf[BUFSIZ];
    // 读取不为空,不等于“\r\n”  ->  一直读下去
    while (fgets(buf,BUFSIZ,fp)!=NULL&&strcmp(buf,"\r\n")!=0);
}

文件属性

int isadir(char* f)
{
    struct stat info;
    //stat() 得到文件(目录文件是一种特殊文件)属性
    return(stat(f,&info)!=-1&&S_ISDIR(info.st_mode));
}

io重定向,execlp()

int do_ls(char *dir,int fd)
{
    FILE* fp;
    
    fp=fdopen(fd,"w");
    header(fp,"text/plain");
    fprintf(fp,"\r\n");
    fflush(fp);
    // stdout和stderror定向到fd
    dup2(fd,1);
    dup2(fd,2);
    close(fd);
    // execlp() 运行程序输出到fd 
    execlp("ls","ls","-1",dir,NULL);
    perror(dir);
    exit(1);
}

strrchr()

char* file_type(char* f)
{
    char *cp;
    // 在参数 str 所指向的字符串中搜索最后一次出现字符 c(一个无符号字符)的位置
    // a.txt 返回txt
    if((cp=strrchr(f,'.'))!=NULL)
    {
        return cp+1;
    }
    return "";
}

源码

// socklib.h
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <time.h>
#include <string.h>

#define HOSTLEN 256
#define BACKLOG 1

int make_server_socket_q(int ,int);
int make_server_socket(int portnum)
{
    return make_server_socket_q(portnum,BACKLOG);
}
int make_server_socket_q(int portnum,int backlog)
{
    struct sockaddr_in saddr;
    struct hostent* hp;
    char hostname[HOSTLEN];
    int sock_id;
    // socket()
    sock_id=socket(PF_INET,SOCK_STREAM,0);
    if(sock_id==-1)
    {
        return -1;
    }
    // init a sockaddr_in struct
    bzero(&saddr,sizeof(saddr));
    gethostname(hostname,HOSTLEN);
    printf("hostname:%s\n",hostname);
    hp=gethostbyname(hostname);
    bcopy((void*)hp->h_addr,(void*)&saddr.sin_addr,hp->h_length);
    saddr.sin_port=htons(portnum);
    saddr.sin_family=AF_INET;

    // bind()
    if(bind(sock_id,(struct sockaddr*)&saddr,sizeof(saddr))!=0)
    {
        return -1;
    }
    // listen()
    if(listen(sock_id,backlog)!=0)
    {
        return -1;
    }
    return sock_id;

}
int connect_to_server(char* host,int portnum)
{
    int sock;
    struct sockaddr_in servadd;
    struct hostent* hp;

    sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock==-1)
    {
        return -1;
    }
    bzero(&servadd,sizeof(servadd));
    hp=gethostbyname(host);
    if(hp==NULL)
    {
        return -1;
    }
    bcopy(hp->h_addr,(struct sockaddr*)&servadd.sin_addr,hp->h_length);
    servadd.sin_port=htons(portnum);
    servadd.sin_family=AF_INET;

    if(connect(sock,(struct sockaddr*)&servadd,sizeof(servadd))!=0)
    {
        return -1;
    }
    return sock;
}
// webserv.c
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<stdlib.h>
#include<sys/socket.h>
#include"socklib.h"

// 读清
void read_til_crnl(FILE* fp)
{
    char buf[BUFSIZ];
    // 读取不为空,不等于“\r\n”  ->  一直读下去
    while (fgets(buf,BUFSIZ,fp)!=NULL&&strcmp(buf,"\r\n")!=0);
}

void header(FILE* fp,char* content_type)
{
    fprintf(fp,"HTTP/1.0 200 OK\r\n");
    if(content_type)
    {
        fprintf(fp,"Content-type:%s\r\n",content_type);
    }
}

void cannot_do(int fd)
{
    FILE* fp=fdopen(fd,"w");
    fprintf(fp,"HTTP/1.0 501 Not Implemented\r\n");
    fprintf(fp,"Content-type:text/plain\r\n");
    fprintf(fp,"\r\n");

    fprintf(fp,"That command is not yet implemented\r\n");
    fclose(fp);

}
void do_404(char* item,int fd)
{
    FILE* fp=fdopen(fd,"w");

    fprintf(fp,"HTTP/1.0 404 Not Found\r\n");
    fprintf(fp,"Content-type:text/plain\r\n");
    fprintf(fp,"\r\n");

    fprintf(fp,"The item you requested:%s\r\n is not found\r\n",item);
    fclose(fp);
}
int isadir(char* f)
{
    struct stat info;
    //stat() 得到文件(目录文件是一种特殊文件)属性
    return(stat(f,&info)!=-1&&S_ISDIR(info.st_mode));
}
int not_exist(char* f)
{
    struct stat info;
    return(stat(f,&info)==-1);
}

int do_ls(char *dir,int fd)
{
    FILE* fp;
    
    fp=fdopen(fd,"w");
    header(fp,"text/plain");
    fprintf(fp,"\r\n");
    fflush(fp);
    // stdout和stderror定向到fd
    dup2(fd,1);
    dup2(fd,2);
    close(fd);
    // execlp() 运行程序输出到fd 
    execlp("ls","ls","-1",dir,NULL);
    perror(dir);
    exit(1);
}
char* file_type(char* f)
{
    char *cp;
    // 在参数 str 所指向的字符串中搜索最后一次出现字符 c(一个无符号字符)的位置
    // a.txt 返回txt
    if((cp=strrchr(f,'.'))!=NULL)
    {
        return cp+1;
    }
    return "";
}
int ends_in_cgi(char* f)
{
    return (strcmp(file_type(f),"cgi")==0);
}

void do_exec(char* prog,int fd)
{
    printf("do_exec %s\n",prog);
    FILE* fp;
    fp=fdopen(fd,"w");
    // in header
    header(fp,NULL);
    fflush(fp);
    // stdout stderr  ->   fd
    dup2(fd,1);
    dup2(fd,2);
    close(fd);
    execlp(prog,prog,NULL);
    perror(prog);

}
int do_cat(char* f,int fd)
{
    char* extension=file_type(f);
    char* content="text/plain";
    FILE* fpsock, *fpfile;
    int c;
    if(strcpy(extension,"html")==0)
    {
        content="text/html";
    }else if(strcmp(extension,"gif")==0)
    {
        content="image/gif";
    }else if(strcmp(extension,"jpg")==0)
    {
        content="image/jpg";
    }else if(strcmp(extension,"jpeg")==0)
    {
        content="image/jpeg";
    }
    fpsock=fdopen(fd,"w");
    fpfile=fopen(f,"r");
    if(fpsock!=NULL&&fpfile!=NULL)
    {
        header(fpsock,content);
        fprintf(fpsock,"\r\n");

        while ((c=getc(fpfile))!=EOF)
        {
            putc(c,fpsock);
        }
        fclose(fpfile);
        fclose(fpsock);

    }
    exit(0);
}

void process_rq(char *rq,int fd)
{
    char cmd[BUFSIZ],arg[BUFSIZ];
    // fork()
    if(fork()!=0)
    {
        // 副线程返回
        return;
    }

    // 当前地址
    strcpy(arg,"./");
    // 分解request
    if(sscanf(rq,"%s%s",cmd,arg+2)!=2)
    {
        return;
    }
    
    // strcmp
    if(strcmp(cmd,"GET")!=0)
    {
        cannot_do(fd);

    }else if(not_exist(arg))
    {
        do_404(arg,fd);

    }else if(isadir(arg))
    {
        do_ls(arg,fd);
    }else if(ends_in_cgi(arg))
    {
        do_exec(arg,fd);
    }else{
        do_cat(arg,fd);
    }
}
int main(int ac,char* av[])
{
    printf("start\n");
    int sock,fd;
    FILE* fpin;
    char request[BUFSIZ];
    if(ac==1)
    {
        fprintf(stderr,"usage:ws portnum\n");
        exit(1);
    }
    sock=make_server_socket(atoi(av[1]));
    if(sock==-1)
    {
        exit(2);
    }
    while(1)
    {
        // accept()
        fd=accept(sock,NULL,NULL);

        // 文件描述符转文件指针(带缓冲区),读取
        fpin=fdopen(fd,"r");
        // 读取
        fgets(request,BUFSIZ,fpin);
        printf("got a call: request = %s",request);
        read_til_crnl(fpin);
        
        process_rq(request,fd);

        //父线程关闭文件指针
        fclose(fpin);
    }
    return 0;
}

问题一

子进程会成为僵尸进程

  1. 父进程wait()或waitpid(),会导致父进程挂起
  2. signal函数为SIGCHLD安装handler,handler中wait
  3. signal(SIGCHLD,SIG_IGN)通知内核对子进程结束不感兴趣
  4. fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做。

问题二

accpet 得到的fd未做处理

当fd不可写时关闭,避免文件描述符不够用,导致新连接无法生成。

参考文献

《Unix-Linux编程实践教程》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值