FAQ - Multiplayer and Network Programming-Part1(原创转译)

  1. 想开始学习网络编程。该从哪儿开始?
    答:试一下Beej's Networking Tutorials. 你也能够right here on this site.
    Sockets编程(Windows相关)疑问还可以查看 WinSock FAQ , GameDev.net WinSock tutorial.
    最后, MSDN section on WinSock 有相当好的介绍,并且也是不错的技术参考文档。

  2. 应该使用DirectPlay还是WinSock?
    答:在“Summer 2004"的DX更新版本中, Microsoft把DirectPlay放置到了淘汰状态, 他们不再开发该模型更多的API了。Microsoft现在建议你使用WinSock来开发网络程序, until the re-cast X-box Live! technology is rolled out for PCs, probably around Longhorn time-frame.如果你在Linux, MacOS, 或者一些其它类似的平台上开发,则更容易:你只需使用标准的sockets来工作。幸运的是,如果你使用WinSock的Berkeley函数并且避免WSA函数,那么sockets和WinSock代码会看起来很相似。经过少量的思考,你就可以编写出非常容易移植的程序。
    记住在你的win32程序中包含"<winsock2.h>"于"<windows.h>"之后,否则编译会出错。在你的WinSock程序中总是应该使用WSAStartup( MAKEWORD(2,2), ... )来取得2.2版本的WinSock,因为旧的版本有缺陷和局限,而你当然不会想自己来处理这些问题。最后,在使用WinSock的时候你需要链接ws2_32.lib程序库。

  3. 应该使用TCP还是UDP?
    答:至少有四种类型的游戏可以被考虑网络化:回合制游戏,实时战略游戏(RTS),角色扮演游戏(RPG),动作游戏。简而言之,如果基于回合制,可以用TCP因为它简单,unless you need peer-to-peer NAT punch-through. 对于RTS游戏, 最好使用TCP,虽然UDP在极端的条件下是更明智的选择。对于RPG游戏,事情不太清晰---基于动作类的RPG游戏包含大量的动力学,像City of Heroes, 使用UDP, 但是较慢的RPGs和MUDs通常还是用TCP。基于动作的游戏,像第一人称设计游戏或者赛车游戏,你绝对应该使用UDP。
  4. 如果TCP能够保证数据正确的到达,为什么还要用没有这种保证的UDP?
    答:TCP是基于数据流的协议。从一端流进,另一端流出,按次序、没有副本或数据包碎片。当正确性是非常重要的时候则很方便。但是,假设有一个数据包丢失,在检测到丢包并且重发之前,为了取得这种保证,TCP必须挂起所有准备递送到接受端的数据报。因为一个数据包的丢失,可能会引起数秒的延迟。
  5. UDP是不是比TCP快?
    答: UDP使用数据包的头部分比TCP更小巧,这样能够让程序在窄带(例如modem)下有更大的数据吞吐量。UDP会立即递交收到的数据包而不需要等待重新对数据包排序,所以如果延迟比正确性更重要则优先考虑它,UDP会减少游戏的停顿,让游戏运行得更加平滑。
  6. 使用TCP发送数据,但我的Ping值异常的高!看起来数据会在某些地方慢下去。这是什么问题?
    答:在TCP连接上发送数据时,网络数据包典型的实现是一种被称为Nagle's的算法。这使得如果程序在一次独立的写操作中没有填满一个MTU的数据空间,则系统不会立即发送数据;改为停留等待一些时间(通常是200ms),以期望程序写入更多的数据结合成一个单独的数据包。可以通过设置TCP_NODELAY为ON来关闭这种延迟。
  7. 是否有一个在UDP之上封装好,提供数据包之间可靠的传递的程序?
    答:Enet 非常不错, 尽管它的文档很少。
  8. 还有其它哪些经常被提起的网络应用程序库?
    答:低级"sockets portability"程序库包括
    Enet
    HawkNL
    PLib/Medusa
    SDL_net

    高级"we give you structure"程序库包括
    ClanLib
    DemonWare
    Net-Z/Eterna
    Nevrax/NEL
    OpenPlay
    OpenSkies
    OpenTNL
    RakNet
    ReplicaNet
    ZoidCom

    最常被提起的库是Enet, RakNet and OpenTNL.
  9. 为什么当我在一个防火墙或者DSL路由后不能host a game?
    答:可能受到在firewall/router的网络地址传输(NAT)的限制。Try this web page or wait for my upcoming article in Game Programming Gems 5 (out spring 2005).
  10. 在游戏的代码中,应该为每一个连接生成一个线程吗?
    答:简单的回答是"很可能不需要"。详细的说,如果使用UDP,你只需要一个socket,用它你可以读一个数据包,处理该数据包,重复。如果使用TCP,你需要给每个玩家一个socket,但所有的玩家很可能共享、作用于在程序里的同一个世界状态,这需要通过locking来保护;因此,额外的线程仅用来结束locks的阻塞,所以建议使用select()来读出数据,然后处理数据。Some users find "co-operative threads" or fibers to be a nice middle ground, because it gives some of the advantages of real threads (a stack per context) without the locking overhead (because yield is explicit).
  11. 应该使用阻塞式sockets,还是非阻塞式sockets,或者异步sockets?
    答:在UNIX(至少在Linux)上, 异步sockets通常是不被支持的,尽管如此,你可以安排在"any fd"就绪的时候(FIOASYNC)取得一个信号量。如果是在windows上开发,就要准备处理重要的管理开销,这样可以取得良好的性能表现。无论如何,使用非阻塞式sockets结合select()通常不仅是最容易正确的实现的,而且也会运行得很好。-- I recommend not doing anything fancier unless a profiler tells you to (i e, until oprofile tells you you're spending 10% of your CPU time inside select()).

    用阻塞式sockets写一个实时游戏是比较困难的,因为它是阻塞的!先假设你能够在这种IO模型的约束下很好的工作,如果你正在写一个文件传输程序或者类似这样的东西,尤其如果它是P2P(只有单独的一条连接)类型的,那么采用阻塞式TCP sockets是非常容易实现的。

    如果你只针对于Windows,一个可供选择的高性能解决方案是 Overlapped I/O on sockets.
  12. 应该如何提交网络数据包?多久来发送它们呢?
    答:这依赖于你的目标和你的游戏。
    对于FPS风格的游戏,你可能想了解Quake III, Unreal 和 Tribes 是如何做的. (如果链接不能正常使用,试一下 webarchive.org,不过它很慢)
    对于回合制游戏,最重要的事情是要保证玩家之间的同步;带宽和延迟则相对不那么重要。
    对于实时战略游戏,压缩带宽变得极为重要,因为游戏里经常会有大量的单位来回移动。一篇不错的文章介绍在 GamaSutra.com 上。
    关于数据压缩的方法, Jon Blow在Game Developer上写了一系列的文章介绍: here.
    在论坛上的 this thread 有关于RPG和RTS的命令/数据包的编码介绍,也是很有帮助的。
    对于MMORPGs, 典型的解决方案是发送一些角色的位置和状态信息(可能使用delta压缩,定时发送),通常角色会对他/她附近的玩家更加频繁的发送信息。
  13. 为什么FD_SET的设置在Windows上不能大于64,在UNIX上不能大于1024?
    答(以下内容非原文翻译):参阅MSDN及网上资料后,发现在Windows上fd_set是一个结构体,而题目中的FD_SET可能是FD_SETSIZE。详情请查看MSDN。
  14. 我尝试在TCP上发送两个数据包,但似乎它们到达的时候合并到了一起,或者说一个数据包在接受端消失了。该如何继续?
    答:TCP是数据流协议。你从一端插入的任何数据,都将有序的到达另外一端。但是,你在一个终端通过调用send()来建立的所谓"边界"并没有保存在数据流里。TCP实现网络传输的时候,可能将多次send()调用看起来产生分离的数据合并在一起,通过一次recv()调用来获得;或者出现因为一次大的数据send(),而用多次recv()调用来取得数据。
    要在TCP中定义消息的含义,每次发送消息之前,要先将消息的长度置于其中,可以作为short或long数据类型(使用htons()或htonl()使之跨平台),然后再写消息内容数据。在接收端,先读取消息的长度,再据此读取指定长度的消息数据字节,以此类推,重建另外一端所发送的每条消息。
  15. 如何使用 send() 或 sendto() 来发送一个结构体数据?
    答:在一些旧的头文件中socket发送函数中可能有char *数据原型,但实际上它们就是采用一个字节指针和一个尺寸大小。发送结构体的方法和将它写到一个文件相同。要发送和接收一个玩家的位置更新,一些简单的代码可能看起来像这样:
    // ------------------------------------------------------------------------------------------------
      struct PlayerPosPacket {
        unsigned short code;
        unsigned short player;
        float pos_x, pos_y, pos_z;
        float vel_x, vel_y, vel_z;
        float heading;
      };

      PlayerPosPacket packet;
      packet.code = PLAYER_POS;
      packet.player = thePlayerId;
      ...
      int r = sendto( sock, (char const *)&packet, sizeof(packet), 0, (sockaddr *)&dst_addr, sizeof(dst_addr) );
      if( r < 0 ) { error(sock); }

      union {
        PlayerPosPacket playerPos;
        SomeOtherKindOfPacket someOther;
      } recvPacket;

      int r = recvfrom( sock, (char *)&recvPacket, sizeof( recvPacket ), 0, (sockaddr *)&src_addr, sizeof(src_addr) );
      if( r < 0 ) { error(sock); }
      /* assume all packet types start with unsigned short code; */
      switch( recvPacket.playerPos.code ) {
        case PLAYER_POS:
          if( r != sizeof(recvPacket.playerPos) ) { formatError(); }
          do_playerPos( recvPacket.playerPos, src_addr );
          break;
        case SOME_OTHER:
          if( r != sizeof(recvPacket.someOther) ) { formatError(); }
          do_someOther( recvPacket.someOther, src_addr );
          break;
        ...
      }
    // ------------------------------------------------------------------------------------------------
    注意,这里的代码没有应用 htonl()/htons() 来转换代码的字节顺序。

    如果你想发送更复杂的对象(例如类和虚拟函数表vtables,使用了指针的数据结构等),则你在发送前需要将对象序列化serialize为字节序列数组,然后在收到时将序列化的内容还原。序列化一般被包含在程序设计语言的高级教程中。
  16. 当服务器发生延迟游戏的角色该如何插值?如何一直保持和服务器同步呢?
    答:这儿有很多探讨 the Zen of Networked Character Physics thread.

 


 

 

注:这是我初次尝试翻译技术文档,疏漏之处请指教。文中有些链接无效,可能是时间太久的缘故。
原文地址http://www.gamedev.net/community/forums/showfaq.asp?forum_id=15

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值