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

    上一部分将服务器的具体代码的实现介绍给了大家,想必大家也了解到了服务器处理一次消息的复杂度。如果大家能够将各个过程掌握清楚,就会发觉其实整个逻辑与交互过程是比较清晰的。那么服务器与服务器之间的通讯,其实也就是相当于客户端与服务器的通讯又是如何实现的呢?本文将用一个实例来将这个过程展示给大家。

     CODE

    bool ServerManager::connectserver() {
      uint8_t step = 0;
      __ENTER_FUNCTION
        bool result = false;
        pap_server_common_net::packets::serverserver::Connect* connectpacket = NULL;
        pap_common_net::socket::Base* billingsocket = NULL;
        const char *kServerIp = "192.168.200.100";
        const uint16_t kServerPort = 12680;
        billingsocket = billing_serverconnection_.getsocket();
        try {
          result = billingsocket->create();
          if (!result) {
            step = 1;
            Assert(false);
          }
          result = billingsocket->connect(
              kServerIp,
              kServerPort);
          if (!result) {
            step = 2;
            printf("exception 2");
            goto EXCEPTION;
            Assert(false);
          }
          result = billingsocket->set_nonblocking();
          if (!result) {
            step = 3;
            printf("exception 3");
            Assert(false);
          }
    
          result = billingsocket->setlinger(0);
          if (!result) {
            step = 4;
            printf("exception 4");
            Assert(false);
          }
          g_log->fast_save_log(kBillingLogFile,
                               "ServerManager::connectserver()"
                               " ip:%s, port: %d, success",
                               kServerIp,
                               kServerPort);
        }
        catch(...) {
          step = 5;
          Assert(false);
        }
        result = addconnection(
            (pap_server_common_net::connection::Base*)&billing_serverconnection_);
        if (!result) {
          step = 6;
          Assert(false);
        }
        connectpacket = new pap_server_common_net::packets::serverserver::Connect();
        connectpacket->set_serverid(9999);
        connectpacket->set_worldid(0);
        connectpacket->set_zoneid(0);
        result = billing_serverconnection_.sendpacket(connectpacket);
        SAFE_DELETE(connectpacket);
        if (!result) {
          step = 7;
          Assert(false);
        }
        g_log->fast_save_log(kBillingLogFile, 
                             "ServerManager::connectserver() is success!");
        return true;
    EXCEPTION:
        g_log->fast_save_log(
            kBillingLogFile, 
            "ServerManager::connectserver() have error, ip: %s, port: %d, step: %d",
            kServerIp,
            kServerPort,
            step);
        billing_serverconnection_.cleanup();
        return false;
      __LEAVE_FUNCTION
        return false;
    }

      我在这里简单介绍下以上代码:billing_serverconnection_是服务器的主连接,我们以这个连接连接到目标IP为192.168.200.100,port为12680的服务器。连接成功后,我们会发送一个连接信息的包到目标服务器上。可以看出来这个包里设置了三个参数,serverid(服务器id)、worldid(世界id)、zoneid(区域id)。至于这个方法需要放置的位置,必须置于服务器初始化完成后,因为我们要让他创建一个可用的主套接字连接。

      接着我们看看在服务器,执行完这个方法后又进行了哪些处理。

      

    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]);
          serverconnection = &billing_serverconnection_;
          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;
    }
    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;
    }

      特别注意代码中的注释部分,因为验证服务器是等待连接的,没有这种主动连接别的服务器的需要,所以整个功能设计上没有这部分,这个连接的方法也是我临时添加上的。既然是验证服务器的连接发送出去的包,那么对应处理输出流和命令处理自然也应该是这个连接,否则我们的包就发送不出去了。

      下面我们就来看看服务器端对这个包的处理代码(handler):

    #include "server/common/net/packets/serverserver/connect.h"
    #include "server/billing/connection/server.h"
    #include "server/common/base/log.h"
    
    namespace pap_server_common_net {
    
    namespace packets {
    
    namespace serverserver { 
    
    uint32_t ConnectHandler::execute(Connect* packet, 
                                     connection::Base* connection) {
      __ENTER_FUNCTION
        g_log->fast_save_log(kBillingLogFile, 
                             "ConnectHandler::execute(...) serverid: %d ...success", 
                             packet->get_serverid());
        return kPacketExecuteStatusContinue;
      __LEAVE_FUNCTION
        return kPacketExecuteStatusError;
    }
    
    } //namespace serverserver
    
    } //namespace packets
    
    } //namespace pap_server_common_net

      包收到后,我们需要进行一段逻辑处理,这个包是最简单的连接,只是对包里面这个服务器id进行了输出处理。

    RESULT

       服务器启动:

      这个例子中,我以两个服务器作为客户端,分别在windows和linux平台下。

    LINUX

      配置:

      连接失败:

    连接成功:

      服务器接收成功:

    WINDOWS

      配置:

      连接失败:

      连接成功:

      服务器接收成功:

    SERVER

      同时接收连接:

      下一部分将对网络部分的设计进行总结,同时这些文章也会不断更新。

    作者:viticm 出处: http://www.cnblogs.com/lianyue/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】,希望能够持续的为大家带来好的技术文章!想跟我一起进步么?那就【关注】我吧。
  • 相关阅读:
    HDU 5486 Difference of Clustering 图论
    HDU 5481 Desiderium 动态规划
    hdu 5480 Conturbatio 线段树 单点更新,区间查询最小值
    HDU 5478 Can you find it 随机化 数学
    HDU 5477 A Sweet Journey 水题
    HDU 5476 Explore Track of Point 数学平几
    HDU 5475 An easy problem 线段树
    ZOJ 3829 Known Notation 贪心
    ZOJ 3827 Information Entropy 水题
    zoj 3823 Excavator Contest 构造
  • 原文地址:https://www.cnblogs.com/lianyue/p/3586250.html
Copyright © 2011-2022 走看看