一、Reactor的基本概念
一、问题回顾
1、端口复用、地址复用使用什么函数进行设置?参数是什么?
2、IO多路复用的形式有哪三种?
3、select对应的函数接口、使用方式如何?有什么特点?优缺点?
4、poll对应的函数接口、使用方式如何?有什么特点?优缺点?
5、epoll的函数借楼、使用方式如何?有什么特点?优缺点?
6、有哪五种网络IO模型?
二、ReactorV1版本
1、类的设计过程
Socket类:所有与套接字相关的操作全部封装到该类中。包括:套接字的创建、套接字的关闭、套接字的获取。
InetAddress类:将所有与地址相关的操作全部封装到该类中。包括:ip地址的获取、端口号的获取以及通过ip与端口号创建InetAddress类的对象、包括struct sockaddr_in变量的获取。
Acceptor类:将所有服务器的主要函数全部封装到该类中。包括:地址复用、端口复用、bind函数、listen函数、accept函数。只要Acceptor类调用了accept函数就表明三次握手建立成功。
TcpConnection类:如果Acceptor类调用accept函数有正确的返回结构,就表明三次握手建立成功,就可以创建一条连接,该连接就是TcpConnection连接,就可以用该连接发送数据,即send数据,与接收数据,即receive数据。
SocketIO类:该类的作用就是为了完成数据的真正的收发。也就是完成系统调用read/recv/write/send的封装。还需要具体进行封装数据的收发数据量。
首先,客户端与服务器进行连接,就是客户端发送数据,服务器接收数据,然后服务器对数据进行处理后再发送给客户端。很明显,首先便是需要创建套接字连接,然后找到对应的ip地址和端口号,确保发送成功。然后服务器进行操作。就像bind,listen,accept函数都调用一遍,而且一般来说肯定不只是一台客户端,所以你应该支持多台客户端都可以与服务器进行连接,这样你就需要进行地址复用和端口复用。然后,建立连接后,就需要对数据进行操作,发送数据和接收数据,发送数据是send函数,接收数据是receive函数。除此之外,这些数据还需要进行处理,这个处理就是读写操作。
根据上面这整个流程,我们可以将它分开,比如,与套接字有关的抽象成一个类,与地址有关的抽象成一个类,还有服务器的主要函数抽象一个类,还有你建立连接的过程也抽象一个类,还有根据单一功能原则,数据的封装和修改这里最好也抽象成一个类。那么这个过程就可以根据面向对象的方法抽象成几个不同的类,然后再进行操作。
2、类图设计
3、重难点
==recv使用MSG_PEEK数据是从内核态缓存区拷贝到用户态缓冲区,但是数据在内核态缓冲区中还存在。如果直接使用read,那么数据会从内核态缓冲区被读取到用户态,并给数据在内核态会被清空。==
一、ReactorV2版本(==重难点==)
1、TCP网络编程最本质的是处理三个半事件
连接建立:包括服务器端被动接受连接(accept)和客户端主动发起连接(connect)。TCP连接一旦建立,客户端和服务端就是平等的,可以各自收发数据。(三次握手)
连接断开:包括主动断开(close、shutdown)和被动断开(read()返回0)。(四次挥手)
消息到达:文件描述符可读。这是最为重要的一个事件,对它的处理方式决定了网络编程的风格(阻塞还是非阻塞,如何处理分包,应用层的缓冲如何设计等等)。
消息发送完毕:这算半个。对于低流量的服务,可不必关心这个事件;另外,这里的“发送完毕”是指数据写入操作系统缓冲区(内核缓冲区),将由TCP协议栈负责数据的发送与重传,不代表对方已经接收到数据。
2、类图设计
3、代码难点(==重难点==)
3.1、键值对的数据成员
3.2、EventLoop中三个回调数据成员
3.3、EventLoop中三个回调的注册
3.4、EventLoop构造函数中监听 fd
3.5、获取vector首元素的地址
3.6、waitEpollFd的实现
3.7、handleNewConnection的实现(==重要==)
3.8、EventLoop中的handleMessage
3.9、TcpConnection中注册回调函数做数据成员
3.10、TcpConnection中的三个回调函数的注册
3.11、防止智能指针的误用
二、ReactorV3版本
1、类图的设计
第三个版本就是在第二个版本的基础上进行了封装。
2、代码解析
2.1、子对象的初始化
2.2、服务器的启动
2.3、三个回调函数同时注册
三、ReactorV4版本
1、v3版本的瓶颈
当业务逻辑比较复杂的时候,就需要CPU大量参与进来,但是本版本中,接收数据、传输数据以及发送数据是串行执行的,所以需要将业务逻辑的处理交给线程池做。
2、V4版本的逻辑图
四、eventfd的使用
1、作用
eventfd可以在进程或者线程之间进行通信。
2、函数接口
1 |
|
write操作eventfd返回的文件描述符,可以将内核计数器进行累加,但是如果只要read一次eventfd返回的文件描述符,就可以将内核计数器的值清空。
同时我们看成eventfd可以在进程之间进行通信。
3、封装eventfd
3.1、类图
3.2、代码难点
3.2.1、EventFd的start函数实现
3.2.2、自己构建新的线程,然后主子线程之间通信
一、ReactorV4版本
1、类图设计
2、代码难点
2.1、TcpConnection中的sendInLoop函数
2.2、EventLoop中的runInLoop
2.3、EventLoop中的doPengdingFunctors
2.4、测试代码中的回调函数onMessage与线程池的关系
2.5、MyTask中的process的分析
3、流程图
一、ReactorV5版本
1、类图设计
二、timerfd的封装
1、timerfd的基本特征
timerfd是Linux提供的一个定时器接口。这个接口基于文件描述符,通过文件描述符的可读事件进行超时通知,所以能够被用于select/poll/epoll的应用场景
2、函数接口
1 |
|