第一篇 TCP server
守護程序daemonize的源碼可以借鑒redis的:
void daemonize(void) { //come from /redis/server.c/daemonize()
int fd;
if (fork() != 0) exit(0); /* parent exits */
setsid(); /* create a new session */
/* Every output goes to /dev/null. If Redis is daemonized but
* the 'logfile' is set to 'stdout' in the configuration file
* it will not log at all. */
if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
if (fd > STDERR_FILENO) close(fd);
}
}
一、echo源碼1如下,main.c,注意是.c檔案
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/resource.h> /*setrlimit */
#include <signal.h>
#include <fcntl.h>
#define bool int //linux C中沒有bool類型
#define false 0 //linux C中沒有bool類型
#define true 1 //linux C中沒有bool類型
#define IPADDRESS "127.0.0.1"
#define PORT 1883
#define MAXSIZE 1024
#define LISTENQ 512
#define FDSIZE 1024
#define EPOLLEVENTS 60000
#define MAXCONN 60000
//函數聲明
//建立套接字并進行綁定
static int socket_bind(const char* ip,int port);
//IO多路複用epoll
static void do_epoll(int listenfd);
//事件處理函數
static void handle_events(int epollfd,struct epoll_event *events,int num,int listenfd,char *buf);
//處理接收到的連接配接
static void handle_accpet(int epollfd,int listenfd);
//讀處理
static void do_read(int epollfd,int fd,char *buf);
//寫處理
static void do_write(int epollfd,int fd,char *buf);
//添加事件
static void add_event(int epollfd,int fd,int state);
//修改事件
static void modify_event(int epollfd,int fd,int state);
//删除事件
static void delete_event(int epollfd,int fd,int state);
void init_signal(void)//設定信号處理,SIG_IGN表示忽略信号,SIG_DFL表示使用信号的預設處理方式
{
signal(SIGCHLD, SIG_DFL);
signal(SIGPIPE, SIG_IGN);
}
int set_fdlimit()
{
//設定每個程序允許打開的最大檔案數
//這項功能等價于linux終端指令 "ulimit -n 102400"
struct rlimit rt;
rt.rlim_max = rt.rlim_cur = MAXCONN;
if (setrlimit(RLIMIT_NOFILE, &rt) == -1)
{
perror("setrlimit error");
return -1;
}
return 0;
}
void daemon_run_method1()//來自https://github.com/baloonwj/flamingo
{
int pid;
signal(SIGCHLD, SIG_IGN);
//1)在父程序中,fork傳回新建立子程序的程序ID;
//2)在子程序中,fork傳回0;
//3)如果出現錯誤,fork傳回一個負值;
pid = fork();
if (pid < 0)
{
//std::cout << "fork error" << std::endl;
exit(-1);
}
//父程序退出,子程序獨立運作
else if (pid > 0)
{
exit(0);
}
//之前parent和child運作在同一個session裡,parent是會話(session)的領頭程序,
//parent程序作為會話的領頭程序,如果exit結束執行的話,那麼子程序會成為孤兒程序,并被init收養。
//執行setsid()之後,child将重新獲得一個新的會話(session)id。
//這時parent退出之後,将不會影響到child了。
setsid();
int fd;
fd = open("/dev/null", O_RDWR, 0);
if (fd != -1)
{
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
}
if (fd > 2)
{
close(fd);
}
}
bool daemon_run_method2() //Linux高性能伺服器程式設計.pdf,遊雙
{
//建立子程序,關閉父程序,這樣可以使程式在背景進行
pid_t pid = fork();
if ( pid < 0 )
{
return false;
}
else if ( pid > 0 )
{
exit( 0 );
}
//設定檔案權限掩碼。當程序建立新檔案時,檔案的權限将是mode & 0777
umask( 0 );
//建立新的會話,設定本程序為程序組的首領
pid_t sid = setsid();
if ( sid < 0 )
{
return false;
}
//切換工作目錄
if ( ( chdir( "/" ) ) < 0 )
{
/* Log the failure */
return false;
}
//關閉标準輸入裝置、标準輸出裝置和标準錯誤輸出裝置
close( STDIN_FILENO );
close( STDOUT_FILENO );
close( STDERR_FILENO );
//将标準輸入、輸出和錯誤輸出都定向到/dev/null檔案
open( "/dev/null", O_RDONLY );
open( "/dev/null", O_RDWR );
open( "/dev/null", O_RDWR );
return true;
}
int main(int argc,char *argv[])
{
//設定信号處理
init_signal();
//設定每個程序允許打開的最大檔案數,socket
if (set_fdlimit() < 0)
{
return -1;
}
//守護者程序
bool bdaemon = false;
if (bdaemon)
{
daemon_run_method1();
}
int listenfd;
listenfd = socket_bind(IPADDRESS,PORT);
listen(listenfd,LISTENQ);
do_epoll(listenfd);
return 0;
}
static int socket_bind(const char* ip,int port)
{
int listenfd;
struct sockaddr_in servaddr;
listenfd = socket(AF_INET,SOCK_STREAM,0);
if (listenfd == -1)
{
perror("socket error:");
exit(1);
}
//一個端口釋放後會等待兩分鐘之後才能再被使用,SO_REUSEADDR是讓端口釋放後立即就可以被再次使用。
int reuse_addr = 1;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)) == -1)
{
return -1;
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
//inet_pton(AF_INET,ip,&servaddr.sin_addr);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//綁定所有網卡所有IP
//servaddr.sin_addr.s_addr = inet_addr("172.16.6.178");
//servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");//這樣寫指代不明,當伺服器有多網卡時,不知道綁定哪個IP,導緻連接配接失敗
servaddr.sin_port = htons(port);
if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1)
{
perror("bind error: ");
exit(1);
}
printf("listen on: %d,listenfd=%d\n",PORT,listenfd);
return listenfd;
}
static void do_epoll(int listenfd)
{
int epollfd;
struct epoll_event events[EPOLLEVENTS];
int ret;
char buf[MAXSIZE];
memset(buf,0,MAXSIZE);
//建立一個描述符
epollfd = epoll_create(FDSIZE);
//添加監聽描述符事件
add_event(epollfd,listenfd,EPOLLIN);
for ( ; ; )
{
//擷取已經準備好的描述符事件
ret = epoll_wait(epollfd,events,EPOLLEVENTS,-1);
handle_events(epollfd,events,ret,listenfd,buf);
}
close(epollfd);
}
static void handle_events(int epollfd,struct epoll_event *events,int num,int listenfd,char *buf)
{
int i;
int fd;
//進行選好周遊
for (i = 0;i < num;i++)
{
fd = events[i].data.fd;
//根據描述符的類型和事件類型進行處理
if ((fd == listenfd) &&(events[i].events & EPOLLIN))
handle_accpet(epollfd,listenfd);
else if (events[i].events & EPOLLIN)
do_read(epollfd,fd,buf);
else if (events[i].events & EPOLLOUT)
do_write(epollfd,fd,buf);
}
}
static void handle_accpet(int epollfd,int listenfd)
{
int clifd;
struct sockaddr_in cliaddr;
socklen_t cliaddrlen = sizeof(cliaddr);
clifd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen);
if (clifd == -1)
perror("accpet error:");
else
{
printf("accept a new client: %s:%d,fd=%d\n",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port,clifd);
//添加一個客戶描述符和事件
add_event(epollfd,clifd,EPOLLIN);
}
}
static void do_read(int epollfd,int fd,char *buf)
{
int nread;
nread = read(fd,buf,MAXSIZE);
if (nread == -1)
{
perror("read error:");
close(fd);
delete_event(epollfd,fd,EPOLLIN);
}
else if (nread == 0)
{
fprintf(stderr,"client close,fd=%d\n",fd);
close(fd);
delete_event(epollfd,fd,EPOLLIN);
}
else
{
printf("read message is: %s,fd=%d\n",buf,fd);
//修改描述符對應的事件,由讀改為寫
modify_event(epollfd,fd,EPOLLOUT);
}
}
static void do_write(int epollfd,int fd,char *buf)
{
int nwrite;
nwrite = write(fd,buf,strlen(buf));
if (nwrite == -1)
{
perror("write error:");
close(fd);
delete_event(epollfd,fd,EPOLLOUT);
}
else
modify_event(epollfd,fd,EPOLLIN);
memset(buf,0,MAXSIZE);
}
static void add_event(int epollfd,int fd,int state)
{
struct epoll_event ev;
ev.events = state;//LT
ev.data.fd = fd;
epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);
}
static void delete_event(int epollfd,int fd,int state)
{
struct epoll_event ev;
ev.events = state;
ev.data.fd = fd;
epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev);
}
static void modify_event(int epollfd,int fd,int state)
{
struct epoll_event ev;
ev.events = state;
ev.data.fd = fd;
epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev);
}