TeamTalk源码分析之login server

客户端连接login_server流程

login_server在启动之后就进入了事件循环中,即netlib_eventloop,而netlib_eventloop实际调用的是CEventDispatch::Instance->StartDispatch(wait_timeout)(我们简称为事件分发器),事件分发器在Linux下使用epoll,会处理读事件、写事件、异常等事件,当客户端建立连接时,相当于是读事件。客户端发送请求格式如下(ps:下图为wireshark抓包结果,运行TeamTalk的主机IP是192.168.1.150):

在事件分发器中会处理读事件,其他事件处理流程也大致类似,相应代码如下(注意:以下代码只是截取能够说明流程那部分代码,并不完整,...为省略部分):

 1 void CEventDispatch::StartDispatch(uint32_t wait_timeout)
 2 {
 3 ...
 4     while (running)
 5     {
 6         nfds = epoll_wait(m_epfd, events, 1024, wait_timeout);
 7         for (int i = 0; i < nfds; i++) {
 8 ...
 9 if (events[i].events & EPOLLIN) {
10 pSocket->OnRead;
11  }
12         _CheckTimer;
13         _CheckLoop;
14     }
15 ...

到达OnRead时,流程如下:

 1 void CBaseSocket::OnRead
 2 {
 3     if (m_state == SOCKET_STATE_LISTENING) {
 4         _AcceptNewSocket;
 5     }
 6     else {
 7         u_long avail = 0;
 8         // 得到缓冲区中有多少个字节要被读取,然后将字节数放入b里面。
 9         if ( (ioctlsocket(m_socket, FIONREAD, &avail) == SOCKET_ERROR) || (avail == 0) ) {
10  m_callback(m_callback_data, NETLIB_MSG_CLOSE, (net_handle_t)m_socket, NULL);
11         }
12         else {
13  m_callback(m_callback_data, NETLIB_MSG_READ, (net_handle_t)m_socket, NULL);
14         }
15     }
16 }
 1 // 接收一个新连接
 2 void CBaseSocket::_AcceptNewSocket
 3 {
 4 ...
 5     // accept为非阻塞的,所以这里可以用while循环
 6     while ( (fd = accept(m_socket, (sockaddr*)&peer_addr, &addr_len)) != INVALID_SOCKET ) {
 7         CBaseSocket* pSocket = new CBaseSocket;
 8 ...
 9         pSocket->SetSocket(fd);
10         pSocket->SetCallback(m_callback);
11         pSocket->SetCallbackData(m_callback_data);
12         pSocket->SetState(SOCKET_STATE_CONNECTED); // 设置m_state状态为建立连接
13         pSocket->SetRemoteIP(ip_str);
14         pSocket->SetRemotePort(port);
15 
16         _SetNoDelay(fd);
17         _SetNonblock(fd);
18         AddBaseSocket(pSocket);
19         CEventDispatch::Instance->AddEvent(fd, SOCKET_READ | SOCKET_EXCEP);
20         // 这里会调用回调函数
21         m_callback(m_callback_data, NETLIB_MSG_CONNECT, (net_handle_t)fd, NULL);
22     }
23 }

最后到达启动流程中在监听http_listen中设置的回调函数,流程如下:

1 void http_callback(void* callback_data, uint8_t msg, uint32_t handle, void* pParam)
2 {
3     if (msg == NETLIB_MSG_CONNECT) {
4         CHttpConn* pConn = new CHttpConn;
5         pConn->OnConnect(handle);
6     }
7 ...
 1 // 建立连接成功后读取数据
 2 void CHttpConn::OnConnect(net_handle_t handle)
 3 {
 4     m_sock_handle = handle;
 5     m_state = CONN_STATE_CONNECTED;
 6     g_http_conn_map.insert(make_pair(m_conn_handle, this));
 7     
 8     // 这里重新设置回调函数为httpconn_callback
 9     netlib_option(handle, NETLIB_OPT_SET_CALLBACK, (void*)httpconn_callback);
10     netlib_option(handle, NETLIB_OPT_SET_CALLBACK_DATA, reinterpret_cast<void *>(m_conn_handle) );
11     netlib_option(handle, NETLIB_OPT_GET_REMOTE_IP, (void*)&m_peer_ip);
12 }

当读取客户端发送上来的数据时,会到达事件监听函数并且是读事件,这样会到达CBaseSocket::OnRead中,然后就会调用设置好的回调函数,即httpconn_callback函数中,最后调用OnRead中,其流程如下:

 1 void httpconn_callback(void* callback_data, uint8_t msg, uint32_t handle, uint32_t uParam, void* pParam)
 2 {
 3     // convert void* to uint32_t, oops
 4     uint32_t conn_handle = *((uint32_t*)(&callback_data));
 5     CHttpConn* pConn = FindHttpConnByHandle(conn_handle);
 6     if (!pConn) {
 7         return;
 8     }
 9 
10     switch (msg) {
11     case NETLIB_MSG_READ:
12         pConn->OnRead;
13         break;
14     case NETLIB_MSG_WRITE:
15         pConn->OnWrite;
16         break;
17     case NETLIB_MSG_CLOSE:
18         pConn->OnClose;
19         break;
20 ...

流程走到CHttpConn::OnRead,表示login_server准备读取客户端发送的http数据了,这个代码比较多,就不复制了,简单说一下流程:

  1. 调用netlib_recv接收客户端发送的请求,请求长度不能超过1024字节
  2. 解析http数据信息,解析请求行、请求头、请求体(此次客户端请求无请求体)
  3. 如果url为"/msg_server"则调用_HandleMsgServRequest(url, content);继续处理,否则关闭连接
  4. 在_HandleMsgServRequest中会选择一个msg_server来,并把该msg_server信息作为应答体发送回客户端,这样客户端就用收到的msg_server信息建立新的连接。注意:应答体格式为json格式的。

响应客户端的json数据格式如下:

 1 {
 2    "backupIP" : "192.168.1.150",
 3    "code" : 0,
 4    "discovery" : "http://192.168.1.150/api/discovery",
 5    "msfsBackup" : "http://192.168.1.150:8700/",
 6    "msfsPrior" : "http://192.168.1.150:8700/",
 7    "msg" : "",
 8    "port" : "8000",
 9    "priorIP" : "192.168.1.150"
10 }

3、小结

OK,到这里login_server已经启动完成并且开始工作了(进入事件循环),login_server只是TeamTalk中一个小的模块,它只负责等待客户端的连接,服务端口是8080,如果客户端发送数据格式正确,则分配一个负载相对较小的msg_server给客户端,它相当于是客户端与msg_server之间的连接模块。msg_server才是TeamTalk的核心模块,这个等到后续博客在分析...

TeamTalk底层网络库是自己实现的,相应源码在server\src\base下的netlib.h和netlib.cpp中。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值