客户端打开
客户端打开服务器是怎么知道连接的 1.客户端调用NGP的接口 TcpLinkEx::TcpLinkEx() { auto ser = GetPlug(LibEvtServer); if(!ser) { ser = NEW(LibEvtServer); SetPlug("LibEvtServer", ser); ser->init(&g_TcpSerEvt, 0, 5000);//主要是初始化线程,和创建base int port = 2000; while(1) { bool hr = ser->listen(&port); if(hr) break; port++; } } m_tcpServer.reset(ser); } 2.调用ConnectServer接口 bool NGP::ConnectServer(const char* pIp, unsigned int nPort) { std::shared_ptr<I_SaveOption> so = NEWSP(SaveOption); std::wstring path = Plug::GetCurrentPath() + L"option.xml"; so->openFile(path.c_str()); //连接服务器 std::string my_ip = Plug::wcstombs(so->getStr(L"root.server.ip")); int port = so->getInt(L"root.server.port"); //int i_ip = ntohl(inet_addr(my_ip.c_str())); int i_ip; if( strcmp( pIp, "127.0.0.1")) { i_ip = htonl(inet_addr(pIp)); } else { i_ip = htonl(inet_addr(my_ip.c_str())); nPort = port; } m_nPort = nPort; m_nIp = i_ip; return _TcpLink->Connect(i_ip, nPort); } bool LibEvtServer::connet_to(int ip, int port) { auto bev = bufferevent_socket_new(m_base, -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE); //创建基于SOCKET的bufferevent if (!bev){ std::cout << "bufferevent_socket_new error!" << std::endl; return false; } struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_addr.s_addr = ntohl(ip); sin.sin_port = htons(port); //连接相当于socket里面的connect,此处connect,net进程的监听器就会就会收到通知 int hr = bufferevent_socket_connect(bev, (struct sockaddr*)&sin, sizeof(sin)); if(0 != hr) return false; std::lock_guard<std::mutex> lock(m_offline_mtx);//跟申请释放channel id互锁 auto c2 = CreateChannel(bev); bufferevent_setcb(bev, conn_readcb, conn_writecb, conn_eventcb, c2);//设置读写事件 bufferevent_enable(bev, EV_READ | EV_WRITE); return true; } 3.net进程收到通知就会把这个socketfd放到轮询线程队列中,顺便想socketpair中写入一字节的数据 此时线程的读事件会调用,从socketpair中读取这个一个字节的数据 { ........ //创建基于socket的bufferevent auto bev = bufferevent_socket_new(plt->thread_base, item.fd, BEV_OPT_THREADSAFE); Channel2* c2 = CreateChannel(bev); //设置接收、状态改变 回调函数 bufferevent_setcb(bev, conn_readcb, NULL, conn_eventcb, c2); bufferevent_enable(bev, EV_READ | EV_WRITE ); } 4.在createChannel中会产生回调 void TCPServer::on_connect(int channel_id) { packet pkt; pkt.size = (int)link_stat::link_connected; pkt.channel_id = channel_id; pkt.is_data = false; from_net_push_pkt(pkt);//会将包放入TCPServer的无锁队列中 } 5.net进程中的线程取包,然后放入共享内存中 net进程在创建的时候会创建线程专门中无锁队列中取包然后放入共享内存中 std::thread thr([this]() { for (;;) { fromNet2Mem(); } }); bool NetProcSvr::fromNet2Mem() { packet pkt; pkt.data = m_rev_buffer; auto hr2 = m_spTCPServer->recv(pkt); if(!hr2) { boost::this_thread::interruptible_wait(1); return true; } shareData sd; sd.channel_id = pkt.channel_id; sd.data = pkt.data; sd.is_data = pkt.is_data; sd.size = pkt.size; //其实这个语句的过程 //从共享的维护索引的队列中pop一个,然后根据其中存储的索引获取对应共享内存的地址,但这块内存没有用到,因为这只是个连接,没有数据 //将这个从共享内存里面pop出来的东西放到push到共享的队列中,此时算是放到了共享队列里面去了 auto hr = m_spShareMemInter->pushA(sd); if(hr) return true; int count = 0; while(1) { hr = m_spShareMemInter->pushA(sd); if(hr) break; if(++count > 100) { MessageBoxA(nullptr, "Net进程:fromNet2Mem,向共享内存中pushA失败超过100次!", "提示", MB_ICONWARNING); count = 0; } boost::this_thread::interruptible_wait(1); } return true; } 6.GS从共享内存中去取共享内存 { .... //网络事件 link_stat stat = (link_stat)pkt.size; if (stat == link_stat::link_connected) { auto gc = new GameChannel(); m_Channels[pkt.channel_id] = gc;//有玩家连接 gc->m_channel_id = pkt.channel_id; gc->m_DataLayer = m_spDataLayer.get(); gc->m_share = this; gc->m_asyndb = this->m_asyndb.get(); //gc->m_db = this->m_asyndb->getSynDBptr();//把地址复制一份给GameChannel::m_db,让其具有数据库操作权 m_live_mgr.add(pkt.channel_id);//将其放入live_mgr中 } } 7.然后那个界面的应该是定时的调用 void __stdcall GameServer::get_info(GSinfo& info) { info.clientNum = m_live_mgr.get_links();//这个就是现实的链接数 m_spDataLayer->get_buffer_num(info.sendBuffNum, info.revBuffNum); } //想到客户端打开就做了这么多事情,记得刚开始过来的时候,队长叫我从上面的这个地方看是看,懵懵懂懂看了一个月还是啥也不知道。 //中途一直想把客户端服务器这个连接的过程串起来,现在终于串起来了