epoll

文章目录

epoll

main.cc

#include"EpollServer.hpp"

void Usage(std::string proc)
{
  cout <<"Usage :\n\t" << proc << " port"<<endl;
}

int main(int argc,char *argv[])
{
  if(argc != 2)
  {
    Usage(argv[0]);
    exit(0);
  }

  EpollServer *es = new EpollServer();
  es->InitServer();
  es->Start();

  return 0;
}

Sock.hpp

#pragma once 
#include<stdlib.h>
#include<strings.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/select.h>
#include<sys/epoll.h>
#include<iostream>
#include<string>
using namespace std;

#define BACKLOG 5
class Sock
{
  public:
    static int Socket()
    {
      int sock = socket(AF_INET,SOCK_STREAM,0);
      if(sock < 0)
      {
        cerr << "socket error!" << endl;
        exit(2);
      }
      return sock;
    }

    static void Bind(int sock,int port)
    {
      struct sockaddr_in local;
      bzero(&local,sizeof(local));

      local.sin_family = AF_INET;
      local.sin_port = htons(port);
      local.sin_addr.s_addr = htonl(INADDR_ANY);
      if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0)
      {
        cerr << "bind error!"<< endl;
        exit(3);
      }
    }

    static void Listen(int sock)
    {
      if(listen(sock,BACKLOG) < 0)
      {
        cerr << "listen error!"<< endl;
        exit(4);
      }
    }

    static int Accept(int sock)
    {
      struct sockaddr_in peer;
      socklen_t len = sizeof(peer);
      int fd = accept(sock,(struct sockaddr*)&peer,&len);
      if(fd < 0)
      {
        cerr << "accept error!"<<endl;
      }
      return fd;
    }

    static void SetsockOpt(int sock)
    {int opt = 1;
      setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    }
};

EpollServer.hpp

#include"Sock.hpp"
#include<cstring>
#define SIZE 64
class bucket
{
  public:
    char buffer[20];
    int pos;//一次性读不完,标记,直到读完这个resquest才算结束
    int fd;

    bucket(int sock):fd(sock),pos(0)构造函数
    {
      memset(buffer,0,sizeof(buffer));
    }
    ~bucket()
    {}
}; 
class EpollServer
{
  private:
    int lsock;
    int port;
    int epfd; //epoll模型对应的文件描述符

  public:
    EpollServer(int _p = 8081):port(_p)
    {}

    void InitServer()
    {
      lsock = Sock::Socket();
      Sock::Bind(lsock,port);
      Sock::Listen(lsock);

      epfd = epoll_create(256); //参数并不重要,随便写
      if(epfd < 0){
        cerr << "epoll_create error!"<< endl;
        exit(5);
      }
      cout << "listen socket : " <<lsock << endl;
      cout << "epfd : " <<epfd << endl;
    }
用户告诉内核:把sock套接字关心的事件event添加到Epoll模型:
    void AddEvent2Epoll(int sock,uint32_t event)
    { struct epoll_event ev;
      ev.events = EPOLLIN;
      if(sock == lsock){
//套接字=监听套接字时,不设置(关心)缓冲区,接收链接,无数据交互
        ev.data.ptr = nullptr;
      }
      else{关心缓冲区buffer、文件描述符fd
        ev.data.ptr = new bucket(sock);
      }
      epoll_ctl(epfd,EPOLL_CTL_ADD,sock,&ev);
    }

    void DelEventFromEpoll(int sock)
    {
      close(sock);      epoll_ctl(epfd,EPOLL_CTL_DEL,sock,nullptr);
    }

    void HandlerEvents(struct epoll_event revs[],int num)
    {
      for(int i = 0; i < num; ++i)
      {
        uint32_t ev = revs[i].events;//已经就绪的事件的内容
        if(ev & EPOLLIN)//读事件就绪
        {
          if(revs[i].data.ptr != nullptr){
              //普通的sock
              bucket *bp = (bucket*)revs[i].data.ptr;
//一直读到bucket里面的buffer满的时候,再开始response
              ssize_t s = recv(bp->fd,bp->buffer+bp->pos,sizeof(bp->buffer)-bp->pos,0); //从fd读到buffer
              if(s > 0){读成功
                bp->pos += s;
                cout << "client# " << bp->buffer<<endl;
                if(bp->pos >= sizeof(bp->buffer)){
//不知道文件描述符的写是件是否就绪,sock从原来的关心读修改为写事件
                  struct epoll_event temp;
                  temp.events = EPOLLOUT;
                  temp.data.ptr = bp;
                  epoll_ctl(epfd,EPOLL_CTL_MOD,bp->fd,&temp);
                }
              }
              else if(s == 0){对方关闭连接
             DelEventFromEpoll(bp->fd);
                delete bp;//删除bucket
              }
              else{
                cerr << "recv error"<< endl;
                epoll_ctl(epfd,EPOLL_CTL_DEL,bp->fd,nullptr);
                delete bp;
              }
            } 
          else{
              //listen sock 
              int sock = Sock::Accept(lsock);从lsock中获取新连接
              if(sock > 0){获取成功
                    //把新的文件描述符添加到epoll模型当中,创建结点,添加到红黑树当中
                    AddEvent2Epoll(sock,EPOLLIN);
               }
            } 
        }
        else if(ev & EPOLLOUT){先读后写,开始写
         bucket *bp = (bucket*)revs[i].data.ptr; 
          send(bp->fd,bp->buffer,sizeof(bp->buffer),0);非阻塞,因为检测到事件就绪
          DelEventFromEpoll(bp->fd);
          delete bp;
        }
        else{
          //other events 
        }
      }
    }
    void Start()
    {AddEvent2Epoll(lsock,EPOLLIN);
      //int timeout = 1000;
      struct epoll_event revs[SIZE];//已经就绪的文件描述符:revs
      for(;;){
          int num = epoll_wait(epfd,revs,SIZE,-1);
          switch(num){
            case 0:
              cout << "time out!"<< endl;
              break;
            case -1:
              cerr << "epoll_wait error!"<<endl;
              break;
            default:事件就绪,处理事件
              HandlerEvents(revs,num);
              break;
          }
      }
    }

    ~EpollServer()
    {
      close(lsock);
      close(epfd);
    }

};

makefile:

epollServer : main.cc
	g++ -o $@ s^ -std=c++11
.PHONY:clean
clean :
	rm -f epollServer
[                   ]$ telnet 127.0.0.1 8081
Trying 127.0.0.1.. .
connected to 127.0.0.1.
Escape character is '一]'.
telnet>
nihao
你好

telnet> quit
Connection closed.

[            ]$ vim EpollServer.hpp
[              ]$ make
make : 'epollServer' is up to date.
[              ]$ make clean
rm -f epollServer
[              ]$ make
g++-o epollServer main.cc -std=c++11
[             ]$ ./epollServer
listen sock: 3
epoll fd:4client
# nihao
client# nihao
你好

非阻塞轮询检测有无事件就绪(查就绪队列):客户端无发送数据,服务器一直死循环打印time out!
每个文件描述符都有一个对应的缓冲区,不定义全局buff:A的内容会覆盖B的内容,有可能一次读取上来的数据并不是完整的
操作系统通过中断知道键盘敲了数字,有数据要进入cpu,
1事件就绪–回调机制,回调上层把就绪事件取走(类比:写完作业的人主动交作业)
2上层轮询检测事件,看哪些事件就绪(老师检查所有人的作业)
写底层的不多但必须看懂别人的代码,考点:epoll的优点、应用场景,select poll的缺点

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值