第一篇 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); }