login_server是TeamTalk的登录服务器,负责分配一个负载较小的MsgServer给客户端使用,按照新版TeamTalk完整部署教程来配置的话,login_server的服务端口就是8080,客户端登录服务器地址配置如下(这里是win版本客户端):

1、login_server启动流程

  login_server的启动是从login_server.cpp中的main函数开始的,login_server.cpp所在工程路径为server\src\login_server。下表是login_server启动的大致流程汇总。

signal(SIGPIPE, SIG_IGN)

忽略SIGPIPE信号,向一端关闭的socket写数据会触发该信号

CconfigFileReader:: CconfigFileReader()

读取login_server.conf配置文件

netlib_init()

初始化网络连接,Linux下无操作

netlib_listen(client_listen_ip和port)

貌似是让msg_server建立连接用的,不过不是很清楚

netlib_listen(msg_server_listen_ip和port)

监听msg_server连接,msg_server会主动与login_server建立连接

netlib_listen(http_listen_ip和port)

监听客户端连接,客户端首先会与login_server建立连接。listen端口时,会设置回调函数,并把相应事件添加到事件监听器中

CBaseSocket::Listen()

底层listen函数

init_login_conn();

init_http_conn();

一些初始化操作,比如会添加定时事件

netlib_eventloop

进入事件循环(接收请求/发送响应)

2、客户端连接login_server流程

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

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

 void CEventDispatch::StartDispatch(uint32_t wait_timeout)
{
...
while (running)
{
nfds = epoll_wait(m_epfd, events, , wait_timeout);
for (int i = ; i < nfds; i++) {
...
if (events[i].events & EPOLLIN) {
pSocket->OnRead();
}
_CheckTimer();
_CheckLoop();
}
...

  到达OnRead()时,流程如下:

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

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

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

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

 void httpconn_callback(void* callback_data, uint8_t msg, uint32_t handle, uint32_t uParam, void* pParam)
{
// convert void* to uint32_t, oops
uint32_t conn_handle = *((uint32_t*)(&callback_data));
CHttpConn* pConn = FindHttpConnByHandle(conn_handle);
if (!pConn) {
return;
} switch (msg) {
case NETLIB_MSG_READ:
pConn->OnRead();
break;
case NETLIB_MSG_WRITE:
pConn->OnWrite();
break;
case NETLIB_MSG_CLOSE:
pConn->OnClose();
break;
...

  流程走到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数据格式如下:

 {
"backupIP" : "192.168.1.150",
"code" : 0,
"discovery" : "http://192.168.1.150/api/discovery",
"msfsBackup" : "http://192.168.1.150:8700/",
"msfsPrior" : "http://192.168.1.150:8700/",
"msg" : "",
"port" : "8000",
"priorIP" : "192.168.1.150"
}

3、小结

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

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

  博客中难免会有错误或者不恰当的地方,恳请读者批评指正。

参考

  1、新版TeamTalk完整部署教程

  2、TeamTalk源码分析之服务端描述

TeamTalk源码分析之login_server的更多相关文章

  1. TeamTalk源码分析(十一) —— pc客户端源码分析

           --写在前面的话  在要不要写这篇文章的纠结中挣扎了好久,就我个人而已,我接触windows编程,已经六七个年头了,尤其是在我读研的三年内,基本心思都是花在学习和研究windows程序上 ...

  2. TeamTalk源码分析之服务端描述

    TTServer(TeamTalk服务器端)主要包含了以下几种服务器: LoginServer (C++): 登录服务器,分配一个负载小的MsgServer给客户端使用 MsgServer (C++) ...

  3. TeamTalk源码分析(十) —— 开放一个TeamTalk测试服务器地址和几个测试账号

    由于TeamTalk是用于企业内部的即时通讯软件,一般客户端并不提供账号注册功能.如果你仅对TeamTalk的客户端感兴趣,你可以仅仅研究pc端和移动端代码.官方的测试服务器地址已经失效,所以我已经部 ...

  4. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  5. HashMap与TreeMap源码分析

    1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...

  6. nginx源码分析之网络初始化

    nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...

  7. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  8. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  9. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

随机推荐

  1. 【追寻javascript高手之路01】javascript参数知多少?

    前言 我最近在思考一个问题,我本身平时还是积累了不少东西,面试时候问的东西基本逃不出写的博客(当然,高级阶段的就不行了),但是真的被问到时我却不一定答得上来. 知道且能回答,回答的效果都不是很好... ...

  2. QQ拼音输入法 该到放弃的时候了

    一直使用QQ拼音的纯净版,后来给了搜狗了,纯净版和普通版已经变成同一个版本了... 不过. 不过,2016.03.16更新版本后,重启后居然还弹出这样的窗口: 不知道怎么设置,每次重启后就弹出! 卸载 ...

  3. strurts2入门

    MVC :struts2是一个经典开源的mvc框架.主要负责控制器(Controller)   处理请求特点: 核心控制器:  +Actions; 与ServletAPI解耦合: 非单例,线程安全. ...

  4. AE,按照属性值关系选择要素

    if(axMapControl2.LayerCount<=0) { MessageBox.Show("请加载图层后使用该功能","系统提示",Messag ...

  5. [outlook]打开以后就自动进入安全模式的解决方法。Outlook start in safe mode.

    给客户写了一个Outlook的add-in, 用现在时髦的话应该叫outlook的app. 这个add-in的作用就是把outlook中的email,直接上传到SharePoint中.想要代码的联系我 ...

  6. 超人学院二期学员分享hadoop工作经验

    定于2月17日--2月23日的某一天,邀请咱们学员分享一下hadoop工作经验.对于没工作过的同学或者没从事过hadoop工作的同学,抓住机会啊,你可以提前准备自己关心的各种问题! 具体时间请关注QQ ...

  7. Sharepoint学习笔记—习题系列--70-576习题解析 -(Q95-Q98)

    Question  95  Your development team has asked you to help them determine the cause of an error in a ...

  8. Android JNI 和 NDK

    1.Android NDK 一.NDK产生的背景 Android平台从诞生起,就已经支持C.C++开发.众所周知,Android的SDK基于Java实现,这意味着基于Android SDK进行开发的第 ...

  9. 终极指南:如何为iOS8应用制作预览视频

    最近一两个月里,苹果的世界里出现了很多新东西,比如屏幕更大的iPhone 6,可穿戴设备Apple Watch,iOS8,以及旨在帮助用户更好的发现应用的App Store改版等等. 说到App St ...

  10. JAVA IO 字节流与字符流

    文章出自:听云博客 题主将以三个章节的篇幅来讲解JAVA IO的内容 . 第一节JAVA IO包的框架体系和源码分析,第二节,序列化反序列化和IO的设计模块,第三节异步IO. 本文是第一节.     ...