现在每次改写服务器的时候最大的愿望就是智能指针别出问题,不过说到底还是各个对象的生命周期没弄明白,有的地方需要指针一直存活,有的用完就可以释放了,网络编程没有基础是真不行。
这次新增了两个类Acceptor和Connection类,Acceptor用于接收连接,Connection用于处理TCP连接,在这里梳理一下连接过程,有点迷糊了。
首先创建一个事件循环类,类里包含一个Epoll类,在事件循环类里初始化Epoll类,而Eopoll类的构造函数就是创建一个epoll对象,负责监听IO事件,同时创建一个管理epoll_event对象数组的智能指针,并对数组初始化(置0)。
pEventLoop loop(new EventLoop());
EventLoop::EventLoop() : ep(new Epoll()), quit(false) {}
然后传参loop,new一个Server对象,在Server的构造函数内new一个Acceptor对象,也就是上文提到的专门用来处理连接的对象,Acceptor构造函数创建一个Socket对象,该对象用于创建socket,并初始化一个Channel对象,Channel对象是用于对不同的文件描述符做不同处理的对象,通过绑定Eventloop对象(也就是Epoll对象)和一个文件描述符来做不同的处理。
Acceptor构造函数内新建了一个地址对象设置ip地址及端口,将服务端socket绑定该地址,并开始监听,设置该文件描述符为非阻塞状态。
并绑定Acceptor对象内部Channel对象回调函数为acceptConnection,该函数用于接收客户端的连接。当新客户端连接时,acceptConnection函数创建客户端socket并连接该socket,并将该socket传给Server对象的回调函数中,Server对象内的回调函数为newConnection,该函数通过客户端socket新建一个Connection对象,Connection构造函数中新建一个Channel类并绑定echo函数(回显服务端消息)到该Channel对象上。回到
newConnection函数内,该函数还绑定一个断开连接后的操作函数到该Connection对象上,用于销毁tcp连接。最后将该Connection对象加入到Server对象管理的map内,该map用于将一个文件描述符和一个Connection绑定起来。
pServer server(new Server(loop));
Server::Server(pEventLoop _loop) : loop(_loop), acceptor(new Acceptor(loop)) {
std::function<void(pSocket)> cb = std::bind(&Server::newConnection, this, std::placeholders::_1);
acceptor->setNewConnectionCB(cb);
}
Acceptor::Acceptor(pEventLoop _loop) : loop(_loop), sock(new Socket()), acceptChannel(new Channel(loop.get(), sock->getFd())) {
pInetAddress addr(new InetAddress("127.0.0.1", 8888));
sock->bind(addr.get());
sock->listen();
sock->setNonBlocking();
std::function<void()> cb = std::bind(&Acceptor::acceptConnection, this);
acceptChannel->setCallBack(cb);
acceptChannel->enableReading();
}
void Acceptor::acceptConnection() const {
pInetAddress clntAddr(new InetAddress());
pSocket clntSock(new Socket(sock->accept(clntAddr.get())));
printf("new client fd %d! IP: %s Port: %d\n", clntSock->getFd(),
inet_ntoa(clntAddr->getSocketaddr().sin_addr), ntohs(clntAddr->getSocketaddr().sin_port));
clntSock->setNonBlocking();
newConnectionCallBack(clntSock);
}
void Server::newConnection(pSocket sock) {
pConnection conn(new Connection(loop, sock));
std::function<void(pSocket)> cb = std::bind(&Server::delConnection, this, std::placeholders::_1);
conn->setDelConncetCB(cb);
connections[sock->getFd()] = conn;
}
Connection::Connection(pEventLoop _loop, pSocket _sock) : loop(_loop), sock(_sock),
channel(new Channel(loop.get(), sock->getFd())) {
std::function<void()> cb = std::bind(&Connection::echo, this, sock->getFd());
channel->setCallBack(cb);
channel->enableReading();
}
最后则是
loop->loop();
void EventLoop::loop() const {
while (!quit) {
std::vector<pChannel> chs;
chs = ep->poll();
for(auto it : chs) {
it->handleEvent();
}
}
}
通过poll函数获取到活动的Channel对象,并调用之前绑定的回调函数进行事务处理。
理了一遍感觉思路清晰了一点,但还是有点糊,总之就是Acceptor负责接受链接,由Connection进行具体的业务处理,下次有时间了画一张时序图出来,方便理解一点。