多進程監聽適合於短鏈接,且鏈接間無交集的應用。
前兩天簡單寫了一個,在這裏保存一下。
socket
#include <sys/types.h>
測試
#include <stdarg.h>
ui
#include <signal.h>
spa
#include <unistd.h>
線程
#include <fcntl.h>
日誌
#include <time.h>
orm
#include <string.h>
server
#include <stdlib.h>
進程
#include <stdio.h>
get
#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[3], 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[strlen("yyyy-mm-dd hh:mi:ss")] = '\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[0] = szFormat[i];
i += 1;
if (field[0] != c1 && field[0] != c2 && field[0] != c3 && field[0] != c4) /* 若是第一個字符不是分隔符 */
{
field[1] = szFormat[i];
field[2] = '\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[0];
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[128];
char date_time[20];
char date_str[10];
char time_str[10];
FILE *fp;
va_list varArg;
char buf_str[1024];
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("[Chlid_Run]accept fail !\n");
return -1;
}
//隨便返回一個數據
char szReturn[20];
sprintf(szReturn, "hello");
if(-1 == write(nClientfd, szReturn, strlen(szReturn)))
{
mlog((char* )"process", (char* )"[Chlid_Run](%s:%d)(%d)Connected Send error!", inet_ntoa(Clientaddr.sin_addr), ntohs(Clientaddr.sin_port), getpid());
return -1;
}
//打印進程ID
mlog((char* )"process", (char* )"[Chlid_Run](%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[200] = {'\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("[Main]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("[main]bind server addr fail!\n");
return -1;
}
//開始監聽
if(-1 == listen(nServerfd, 5))
{
printf("[main]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;
}
複製代碼