freeeyes 发表于 2014-4-23 13:10:44

类似ngnix的多进程监听用例

多进程监听适合于短连接,且连接间无交集的应用。
前两天简单写了一个,在这里保存一下。
#include <sys/types.h>
#include <stdarg.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>

char * ToDateStr(time_t tt, char *szDateTime, const char *szFormat)
{
        size_t i, len;
        char field, c1 = ' ', c2 = '-', c3 = ':', c4 = '/'; /* 常见的分隔符 */
        char *p;
        struct tm result;

        if (szDateTime == NULL)
        {
                return NULL;
        }
       
        localtime_r(&tt, &result);

        /* 默认的格式 yyyy-mm-dd hh:mi:ss*/
        if (szFormat == NULL)
        {
                sprintf(szDateTime, "%04d-%02d-%02d %02d:%02d:%02d",
                        result.tm_year + 1900, result.tm_mon + 1, result.tm_mday,
                        result.tm_hour, result.tm_min, result.tm_sec);
               
                szDateTime = '\0';
                return szDateTime;
        }
       
        /* 用户指定格式 */
        len = strlen(szFormat);
        i = 0;
        p = szDateTime;
       
        /* 判断前4个字符是否为yyyy */
        if (strncmp(szFormat, "yyyy", 4) == 0)
        {
                sprintf(p, "%04d", result.tm_year + 1900);
                p += 4;
                i += 4;
        }

        /* 格式中的剩余部分 */
        while (i < len)
        {
                /* 格式中的每个域必须以两个紧邻字符为整体 */
                field = szFormat;
                i += 1;
               
                if (field != c1 && field != c2 && field != c3 && field != c4) /* 如果第一个字符不是分隔符 */
                {
                        field = szFormat;
                        field = '\0';
                        i += 1;
                       
                        if (strcmp(field, "yy") == 0) /* 这种情况下整个格式里最多有两个yy */
                        {
                                sprintf(p, "%02d", (result.tm_year + 1900) % 100);
                        }
                        else if (strcmp(field, "mm") == 0)
                        {
                                sprintf(p, "%02d", result.tm_mon + 1);
                        }
                        else if (strcmp(field, "dd") == 0)
                        {
                                sprintf(p, "%02d", result.tm_mday);
                        }
                        else if (strcmp(field, "hh") == 0)
                        {
                                sprintf(p, "%02d", result.tm_hour);
                        }
                        else if (strcmp(field, "mi") == 0)
                        {
                                sprintf(p, "%02d", result.tm_min);
                        }
                        else if (strcmp(field, "ss") == 0)
                        {
                                sprintf(p, "%02d", result.tm_sec);
                        }
                        else
                        {
                                return NULL;
                        }
                       
                        p += 2;
                }
                else /* 如果是分隔符则直接打印出来 */
                {
                        *p = field;
                        p += 1;
                }
        }
       
        *p = '\0';
       
        return szDateTime;
}

//时间格式化
char * Now(char *szDateTime, const char *szFormat)
{
        return ToDateStr(time(NULL), szDateTime, szFormat);
}

//写日志文件
void mlog(char *logFileName,char *fmt,...)
{
        char        log_path;
        char        date_time;
        char        date_str;
        char        time_str;
       
        FILE         *fp;
        va_list        varArg;
        char        buf_str;
       
        memset(log_path,0, sizeof(log_path));
        memset(date_time, 0, sizeof(date_time));
        memset(date_str,0, sizeof(date_str));
        memset(time_str,0, sizeof(time_str));
        memset(buf_str,   0, sizeof(buf_str));
       
        // 取日期及时间
        Now(date_time, "yyyymmddhh:mi:ss");
        memcpy(date_str, date_time, 8);
        memcpy(time_str, date_time + 8, 8);
       
        /* 组合日志文件目录 */
        sprintf(log_path, "./%s.%s", logFileName, date_str);

        /* 以(创建)追加方式打开日志文件 */
        fp = fopen(log_path, "a+");
        if(fp == NULL)
        {
                return;
        }
       
        va_start(varArg, fmt);
        vsprintf(buf_str, fmt, varArg);
        va_end(varArg);

        fprintf(fp, "%s: %s\n", time_str, buf_str);       
       
        fclose(fp);
}

//写独占文件锁
int AcquireWriteLock(int fd, int start, int len)
{
        struct flock arg;
        arg.l_type = F_WRLCK; // 加写锁
        arg.l_whence = SEEK_SET;
        arg.l_start = start;
        arg.l_len = len;
        arg.l_pid = getpid();

        return fcntl(fd, F_SETLKW, &arg);
}

//释放独占文件锁
int ReleaseLock(int fd, int start, int len)
{
        struct flock arg;
        arg.l_type = F_UNLCK; //解锁
        arg.l_whence = SEEK_SET;
        arg.l_start = start;
        arg.l_len = len;
        arg.l_pid = getpid();

        return fcntl(fd, F_SETLKW, &arg);
}

//查看写锁
int SeeLock(int fd, int start, int len)
{
        struct flock arg;
        arg.l_type = F_WRLCK;
        arg.l_whence = SEEK_SET;
        arg.l_start = start;
        arg.l_len = len;
        arg.l_pid = getpid();

        if (fcntl(fd, F_GETLK, &arg) != 0) // 获取锁
        {
                return -1; // 测试失败
        }

        if (arg.l_type == F_UNLCK)
        {
                return 0; // 无锁
        }
        else if (arg.l_type == F_RDLCK)
        {
                return 1; // 读锁
        }
        else if (arg.l_type == F_WRLCK)
        {
                return 2; // 写所
        }

        return 0;
}

int Chlid_Run(int nServerfd)
{
        socklen_t nClientAddrSize = 0;
        int nClientfd       = -1;
        struct sockaddr_in Clientaddr;
       
        //循环监听接收数据
        while(1)
        {
                nClientAddrSize = sizeof(struct sockaddr_in);
               
                nClientfd = accept(nServerfd, (struct sockaddr *)(&Clientaddr), &nClientAddrSize);
                if(-1 == nClientfd)
                {
                  printf("accept fail !\n");
                  return -1;
                }
               
                //随便返回一个数据
                char szReturn;
                sprintf(szReturn, "hello");
                if(-1 == write(nClientfd, szReturn, strlen(szReturn)))
                {
            mlog((char* )"process", (char* )"(%s:%d)(%d)Connected Send error!", inet_ntoa(Clientaddr.sin_addr), ntohs(Clientaddr.sin_port), getpid());
            return -1;
                }
               
                //打印进程ID
                mlog((char* )"process", (char* )"(%s:%d)Connected pid=%d!", inet_ntoa(Clientaddr.sin_addr), ntohs(Clientaddr.sin_port), getpid());
               
                //关闭socket连接
                close(nClientfd);
        }
}

int main(int argc, char *argv[])
{
        //当前监控子线程个数
        int nNumChlid = 5;
       
        //检测时间间隔参数
        struct timespec tsRqt;
       
        //文件锁
        int fd_lock = 0;
       
        //要监听的IP和端口
        struct sockaddr_in server_addr;
        struct sockaddr_in client_addr;
        int nPort = 10030;    //监听端口
        int nServerfd = 0;    //Server Socket
       
        int nRet = 0;

        //主进程检测时间间隔(设置每隔5秒一次)
tsRqt.tv_sec= 5;
        tsRqt.tv_nsec = 0;
       
        // 打开(创建)锁文件
        char szFileName = {'\0'};
        memset(szFileName, 0, sizeof(flock));
        sprintf(szFileName, "./MultiListen.lk", getenv("HOME"));
        fd_lock = open(szFileName, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
        if (fd_lock < 0)
        {
                printf("open the flock and exit, errno = %d.", errno);
                exit(1);
        }
       
        //查看当前文件锁是否已锁
        nRet = SeeLock(fd_lock, 0, sizeof(int));
        if (nRet == -1 || nRet == 2)
        {
                printf("file is already exist!");
                exit(1);
        }
       
        //如果文件锁没锁,则锁住当前文件锁
if (AcquireWriteLock(fd_lock, 0, sizeof(int)) != 0)
        {
                printf("lock the file failure and exit, idx = 0!.");
                exit(1);
        }
       
        //写入子进程锁信息
        lseek(fd_lock, 0, SEEK_SET);
        for (int nIndex = 0; nIndex <= nNumChlid; nIndex++)
{
        write(fd_lock, &nIndex, sizeof(nIndex));
}

//在这里初始化监听
nServerfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == nServerfd)
{
        printf("Create Server FD error.\n");
        return -1;
}

//初始化监听地址信息
bzero(&server_addr,sizeof(struct sockaddr_in));
        server_addr.sin_family=AF_INET;
        server_addr.sin_addr.s_addr=htonl(INADDR_ANY); /* 这里地址使用全0,即所有 */
        server_addr.sin_port=htons(nPort);

//绑定Socket FD
if(-1 == bind(nServerfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr)))
        {
    printf("bind server addr fail!\n");
    return -1;
        }
       
        //开始监听
        if(-1 == listen(nServerfd, 5))
        {
    printf("listen server fail!\r\n");
    return -1;
        }
       
       

while (1)
{
        for (int nChlidIndex = 1; nChlidIndex <= nNumChlid; nChlidIndex++)
        {
                   //测试每个子进程的锁是否还存在
                   nRet = SeeLock(fd_lock, nChlidIndex * sizeof(int), sizeof(int));
                   if (nRet == -1 || nRet == 2)
                   {
                           continue;
                   }
                   //如果文件锁没有被锁,则设置文件锁,并启动子进程
                   int npid = fork();
                   if (npid == 0)
                   {
                           //上文件锁
                           if(AcquireWriteLock(fd_lock, nChlidIndex * sizeof(int), sizeof(int)) != 0)
                {
                        printf("child %d AcquireWriteLock failure.\n", nChlidIndex);
                        exit(1);
                }
                
                //启动子进程
                Chlid_Run(nServerfd);
                
                                        //子进程在执行完任务后必须退出循环和释放锁
                                        //ReleaseLock(fd_lock, nChlidIndex * sizeof(int), sizeof(int));                
                   }
        }
       
        printf("child count(%d) is ok.\n", nNumChlid);
        //检查间隔
        nanosleep(&tsRqt, NULL);
}
       
        return 0;
}



页: [1]
查看完整版本: 类似ngnix的多进程监听用例