TeamTalk源码分析之login_server
来源:互联网 发布:软件测试缺陷管理工具 编辑:程序博客网 时间:2024/05/21 19:50
1、login_server启动流程
login_server的启动是从login_server.cpp中的main函数开始的,login_server.cpp所在工程路径为server\src\login_server。下表是login_server启动的大致流程汇总。
出现SIGPIPE的原因:如果尝试send到一个已关闭的 socket上两次,就会出现此信号,也就是用协议TCP的socket编程,服务器是不能知道客户机什么时候已经关闭了socket,导致还在向该已关 闭的socket上send,导致SIGPIPE。而系统默认产生SIGPIPE信号的措施是关闭进程,所以出现了服务器也退出。
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):
在事件分发器中会处理读事件,其他事件处理流程也大致类似,相应代码如下(注意:以下代码只是截取能够说明流程那部分代码,并不完整,...为省略部分):
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数据了,这个代码比较多,就不复制了,简单说一下流程:
- 调用netlib_recv()接收客户端发送的请求,请求长度不能超过1024字节
- 解析http数据信息,解析请求行、请求头、请求体(此次客户端请求无请求体)
- 如果url为"/msg_server"则调用_HandleMsgServRequest(url, content);继续处理,否则关闭连接
- 在_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中。
博客中难免会有错误或者不恰当的地方,恳请读者批评指正。
- TeamTalk源码分析之login_server
- 【TeamTalk】login_server分析
- TeamTalk源码分析(六) —— 服务器端login_server源码分析
- 【TeamTalk】login_server之自定义网络库netlib
- TeamTalk源码分析之msg_server
- TeamTalk源码分析之db_proxy_server
- TeamTalk源码分析之msfs
- TeamTalk源码分析之file_server
- TeamTalk源码分析之login server
- 【TeamTalk】源码分析之服务端描述
- TeamTalk源码分析之win-client
- TeamTalk源码分析之http_msg_server对外提供API
- TeamTalk源码分析之http_msg_server对外提供API
- TeamTalk源码分析之http_msg_server对外提供API
- TeamTalk源码分析(一)—— TeamTalk介绍
- TeamTalk服务端源码解析之DB_Server
- teamtalk源码安装
- TeamTalk服务端分析之服务端以及客户端流程
- 名字的作用域
- ZCMU-1607-大二下之悬梁刺股
- 算法与游戏之OBB碰撞算法
- 我遇到的$.ajaxFileUpload is not a function
- IE条件注释详解
- TeamTalk源码分析之login_server
- VS编程之查看数组信息
- liunx下查看tomcat是否启动/系统日志等
- Java并发编程的艺术(九)——批量获取多条线程的执行结果
- c++ 引用 底层实现机制
- POJ - 2528 线段树 + 离散化
- Xcode8之过滤打印日志
- MFC 修改icon exe图标
- ubuntu16.04 安装composer和 laravel