epoll多进程

时间:2016-02-24 09:58:56

标签: c++ network-programming

我继续使用c / c ++学习网络编程,之后我创建了多进程tcp服务器,我想创建简单的http服务器,它返回静态资源,我使用epoll所以让我显示我的代码

首先我使用fd passing for handle request in workers 所以,我的主要功能和头脑过程

struct Descriptors{
int sv[2]; 
};

class Parent{
public:
static Parent& getInstance(){
    static Parent instance;
    return instance;
}
Parent(Parent const&)         = delete;
    void operator=(Parent const&)  = delete;
void addFd(int fd){
    m_fd.push_back(fd);
};
void run() {
    startServer();
    size_t index = 0;
    while(true){
        struct epoll_event Events[MAX_EVENTS];
        int N = epoll_wait(m_epoll, Events, MAX_EVENTS, -1);

        for (size_t i =0; i < N; ++i){
            if (Events[i].events & EPOLLHUP){
                 epoll_ctl(m_epoll, EPOLL_CTL_DEL, Events[i].data.fd, &(Events[i]));
                 shutdown(Events[i].data.fd,SHUT_RDWR);

                         close(Events[i].data.fd);
                 continue;
            }else {

                if (Events[i].data.fd == m_masterSocket) {
                    handleConnection();

                }else {
                    char * arg = "1";
                    ssize_t size = sock_fd_write(m_fd[index], arg, 1,Events[i].data.fd);
                    index = (1+index) % m_fd.size();
                }
            }       
    }
}

}
private:
Parent(){
    m_numCpu = sysconf(_SC_NPROCESSORS_ONLN);
}
void startServer(){
    m_masterSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    struct sockaddr_in SockAddr;
    SockAddr.sin_family = AF_INET;
    SockAddr.sin_port = htons(11141);
    SockAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    bind(m_masterSocket, (struct sockaddr *)(&SockAddr), sizeof(SockAddr));

    set_nonblock(m_masterSocket);
    listen(m_masterSocket, SOMAXCONN);
    m_epoll = epoll_create1(0);
    struct epoll_event Event;
    Event.data.fd = m_masterSocket;
    Event.events = EPOLLIN | EPOLLRDHUP;

    epoll_ctl(m_epoll, EPOLL_CTL_ADD, m_masterSocket, &Event);
}
void handleConnection(){
    int SlaveSocket = accept(m_masterSocket, 0, 0);
    set_nonblock(SlaveSocket);
    struct epoll_event Event;
    Event.data.fd = SlaveSocket;
    Event.events = EPOLLIN | EPOLLRDHUP;

    epoll_ctl(m_epoll, EPOLL_CTL_ADD, SlaveSocket, &Event);
}
int m_epoll; 
int m_masterSocket;
int m_numCpu;
std::vector<int> m_fd;
};

void parent(int sock){
Parent::getInstance().addFd(sock);
}

int main(int argc, char **argv){
int numCpu = sysconf(_SC_NPROCESSORS_ONLN);

std::vector<Descriptors> desc;
desc.resize(numCpu);

bool isParent = true;
for (int i  = 0; i < numCpu && isParent; ++i){
    std::cout << "pid my is = " << getpid() <<std::endl;
    int sv[2];
      if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) {
               perror("socketpair");
              exit(1);
        }       
    pid_t forkId = fork();
    switch (forkId){
        case 0:{
            isParent = false;
            close(sv[0]);
                child(sv[1]);
                break;
        }   
          case -1:
                   perror("fork");
                    exit(1);
            default:
                close(sv[1]);
                parent(sv[0]);
                break;
        }   
}

if (isParent){

    Parent::getInstance().run();
    int status;
    waitpid(-1, &status, 0);

}
}   

我的工作流程是

void respond(int fd)
{
char mesg[99999], *reqline[3], data_to_send[BYTES], path[99999];
int rcvd, fileDesc, bytes_read;

memset( (void*)mesg, (int)'\0', 99999 );
const char *ROOT = "/home/web_server/";
int RecvResult = recv(fd,mesg, 99999, MSG_NOSIGNAL);
    if (RecvResult == 0 && errno != EAGAIN){
        shutdown(fd,SHUT_RDWR);
        close(fd);
    }else if (RecvResult >0){

        printf("%s", mesg);
        reqline[0] = strtok (mesg, " \t\n"); // split on lexemes
         if ( strncmp(reqline[0], "GET\0", 4)==0 )  // if first 4 character equal
        {
                reqline[1] = strtok (NULL, " \t");
                reqline[2] = strtok (NULL, " \t\n");
        std::cout << "reqline 1 " << reqline[1] << std::endl;
        std::cout << "reqline 2 " << reqline[2] << std::endl;
            if ( strncmp( reqline[2], "HTTP/1.0", 8)!=0 
            && strncmp(reqline[2], "HTTP/1.1", 8 ) !=0 )
            {
            write(fd, "HTTP/1.0 400 Bad Request\n", 25);
            }
            else
            {
                if ( strncmp(reqline[1], "/\0", 2)==0 )
                    reqline[1] = "/index.html";      

                strcpy(path, ROOT);
                strcpy(&path[strlen(ROOT)], reqline[1]);
                printf("file: %s\n", path);

                if ( (fileDesc=open(path, O_RDONLY))!=-1 )     
                {
                        send(fd, "HTTP/1.0 200 OK\n\n", 17, 0);
                        while ( (bytes_read=read(fileDesc, data_to_send, BYTES))>0 )
                            write (fd, data_to_send, bytes_read);
                }
                     else    write(fd, "HTTP/1.0 404 Not Found\n", 23);  
                }
    }
}
  shutdown(fd,SHUT_RDWR);
  close(fd);
}

void child(int sock)
{
  int fd;
char    buf[16];
ssize_t size;

sleep(1);
for (;;) {

    size = sock_fd_read(sock, buf, sizeof(buf), &fd);
    if (size <= 0)
        break;

    if (fd != -1) { 
    respond(fd);
  }
}
printf("child processes is end\n");
}

当我进入浏览器http://127.0.0.1:11141/时,它就可以了,我得到了index.html,但是当我在apache基准测试中运行时,

ab -n 10 -c 10 http://127.0.0.1:11141/

我得到答案

This is ApacheBench, Version 2.3 
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)...apr_socket_recv: Connection reset by peer (104)
Total of 2 requests completed

我不明白我的错误在哪里,因为我认为理论上我的服务器(因为使用epoll)必须解决C10K problem。但在实践中,我的服务器无法解决10连接问题。请问你能帮帮我吗? 感谢您提供有用的链接和任何建议!

更新 当我以

运行时
  strace -f ./server 2> error.txt

在error.txt结尾

[pid  6552] write(6, 0x7ffdbff00390, 757) = -1 EPIPE (Broken pipe)
[pid  6552] --- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=6552,   si_uid=1000} ---
[pid  6552] +++ killed by SIGPIPE +++
write(1, 0x7fc5ffbe3000, 83)            = 83
write(1, 0x7fc5ffbe3000, 12)            = 12 
write(1, 0x7fc5ffbe3000, 20)            = 20
write(1, 0x7fc5ffbe3000, 41)            = 41
open(0x7ffdbff18e30, O_RDONLY)          = 11
sendto(10, 0x403df9, 17, 0, NULL, 0)    = 17
read(11, 0x7ffdbff00390, 1024)          = 757
write(10, 0x7ffdbff00390, 757)          = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=6554, si_uid=1000}    ---
+++ killed by SIGPIPE +++

所以我认为EPipe错误存在问题,但我不明白为什么......

更新

所以我认为关闭描述符中的问题,但我不明白如何解决它。感谢您提供有用的建议。

更新

我在工作进程中发送函数发送错误

1 个答案:

答案 0 :(得分:-1)

似乎我发现了我的错误,正确的版本功能无效:

void respond(int fd)
{
char mesg[99999], *reqline[3], data_to_send[BYTES], path[99999];
int rcvd, fileDesc, bytes_read;

memset( (void*)mesg, (int)'\0', 99999 );
const char *ROOT = "/home/web_server/";
int RecvResult = recv(fd,mesg, 99999, MSG_NOSIGNAL);
//EAGAIN -  "there is no data available right now, try again later
if (RecvResult == 0 && errno != EAGAIN){
    shutdown(fd,SHUT_RDWR);
    close(fd);      
    std::cout << "error recv" << std::endl;
        return;
}else if (RecvResult >0){
        printf("%s", mesg);
        reqline[0] = strtok (mesg, " \t\n"); // split on lexemes
         if ( strncmp(reqline[0], "GET\0", 4)==0 )  // if first 4 character equal
        {
                reqline[1] = strtok (NULL, " \t");
                reqline[2] = strtok (NULL, " \t\n");
        std::cout << "reqline 1 " << reqline[1] << std::endl;
        std::cout << "reqline 2 " << reqline[2] << std::endl;
            if ( strncmp( reqline[2], "HTTP/1.0", 8)!=0 
            && strncmp(reqline[2], "HTTP/1.1", 8 ) !=0 )
            {
            send(fd, "HTTP/1.0 400 Bad Request\n", 25 , MSG_NOSIGNAL);
            }
            else
            {
                if ( strncmp(reqline[1], "/\0", 2)==0 )
                    reqline[1] = "/index.html";      

                strcpy(path, ROOT);
                strcpy(&path[strlen(ROOT)], reqline[1]);
                printf("file: %s\n", path);

                if ( (fileDesc=open(path, O_RDONLY))!=-1 )     
                {
                        send(fd, "HTTP/1.0 200 OK\n\n", 17, MSG_NOSIGNAL);
                        while ( (bytes_read=read(fileDesc, data_to_send, BYTES))>0 )
                            {
                    if (bytes_read != -1)
                        send (fd, data_to_send, bytes_read, MSG_NOSIGNAL);
                        }
        }
                     else    send(fd, "HTTP/1.0 404 Not Found\n", 23, MSG_NOSIGNAL);  
                }
            shutdown(fd,SHUT_RDWR);
            close(fd);
    }
}else {
std::cout << "Client disconnected unexpect" << std::endl;
}

}

问题是,我关闭套接字,之后我尝试从这个套接字读取。