c++進程池實現回射服務器

進程池相對於線程池就沒那麼麻煩,沒有條件變量,沒有互斥鎖,這些東西!比較簡單!參考網上資料,實現一個回射服務器,儘管看着人家實現思路寫的,還是出現很多問題,太菜了!確實太菜了!就當學習了gdb多進程調試,哈哈!有關進程調試的文章這裏有個學姐寫的可以參考哦!gdb多進程調試

process.h

#pragma once
#include <iostream>
#include <assert.h>
#include <string.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <map>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>
#include <vector>
#include <memory> 
const int EPOLLNUM = 1024 ;
const int BUFFERSIZE = 1024 ;
using namespace std ;

class process ;
class tool ;

class process {
public:
    process() : pid(-1){}
    ~process() {}
public :
    int createSocketPair() ;
    int getReadFd() { return pipe[0] ; }
    int getWriteFd() { return pipe[1] ; }
public :
    pid_t pid ;
private :
    int pipe[2] ;
};

//進程池使用單實例模式
template<typename T>  
class processPool {
public :
    processPool(int listenFd, int processNum = 8) ;
    ~processPool() {}
public :
    //保證時鐘只產生一個進程池
    static shared_ptr<processPool<T>> create(int listenFd, 
                                             int processNum = 8) ;
public :
    void run() ;
private :
    //設置進程對應的管道
    void setupSigPipe() ;
    //父進程運行
    void runParent() ;
    //子進程運行
    void runChild() ;
private :
    static const int maxProcessNum = 16 ; //進程池允許的最大子進程數量
    static const int userPreProcess = 65535 ;//每個進程最多能處理的客戶數量
    static const int maxEventNum = 10000 ; //epoll最多處理的事件數量
    static shared_ptr<processPool<T>> object ; //進程池的靜態實例 

    int curProcessNum ; //進程池目前能處理的進城數量
    int index ; //子進程在進程中的序號
    int epollFd ;  //epoll句柄
    int listenFd ; //監聽的文件描述符
    bool stop ; //進程終止標記
    vector<shared_ptr<process>> proDescInfo ; // 保存進程的描述信息
};

class tool {
public :
    static int setNoBlock(int fd) ;
    static int addFd(int epollFd, int fd) ;
    static int removeFd(int epollFd, int fd) ;
    static void addSig(int sig, void(handle)(int), bool restart=true) ;
};

template<typename T> 
shared_ptr<processPool<T>> processPool<T> :: object = nullptr ;

//創建進程池
template<typename T>
processPool<T> :: processPool(int listenFd, int pNum)
    : listenFd(listenFd), curProcessNum(pNum), index(-1), stop(false) {
    assert(pNum > 0 && pNum <= maxProcessNum) ;
    proDescInfo.reserve(pNum)  ;
    for(int i=0; i<pNum; i++) {
        proDescInfo[i] = make_shared<process>() ;
    }
    //創建子進程
    for(int i=0; i<pNum; i++) {
        //創建unix雙向匿名管道
        int ret = proDescInfo[i]->createSocketPair() ;
        assert(ret > 0) ;
        proDescInfo[i]->pid = fork() ;
        assert(proDescInfo[i]->pid >= 0) ;
        //父進程
        if(proDescInfo[i]->pid > 0) {
            //關掉讀端
            close(proDescInfo[i]->getReadFd()) ;
            continue ;
        }
        //子進程
        else {
            close(proDescInfo[i]->getWriteFd()) ;
            index = i ; //自己在進程池中的下標
            break ; //這裏一定得跳出,否則子進程會繼續創建進程
        }
    }
}

template <typename T>
shared_ptr<processPool<T>> processPool<T> :: create(int listenFd, 
                                                    int processNum) {
    if(object == nullptr) {
        object = make_shared<processPool<T>>(listenFd, processNum) ;
    }
    return object ;
}

//父進程在進程中的下標-1
template<typename T>
void processPool<T>::run() {
    if(index != -1) runChild() ;
    else runParent() ;
}

//統一事件
template<typename T> 
void processPool<T> :: setupSigPipe() {
    epollFd = epoll_create(EPOLLNUM) ;
    assert(epollFd > 0) ;
    tool :: addSig(SIGPIPE, SIG_IGN) ;
}

template<typename T> 
void processPool<T> :: runParent() {
    cout << "在父進程"  << "   " << "序號:" << index << endl ;
    //設置信號
    setupSigPipe() ;
    tool :: addFd(epollFd, listenFd)  ; 
    epoll_event ev[maxEventNum] ;
    int count = 0 ;
    char newConn = 'c' ;
    while(!stop) {
        int number = epoll_wait(epollFd, ev, maxEventNum, -1) ;
        if(number < 0) {
            cout << __LINE__ << "      " << __FILE__ << endl ;
            return  ;
        }
        for(int i=0; i<number; i++) {
            int sockFd = ev[i].data.fd ;
            if(sockFd == listenFd) {
                int index = count ;
                do{
                    if(proDescInfo[index]->pid != -1) break ;
                    index = (index+1)%curProcessNum ;
                }while(index != count) ;

                if(proDescInfo[index]->pid == -1) { //沒有子進程
                    stop = true ;
                    break ;
                }
                count = (index+1)%curProcessNum ;
                int ret = send(proDescInfo[index]->getWriteFd(), &newConn, sizeof(newConn), 0) ;
                if(ret < 0) {
                    cout << __LINE__ << "         " << __FILE__ << endl ;
                    return ;
                }
                cout << "給進程" << index << "發送消息"<< endl ;
            }
        }   
    }
    close(epollFd) ;
}

//子進程的執行函數
template<typename T>
void processPool<T> :: runChild() {
    cout << "在子進程中" << "     " << "序號:" << index << endl ;
    setupSigPipe() ;
    int pipeFd = proDescInfo[index]->getReadFd() ;
    //將管道描述符加入到epoll中
    tool::addFd(epollFd, pipeFd) ;
    epoll_event ev[maxEventNum]  ;
    map<int, shared_ptr<T>> user ; 
    //爲客戶端設置處理的對象的池子
    while(!stop) {
        int num = epoll_wait(epollFd, ev, maxEventNum, -1) ;
        if(num < 0&&errno != EINTR) {
            cout << __LINE__ << "        " << __FILE__ << endl ;
            return ;
        } 
        for(int i=0; i<num; i++) {
            int fd =ev[i].data.fd ;
            if(fd == pipeFd&&(ev[i].events&EPOLLIN)) {
                cout << "管道信號!" << endl ;
                char client ;
                int ret = recv(fd, &client, sizeof(client), 0) ;
                if(ret < 0) {
                    cout << __LINE__ << "      " << __FILE__ << endl ;
                    return ;
                }
                cout << "接收到信號!" << endl ;
                int connFd = accept(listenFd, NULL, NULL) ;
                if(connFd < 0) {
                    cout << __LINE__ << "       " << __FILE__ << endl ;
                    return ;
                }
                cout << "接收到新連接!" << endl ;
                ret = tool::addFd(epollFd, connFd) ;
                if(ret < 0) {
                    cout << __LINE__ <<  "         " << __FILE__ << endl ;
                    return ;
                }
                cout << "創建用戶對象" << endl ;
                //創建用戶對象
                user[connFd] = make_shared<T>(epollFd, connFd) ;
            } 
            //處理事件
            else if(ev[i].events&EPOLLIN) {
                user[fd]->process() ;
            }
        }
    }
    close(pipeFd) ;
    close(epollFd) ;
}

class echo {
public :
    echo(int epollFd, int fd) {
        echo::epollFd = epollFd ;
        sockFd = fd ;
        memset(buf, '\0', BUFFERSIZE) ;
        endIndex = 0 ;
    }
    ~echo() {close(sockFd) ;}
    void process() {
        while(true) {
            int ret = recv(sockFd, buf, BUFFERSIZE-1, 0) ;
            if(ret < 0) {
                if(errno != EAGAIN) {
                    tool :: removeFd(epollFd, sockFd) ;
                    break ;
                }
                cout << __LINE__ << "        " << __FILE__ << endl ;
                return ;
            }
            else if(ret == 0) {
                int res = tool :: removeFd(epollFd, sockFd) ;
                if(res < 0) {
                    cout << "移除失敗!" << endl ;
                    return ;
                }
                break ;
            }
            else {
                cout << "客戶端數據:" << buf << endl ;
                ret  = send(sockFd, buf, sizeof(buf), 0) ;
                if(ret < 0) {
                    cout << __LINE__ << "         " << __FILE__ << endl ;
                    break ;
                }
                break ;
            }
        }
    }

public :
    static int epollFd ;
    int sockFd ;
    char buf[BUFFERSIZE] ;
    int endIndex ;
} ;


int echo :: epollFd ;

int tool :: setNoBlock(int fd) {
    int old = fcntl(fd, F_GETFL) ;
    int ret = fcntl(fd, F_SETFL, old|O_NONBLOCK) ;
    if(ret < 0) {
        cout << __FILE__ << "       " << __LINE__ << endl ;
        return -1 ;
    }
    return old ;
}

int tool :: addFd(int epollFd, int fd) {
    epoll_event ev ;
    ev.data.fd = fd ;
    ev.events = EPOLLIN|EPOLLET ;
    int ret = epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &ev) ;
    if(ret < 0) {
        cout << __FILE__ << "      " << __LINE__ << endl ;
    }
    ret = setNoBlock(fd) ;
    if(ret < 0) {
        cout << __LINE__ << "      " << __FILE__ << endl ;
        return -1 ;
    }
    return 1 ;
}

int tool :: removeFd(int epollFd, int fd) {
   int ret = epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, 0) ;
   if(ret < 0) {
        cout << __LINE__ << "     " << __FILE__ << endl ;
        return -1 ;
   }
   return 1 ;
}

void tool :: addSig(int sig, void(handle)(int), bool restart) {
    struct sigaction sa ;
    memset(&sa, '\0', sizeof(sa)) ;
    sa.sa_handler = handle ;
    if(restart) 
        int res = sa.sa_flags|SA_RESTART ;
    sigfillset(&sa.sa_mask) ;
}

int process :: createSocketPair() {
    int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, pipe) ;
    if(ret < 0) {
        cout << __LINE__ << "      " << __FILE__ << endl ;
        return -1 ; 
    }
    return 1 ;
}   

main.cpp

#include "process.h"
void handle(int sig) {
    if(sig == SIGINT) {
        exit(0) ;
    }
}

int main(int argc, char** argv) {
    if(argc != 3) {
        cout << "error usage" << endl ;
        return 1 ;
    }
    struct sockaddr_in addr ;
    bzero(&addr, sizeof(addr)) ;
    addr.sin_family = AF_INET ;
    int ret = inet_pton(AF_INET, argv[1], &addr.sin_addr) ;
    if(ret < 0) {
        cout << __LINE__ << "        " << __FILE__ << endl ;
        return 0 ;
    }
    addr.sin_port = htons(atoi(argv[2])) ;
    int listenFd = socket(AF_INET, SOCK_STREAM, 0) ; 
    if(listenFd < 0) {
        cout << __LINE__ << "       " << __FILE__ << endl ;
        return 1;
    }
    int use = 1 ;
    ret = setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, &use, sizeof(use)) ;
    if(ret < 0) {
        cout << __LINE__ << "       " << __FILE__ << endl ;
        return 0;
    }
    ret = bind(listenFd, (const sockaddr*)&addr, sizeof(addr)) ;
    if(ret < 0) {
        cout << __FILE__ << "       " << __LINE__ << endl ;
        return 0 ;
    }
    ret = listen(listenFd, 100) ;
    if(ret < 0) {
        cout << "監聽失敗!" << endl ;
        return -1 ;
    }
    //中斷回收進程後再退出
    signal(SIGINT, handle) ;
    //創建
    shared_ptr<processPool<echo>> pool = processPool<echo> :: create(listenFd, 2);
    if(pool)
    pool->run() ;
    close(listenFd) ;
}

實現思路相對較清晰,也比較簡單!所以想和大家分享一下!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章