在看之前需要知道,本篇文章以Login_server为主体分析
对于监听fd(也就是listenfd)的回调函数的接口是不同的
(有http_callback,msg_serv_callback,client_callback三种接口),而普通的fd的回调函数的统一接口是imconn_callback(除了客户端请求登录的http连接的接口是httpconn_callback),记住imconn_callback,非常重要。
在看了上一篇文章后TeamTalk登录流程分析就会知道httpconn_callback是干什么用的了,这里主要对imconn_callback进行分析。
每一个client连接时,都会给它分配一个CBaseSocket对象和一个CLoginConn对象,BashSocket对象用于保存处理一些基本信息,比如对方和自己的ip和port,和读写函数(也就是下边说到的pSocket->OnRead()),这里面可以看到调用了 m_callback , 这个回调函数绑定的就是我们最开始说的imconn_callback,在imconn_callback里面可以通过fd找到CLoginConn对象再调用CLoginConn对象处理具体消息),具体的对数据的处理,比如读写缓冲区,解析PDU数据等,都在CLoginConn对象中。
当epoll_wait触发了可读事件的整个流程任意选:
先只看函数, 理清逻辑,再看注释
pSocket->OnRead();//EventDispatch.cpp中386行
m_callback(m_callback_data, NETLIB_MSG_READ, (net_handle_t)m_socket, NULL);
//这个m_callback函数是fd最初设置的回调函数,一般是imconn_callback(除了客户端登录连接)
/*参数解析:对于listenfd,
m_callback(m_callback_data(回调额外参数), 消息类型,对方给的fd, NULL),
但如果是其他普通fd,这里的第三个参数就是自己的fd,第一个参数m_callback_data一般是一个hash映射表*/
//再进入imconn_callback中可以看到
CImConn* pConn = FindImConn(conn_map, handle);//通过fd取出Conn对象,CImConn是所有Conn对象的基类, 这里我们是CLoginConn对象,
pConn->OnRead();//里面会调用HandlePdu,
HandlePdu();//这是一个子类重写的方法,
所以会去到CLoginConn对象的HandlePdu处理方法,这里面就是具体对PDU的处理
总结分析
一定不要把OnRead,OnWrite等 跟 fd对应的回调函数(在BaseSocket.h中可以找到回调函数对象m_callback,一般给它绑定的是imconn.cpp中的imconn_callback函数) 搞混了,fd对应的回调函数只有一个,而OnRead,OnWrite等只是CBaseSocket对象或者CLoginConn对象具有的功能。
可以这么理解,每一个fd都绑定了两个处理对象(一个CBaseSocket对象,一个CLoginConn对象), 对于CBaseSocket对象主要用于epoll_wait中当检测到消息后,统一调用CBaseSocket对象的读或者写函数(比如上边的pSocket->OnRead(),可以理解用CBaseSocket对象检测消息的发生,以及保存一些基本信息,如ip,port,m_callback对象等),m_callback对象虽然是在CBaseSocket对象中,但其实它在哪个对象中一点不重要,重要的是我们要知道CLoginConn对象只是一个fd的服务对象,(可以理解为CLoginConn对象就是用来处理fd接收和发送具体消息的),并且一般m_callback对象绑定的接口统一都是imconn_callback(除了客户端请求登录的http连接的接口是httpconn_callback)。
至此应该能够明白一个fd 是如何做封装的了
知识补充
对于imconn.cpp, 每个客户来连接Login_server时,绑定的回调函数就是这里面的imconn_callback, 同时给客户设置的callback_data就是在LoginConn.cpp中的g_client_conn_map哈希表,所以在客户的回调函数中,可以通过g_client_conn_map找到对应的那条连接。我们可以把CLoginConn的一个对象当作一个真正的连接
在BaseSocket.cpp中, g_socket_map哈希表保存了每一个fd和CBaseSocket对象的映射(fd->CBaseSocket*);
在LoginConn.cpp中, 有g_client_conn_map保存了每一个fd的个CLoginConn对象的映射(fd->CLoginSocket*);
netlib_option(客户fd,需要进行的操作(如绑定回调函数,设置回调函数附加的参数等),具体的回调函数和回调函数附加的参数(一般是一个hash表), 并且在netlib.cpp中包含了BaseSocket头文件,等同于拥有了g_socket_map哈希表, 所以通过这个哈希表获得对应的socket才能对回调函数即额外参数进行设置
对于login_server.cpp, 只有一个epoll_wait, 但涉及到多个服务,比如普通的client连接,以及http登录连接,这些服务对应的listenfd绑定了EPOLLIN事件,当事件发生时,先通过FindBaseSocket(ev_fd), 找到对应的listenfd的pSocket对象,然后调用pSocket->OnRead();
对于pSocket->OnRead()里面有哪些消息类型,存储在ostype.h里面
对于fd的回调函数m_callback中第二个参数涉及的类型在netlib.h中
int netlib_option(net_handle_t handle, int opt, void* optval) 中, 如果opt是NETLIB_OPT_SET_CALLBACK,则optval是一个函数, 如果opt是NETLIB_OPT_SET_CALLBACK_DATA,则optval是一个全局hash表.