Althttpd 源码分析

Althttpd 源码分析 - 主函数


main 主函数

int main(int argc, char** argv)
{
	int i; 							/* 循环次数 */
	char *zPermUser = 0;			/* 守护进程的用户权限 */
	const char *zPort = 0;			/* http server端口 */
	int useChrootJail = 1;			/* 是否更改root目录 */
	struct passwd *pwd = NULL; 		/* 用户信息 */
	
	/* 记录启动时间 */
	gettimeofday(&beginTime, 0);	

	/* 转换命令行参数 */
	while( argc>1 && argv[1][0] == '-')
	{
		char *z = argv[1];						/* Key */
		char *zArg = argc>=3 ? argv[2] : "0";	/* Value */
		/* 允许参数前缀 - 或 -- */
		if(strcmp(z[0],"-") == 0 && strcmp(z[1],"-"))
			z++;
		/* 参数处理 */
		if(strcmp(z, "-user")==0)
		{
			zPermUser = zArg;		/* 守护进程的用户权限 */
		}
		else if(strcmp(z,"-root")==0)
		{
			zRoot = zArg;			/* web根目录 */
		}
		else if (strcmp(z,"-logfile") == 0)
		{
			zLogFile = zArg;		/* 日志文件路径 */
		}
		else if(strcmp(z,"-max-age") == 0)
		{
			mxAge = zArg;			/* 请求的缓存最大保持时间 */
		}
		else if(strcmp(z,"-max-cpu") == 0)
		{
			maxCpu = atoi(zArg);	/* 每个进程最大的CPU时间 */
		}
		else if(strcmp(z,"-https") == 0)
		{
			useHttps = atoi(zArg);	/* 是否使用HTTPS */
			zHttp = useHttps ? "https" : "http";
			if( useHttps )
			{
				/* 请求的源地址,初始为空 */
				zRemoteAddr = getenv("REMOTE_HOST");
			} 
		}
		else if(strcmp(z,"-port") == 0)
		{
			zPort = zArg;			/* 监听端口 */
			standalone =1;			/* 独立模式 */
		}
		else if( strcmp(z, "-family")==0 )
		{
      		if( strcmp(zArg, "ipv4")==0 )
			{
		        ipv4Only = 1;		/* 仅IPv4 */
      		}
			else if( strcmp(zArg, "ipv6")==0 )
			{
    		    ipv6Only = 1;		/* 仅IPv6 */
      		}
			else
			{
				/* 未知IP协议,打印日志 */
        		Malfunction(500, "unknown IP protocol: [%s]\n", zArg);
      		}
	    }
		else if( strcmp(z, "-jail")==0 )
		{
      		if( atoi(zArg)==0 )
			{
        		useChrootJail = 0;	/* 更改root目录,即重新指定/位置 */
      		}
    	}
		else if( strcmp(z, "-debug")==0 )
		{
      		if( atoi(zArg) )
			{
        		useTimeout = 0;		/* 调试模式,是否设置超时 */
      		}
    	}
		else if( strcmp(z, "-input")==0 )
		{
      		if( freopen(zArg, "rb", stdin)==0 || stdin==0 )
			{
				/* 输入文件无法打开 */
        		Malfunction(501, "cannot open --input file \"%s\"\n", zArg);
      		}
    	}
		else if( strcmp(z, "-datetest")==0 )
		{
	    	TestParseRfc822Date();	/* RFC822格式时间戳测试 */
      		exit(0);
   		}
		else
		{
			/* 未知参数 */
     		Malfunction(510, "unknown argument: [%s]\n", z);
    	}
		argv += 2;
		argc -= 2;
 	}

	/* 强制参数检查 */
	if(zRoot == 0)
	{
		if(standalone)
		{
			zRoot = ".";
		}
		else
		{
			Malfunction(520, "no --root specified");
		}
	}

	/* 切换目录 */
	if( chdir(zRoot)!=0 )
	{
    	Malfunction(530,"cannot change to directory [%s]", zRoot);
  	}

	/* 获取用户信息 from /etc/passwd */
	if( zPermUser )
	{
		pwd = getpwnam(zPermUser);
	} 

	/* 基于安全因素,更改当前目录为/根目录 */
	if( zPermUser && useChrootJail && getuid()==0 )
	{
    	if( chroot(".")<0 )
		{
      		Malfunction(540, /* LOG: chroot() failed */
                  "unable to create chroot jail");
    	}
		else
		{
      		zRoot = "";
    	}
  	}

	/* 启动http服务器*/
  	if( zPort && http_server(zPort, 0) )
	{
    	Malfunction(550, "failed to start server");
  	}
    
	/**************************************************************/
	/* http server有连接请求后,fork子进程,子进程退出http server监听 */
	/**************************************************************/

/* 限制进程的资源使用,避免过度导致系统崩溃 */
#ifdef RLIMIT_CPU
  if( maxCpu>0 ){
    struct rlimit rlim;
    rlim.rlim_cur = maxCpu;
    rlim.rlim_max = maxCpu;
    setrlimit(RLIMIT_CPU, &rlim);
  }
#endif

	/* 设置用户身份属性,限制资源权限 */
	if (zPermUser)
	{
		if (pwd)
		{
			if (setgid(pwd->pw_gid))
			{
				Malfunction(560, "cannot set group-id to %d", pwd->pw_gid);
			}
			if (setuid(pwd->pw_uid))
			{
				Malfunction(570, "cannot set user-id to %d", pwd->pw_uid);
			}
		}
		else
		{
			Malfunction(580, "no such user [%s]", zPermUser);
		}
	}
	if (getuid() == 0)
	{
		Malfunction(590, "cannot run as root");
	}

	/* 获取请求的地址信息,主要用于打印日志 */
	if (zRemoteAddr == 0)
	{
		address remoteAddr;
		unsigned int size = sizeof(remoteAddr);
		char zHost[NI_MAXHOST];
		if (getpeername(0, &remoteAddr.sa, &size) >= 0)
		{
			getnameinfo(&remoteAddr.sa, size, zHost, sizeof(zHost), 0, 0, NI_NUMERICHOST);
			zRemoteAddr = StrDup(zHost);
		}
	}
	if (zRemoteAddr != 0 && strncmp(zRemoteAddr, "::ffff:", 7) == 0 && strchr(zRemoteAddr + 7, ':') == 0 && strchr(zRemoteAddr + 7, '.') != 0)
	{
		zRemoteAddr += 7;
	}

	/* 处理请求,最大100次,每次15秒超时 */
	for (i = 0; i < 100; i++)
	{
		ProcessOneRequest(0);
	}
	ProcessOneRequest(1);
	exit(0);

	return 0;
}

http_server函数

int http_server(const char *zPort, int localOnly)
{
    int listener[20];               /* 服务器socket */
    int connection;                 /* socket连接 */
    fd_set readfds;                 /* select的文件描述符 */
    address inaddr;                 /* 远程对端地址 */
    socklen_t lenaddr;              /* inaddr的长度 */
    int child;                      /* 子进程PID */
    int nchildren = 0;              /* 子进程数 */
    struct timeval delay;           /* select超时时间 */
    int opt = -1;                   /* 设置socket复用标志 */
    struct addrinfo sHints;         /* 地址信息,用于提示 */
    struct addrinfo *pAddrs, *p;
    int rc;                         /* 结果返回码 */
    int i, n;
    int maxFd = -1;                 /* 最大文件描述符 */

    memset(&sHints, 0, sizeof(sHints));

    /* 协议族 */
    if(ipv4Only)
    {
        sHints.ai_family = PF_INET;
    }
    else if(ipv6Only)
    {
        sHints.ai_family = PF_INET6;
    }
    else
    {
        sHints.ai_family = PF_UNSPEC;
    }

    sHints.ai_socktype = SOCK_STREAM;
    sHints.ai_flags = AI_PASSIVE;
    sHints.ai_protocol = IPPROTO_IP;

    /* 获取地址信息 */
    rc = getaddrinfo(localOnly ? "localhost": 0,zPort, &sHints, &pAddrs);
    if(rc)
    {
        fprintf(stderr, "could not get addr info: %s", rc!=EAI_SYSTEM ? gai_strerror(rc) : strerror(errno));
        return 1;
    }

    /* MAX(n) = sizeof listener / sizeof int */
    for(n=0, p=pAddrs; n<(int)(sizeof(listener)/sizeof(listener[0])) && p != 0; p=p->ai_next)
    {
        listener[n] = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
        if(listener[n] >= 0)
        {
            /* 复用,防止异常终止导致socket无法正常使用 */
            setsockopt(listener[n], SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

/* IPv6 */        
#if defined(IPV6_V6ONLY)
      if( p->ai_family==AF_INET6 )
      {
        int v6only = 1;
        setsockopt(listener[n], IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only));
      }
#endif
            /* 绑定 */
            if(bind(listener[n], p->ai_addr, p->ai_addrlen) < 0)
            {
                printf("bind failed: %s\n", strerror(errno));
                close(listener[n]);
                continue;
            }
            /* 监听 */
            if( listen(listener[n], 20) < 0 )
            {
                printf("listen() failed: %s\n", strerror(errno));
                close(listener[n]);
                continue;
            }
            n++;
        }
    }

    if(n == 0)
    {
        fprintf(stderr, "cannot open any sockets\n");
        return 1;
    }

    while (1)
    {
        /* 连接短时间过多,子进程创建过快,减慢 */
        if(nchildren > MAX_PARALLEL)
        {
            sleep(nchildren - MAX_PARALLEL);
        }

        delay.tv_sec = 60;
        delay.tv_usec = 0;
        FD_ZERO(&readfds);

        for(i=0; i<n; i++)
        {
            assert(listener[0] >= 0 );
            FD_SET(listener[i], &readfds);  /* 添加监听fd到集合 */
            if(listener[i] > maxFd)
                maxFd = listener[i];
        }
        /* select阻塞,检测是否有请求 */
        select(maxFd+1, &readfds, 0, 0, &delay);

        for(i=0; i<n; i++)
        {
            if(FD_ISSET(listener[i], &readfds))
            {
                lenaddr = sizeof(inaddr);
                /* 建立连接 */
                connection = accept(listener[i], &inaddr.sa, &lenaddr);
                if(connection >= 0)
                {
                    /* fork子进程 */
                    child = fork();
                    if(child != 0)
                    {
                        /* 父进程统计子进程数,关闭连接,该连接由子进程负责 */
                        if(child>0)
                            nchildren++;
                        close(connection);
                    }
                    else
                    {
                        /* 子进程退出http_server,进入ProcessOneRequest处理请求 */
                        int nErr =0, fd;
                        /* 注意区分 标准fclose和系统调用close, 0 1表示输出输入流fd */
                        close(0);
                        fd = dup(connection); /* 复制 */
                        if(fd != 0)
                            nErr++;
                        close(1);
                        fd = dup(connection);
                        if(fd != 1)
                            nErr++;
                        close(connection);
                        return nErr;
                    }
                }
            }

            /* 有子进程终结 */
            while( (child = waitpid(0, 0, WNOHANG))>0 )
            {
                nchildren--;
            }
        }
    }
   /*  此处不会到达 */
    exit(1);
}

总结

  • Althttpd的主要三个函数为main函数、http_server函数和ProcessOneRequest函数。
  • http_server函数负责监听和fork子进程,子进程产生后退出http_server函数并调用ProcessOneRequest函数处理请求,处理请求中我们忽略掉cgi网关部分,就会发现其实是HTTP的组包收发。
  • Althttpd的设计思想别出心裁,对于加深linux的学习很有帮助。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值