zoukankan      html  css  js  c++  java
  • MMORPG大型游戏设计与开发(part3 of net)

    这一部分需要向大家介绍的是服务器的select以及收发包的具体流程,从核心代码功能上分析网络交互具体过程。

      首先大家要看第二部分(part2 of net)的代码结构图,因为在接下来的流程过程中会用到其中模块的名称,若是不知道大致的功能那么接下来的解说可能就成为天书了。

      总体流程为:服务器管理器初始化并创建主套接字连接,进入主循环等待新连接(select),如果有新的连接则将新连接加入连接管理器。不管有没有新的连接,循环会依次处理连接的异常->输入流->输出流->命令处理。其中异常即连接包发送错误的处理,输入流即套接字输入流中如果大小长度不为空则重新拼接包,输出流进行包的拼接,并将未发送的流进行发送,命令处理其实是对输入流的处理,处理(handler)发送过来的包。

      以下详细说明这几个流程从代码上的实现,以及所在的模块。

      1、  服务器管理器初始化(servermanager)

    复制代码
    bool ServerManager::init() {
      __ENTER_FUNCTION
        serversocket_ = 
          new pap_server_common_net::Socket(g_config.billing_info_.port_);
        Assert(serversocket_);
        serversocket_->set_nonblocking();
        socketid_ = serversocket_->getid();
        Assert(socketid_ != SOCKET_INVALID);
        FD_SET(socketid_, &readfds_[kSelectFull]);
        FD_SET(socketid_, &exceptfds_[kSelectFull]);
        minfd_ = maxfd_ = socketid_;
        timeout_[kSelectFull].tv_sec = 0;
        timeout_[kSelectFull].tv_usec = 0;
        threadid_ = pap_common_sys::get_current_thread_id();
        uint16_t i;
        for (i = 0; i < OVER_SERVER_MAX; ++i) {
          serverhash_[i] = ID_INVALID;
        }
        return true;
      __LEAVE_FUNCTION
        return false;
    }
    复制代码

      2、  服务器管理器进入主循环(servermanager)

    复制代码
    void ServerManager::loop() {
      __ENTER_FUNCTION
        while (isactive()) {
          bool result = false;
          try {
            result = select();
            Assert(result);
            //ERRORPRINTF("select");
            result = processexception();
            Assert(result);
            //ERRORPRINTF("processexception");
            result = processinput();
            Assert(result);
            //ERRORPRINTF("processinput");
            result = processoutput();
            Assert(result); 
            //ERRORPRINTF("processoutput");
          }
          catch(...) {
            
          }
          try {
            result = processcommand();
            Assert(result);
            //ERRORPRINTF("processcommand");
          }
          catch(...) {
            
          }
    
          try {
            result = heartbeat();
            Assert(result);
          }
          catch(...) {
          }
        }
      __LEAVE_FUNCTION
    }
    复制代码

      3、  服务器线程进入select模式

    复制代码
    bool ServerManager::select() {
      __ENTER_FUNCTION
        timeout_[kSelectUse].tv_sec = timeout_[kSelectFull].tv_sec;
        timeout_[kSelectUse].tv_usec = timeout_[kSelectFull].tv_usec;
        readfds_[kSelectUse] = readfds_[kSelectFull];
        writefds_[kSelectUse] = writefds_[kSelectFull];
        exceptfds_[kSelectUse] = exceptfds_[kSelectFull];
        pap_common_base::util::sleep(100);
        int32_t result = SOCKET_ERROR;
        try {
          result = pap_common_net::socket::Base::select(
              maxfd_ + 1,
              &readfds_[kSelectUse],
              &writefds_[kSelectUse],
              &exceptfds_[kSelectUse],
              &timeout_[kSelectUse]);
          Assert(result != SOCKET_ERROR);
        }
        catch(...) {
          g_log->fast_save_log(kBillingLogFile, 
                               "ServerManager::select have error, result: %d", 
                               result);
        }
        return true;
      __LEAVE_FUNCTION
        return false;
    }
    复制代码

      4、  服务器线程进行异常处理

    复制代码
    bool ServerManager::processexception() {
      __ENTER_FUNCTION
        if (SOCKET_INVALID == minfd_ && SOCKET_INVALID == maxfd_)
          return true;
        uint16_t connectioncount = billingconnection::Manager::getcount();
        billingconnection::Server* serverconnection = NULL;
        uint16_t i;
        for (i = 0; i < connectioncount; ++i) {
          if (ID_INVALID == connectionids_[i]) continue;
          serverconnection = g_connectionpool->get(connectionids_[i]);
          Assert(serverconnection);
          int32_t socketid = serverconnection->getsocket()->getid();
          if (socketid_ == socketid) {
            Assert(false);
            continue;
          }
          if (FD_ISSET(socketid, &exceptfds_[kSelectUse])) {
            removeconnection(serverconnection);
          }
        }
        return true;
      __LEAVE_FUNCTION
        return false;
    }
    复制代码

      5、  服务器线程进行输入流处理

    复制代码
    bool ServerManager::processinput() {
      __ENTER_FUNCTION
        if (SOCKET_INVALID == minfd_ && SOCKET_INVALID == maxfd_)
          return true; //no connection
        uint16_t i;
        if (FD_ISSET(socketid_, &readfds_[kSelectUse])) {
          for (i = 0; i < kOneStepAccept; ++i) {
            if (!accept_newconnection()) break;
          }
        }
        uint16_t connectioncount = billingconnection::Manager::getcount();
        for (i = 0; i < connectioncount; ++i) {
          if (ID_INVALID == connectionids_[i]) continue;
          billingconnection::Server* serverconnection = NULL;
          serverconnection = g_connectionpool->get(connectionids_[i]);
          Assert(serverconnection);
          int32_t socketid = serverconnection->getsocket()->getid();
          if (socketid_ == socketid) continue;
          if (FD_ISSET(socketid, &readfds_[kSelectUse])) { //read information
            if (serverconnection->getsocket()->iserror()) {
              removeconnection(serverconnection);
            }
            else {
              try {
                if (!serverconnection->processinput()) 
                  removeconnection(serverconnection);
              }
              catch(...) {
                removeconnection(serverconnection);
              }
            }
          }
        }
        return true;
      __LEAVE_FUNCTION
        return false;
    }
    复制代码

      6、  服务器线程进行输出流处理

    复制代码
    bool ServerManager::processoutput() {
      __ENTER_FUNCTION
        if (SOCKET_INVALID == maxfd_&& SOCKET_INVALID == minfd_)
          return false;
        uint16_t i;
        uint16_t connectioncount = billingconnection::Manager::getcount();
        for (i = 0; i < connectioncount; ++i) {
          if (ID_INVALID == connectionids_[i]) continue;
          billingconnection::Server* serverconnection = NULL;
          serverconnection = g_connectionpool->get(connectionids_[i]);
          Assert(serverconnection);
          int32_t socketid = serverconnection->getsocket()->getid();
          if (socketid_ == socketid) continue;
          if (FD_ISSET(socketid, &writefds_[kSelectUse])) {
            if (serverconnection->getsocket()->iserror()) {
              removeconnection(serverconnection);
            }
            else {
              try {
                if (!serverconnection->processoutput()) 
                  removeconnection(serverconnection);
              }
              catch(...) {
                removeconnection(serverconnection);
              }
            }
          }
        }
        return true;
      __LEAVE_FUNCTION
        return false;
    }
    复制代码

      7、  服务器线程进行命令处理

    复制代码
    bool ServerManager::processcommand() {
      __ENTER_FUNCTION
        if (SOCKET_INVALID == maxfd_&& SOCKET_INVALID == minfd_)
          return false;
        uint16_t i;
        uint16_t connectioncount = billingconnection::Manager::getcount();
        for (i = 0; i < connectioncount; ++i) {
          if (ID_INVALID == connectionids_[i]) continue;
          billingconnection::Server* serverconnection = NULL;
          serverconnection = g_connectionpool->get(connectionids_[i]);
          //serverconnection = &billing_serverconnection_;
          Assert(serverconnection);
          int32_t socketid = serverconnection->getsocket()->getid();
          if (socketid_ == socketid) continue;
          if (serverconnection->getsocket()->iserror()) {
            removeconnection(serverconnection);
          }
          else { //connection is ok
            try {
              if (!serverconnection->processcommand(false)) 
                removeconnection(serverconnection);
            }
            catch(...) {
              removeconnection(serverconnection);
            }
          }
        }
        return true;
      __LEAVE_FUNCTION
        return false;
    }
    复制代码

      下一部分,我将讲解在网络部分一些重要的代码块。

  • 相关阅读:
    POJ 3140 Contestants Division (树dp)
    POJ 3107 Godfather (树重心)
    POJ 1655 Balancing Act (树的重心)
    HDU 3534 Tree (经典树形dp)
    HDU 1561 The more, The Better (树形dp)
    HDU 1011 Starship Troopers (树dp)
    Light oj 1085
    Light oj 1013
    Light oj 1134
    FZU 2224 An exciting GCD problem(GCD种类预处理+树状数组维护)同hdu5869
  • 原文地址:https://www.cnblogs.com/liuzhi/p/4084565.html
Copyright © 2011-2022 走看看