clang linux epool socket


原文链接: clang linux epool socket

第一篇 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文件

 ```cpp

#include
#include
#include
#include

#include
#include
#include
#include
#include
#include

#include /*setrlimit */
#include
#include

#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);

}
 

二、echo源码2如下,main.c

 

 

 

 

#include
#include
#include
#include
#include
#include
#include
#include
#include /*setrlimit */

#define ECHO_SERVER_PORT 1883
#define LISTEN_BACKLOG 16
#define MAX_EVENT_COUNT 32
#define BUF_SIZE 2048
#define MAXCONN 60000

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;

}

int main() {

//设置每个进程允许打开的最大文件数,socket
if (set_fdlimit() < 0)
{
    return -1;
}
int ret, i;
int server_fd, client_fd, epoll_fd;
int ready_count;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
socklen_t addr_len;
struct epoll_event event;
struct epoll_event* event_array;
char* buf;
event_array = (struct epoll_event*)
                malloc(sizeof(struct epoll_event)*MAX_EVENT_COUNT);
buf = (char*)malloc(sizeof(char)*BUF_SIZE);
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(ECHO_SERVER_PORT);
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if(server_fd == -1) {
    perror("create socket failed.\n");
    return 1;
}
//一个端口释放后会等待两分钟之后才能再被使用,SO_REUSEADDR是让端口释放后立即就可以被再次使用。
int reuse_addr = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)) == -1)
{
    return -1;
}
ret = bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if(ret == -1) {
    perror("bind failed.\n");
    return 1;
}
ret = listen(server_fd, LISTEN_BACKLOG);
if(ret == -1) {
    perror("listen failed.\n");
    return 1;
}
fprintf(stderr,"listen on,fd=%d\n",server_fd);
epoll_fd = epoll_create(1);
if(epoll_fd == -1) {
    perror("epoll_create failed.\n");
    return 1;
}
event.events = EPOLLIN;
event.data.fd = server_fd;
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event);
if(ret == -1) {
    perror("epoll_ctl failed.\n");
    return 1;
}
while(1) {
    ready_count = epoll_wait(epoll_fd, event_array, MAX_EVENT_COUNT, -1);
    if(ready_count == -1) {
        perror("epoll_wait failed.\n");
        return 1;
    }
    for(i = 0; i < ready_count; i++) {
        if(event_array[i].data.fd == server_fd) {
            client_fd = accept(server_fd,
                        (struct sockaddr*)&client_addr, &addr_len);
            if(client_fd == -1) {
                perror("accept failed.\n");
                return 1;
            }
            event.events = EPOLLIN;
            event.data.fd = client_fd;
            ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event);
            if(ret == -1) {
                perror("epoll_ctl failed.\n");
                return 1;
            }
            fprintf(stderr,"accept,fd=%d\n",client_fd);
        }
        else {
            ret = recv(event_array[i].data.fd, buf, BUF_SIZE, 0);
            if(ret <= 0) {
                close(event_array[i].data.fd);
                epoll_ctl(epoll_fd, EPOLL_CTL_DEL,
                          event_array[i].data.fd, &event);
                continue;
            }
            ret = send(event_array[i].data.fd, buf, (size_t)ret, 0);
            if(ret == -1) {
                perror("send failed.\n");
            }
        }
    } // for each event
} // while(1)
close(epoll_fd);
close(server_fd);
free(event_array);
free(buf);
return 0;

}
```
————————————————
版权声明:本文为CSDN博主「libaineu2004」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/libaineu2004/article/details/79128878

`