类似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]