- 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
#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的缺点
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)