|
|
Networking网络
Overview
The V12 was designed from the foundation to offer robust client/server network simulation support. Performance over the internet drove the design for the networking model. The V12 attempts to deal with three fundamental problems of network simulation programming - limited
bandwidth, packet loss and latency. For a more detailed, if somewhat outdated, description of the V12 network architecture, see The Tribes II Engine Networking Model paper by Tim Gift and Mark Frohnmayer and the accompanyingPowerPoint slides.
V12设计的目的是提供强大的客户端/服务器网络模拟支持。为internet传输数据性能考虑的网络模块。V12目的是处理三种基本的网络问题:有限带宽、包丢失和网络延迟。具体信息请参考V12网络结构。
An instance of the V12 example can be set up as a dedicated server, a client, or a client and server both. If the game is a client and server both, it still behaves as a client connected to a server - instead of using the network, however, the NetConnection object has a short-circuit link to another NetConnection object in the same application instance.
一个V12例子的实例可以设置成为贡献服务器、客户端、或者客户端和服务器同时运行。如果一个游戏即是客户端又是服务器,它仍然是一个客户端连接到一个服务器,不同的是不使用网络。只是NetConnection对象有一个直接连接到同一个游戏程序的另一个NetConnection对象而已。
Bandwidth is a problem because in the large, open environments the V12 allows, and with the large number of clients the V12 supports (up to 128 per server), potentially many different objects can be moving and updating at once. The V12 uses three main strategies to maximize available bandwidth. First, prioritize data - send updates to what is most "important" to a client at a greater frequency than update data that is less important. Second, send only data that is necessary - using the BitStream class, only the absolute minimum number of bits needed for a given piece of data can be sent. Also, when object state changes, the V12 only sends the part of the object state that changed. Last, the V12 caches common strings (NetStringTable) and data (SimDataBlock) so that they need only be transmitted once.
带宽是一个问题,因为在V12是一个巨大的,开放式的允许128个客户端同时连接的环境。潜在的许多不同的对象能够在同一时刻移动和更新。V12用了三种主要的办法来最大化利用带宽。首先,按照优先级别定义数据,发送重要数据的频率大大高于发送次要数据的频率。第二,发送必要的数据-使用bitstream类,只有在数据中的绝对最少的bit位能够被发送到客户端,而且,当对象状态改变的时候,V12仅仅发送改变的状态。最后,V12缓冲一般字符串(NetStringTable)和数据(SimDataBlock),所以它们只能够被一次传送。
Packet loss is a problem because the information in lost data packets must somehow be retransmitted, yet in many cases the data in the dropped packet, if resent directly, will be stale by the time it gets to the client - for example, suppose that packet 1 contains a position update for a player and packet 2 contains a more recent position update for that same player. If packet 1 is dropped but packet 2 makes it across the engine shouldn't resend the data that was in packet 1 - it is older than the version that was received by the client. In order to minimize data that gets resent unnecessarily, the engine classifies data into four groups:
包丢失也是一个问题,因为丢失包后要求重新传输数据,然而在许多情况下简单重传不能解决问题,比如
按照顺序更新包就不需要重传原始数据,而要求重新传送最新的数据。为了最小化数据传输量,引擎把数据分成4类
1. Unguaranteed Data (NetEvent) - if this data is lost, don't re-transmit it. An example of this type of data could be real-time voice traffic - by the time it is resent subsequent voice segments will already have played.
1.不保证的数据包(NetEvent)如果数据丢失发生,不重新发送。举例,实时语音数据。
2. Guaranteed Data (NetEvent) - if this data is lost, resend it. Chat messages, messages for players joining and leaving the game and mission end messages are all examples of guaranteed data.
保证的数据(NetEvent)-如果数据丢失。重新发送。聊天信息,玩家加入,离开,任务消息等所有保证的数据。
3. Most-Recent State Data (NetObject) - Only the most current version of the data is important - if an update is lost, send the current state, unless it has been sent already.
最新状态信息(NetObject)-只有最新版本的数据会发送,如果丢失发生,发送当前状态,除非这个状态已经发送了。
4. Guaranteed Quickest Data (Move) - critical data that must get through as soon as possible.
保证的即时数据(Move)-是指关键的数据必须最快速度传送到客户端
Latency is a problem in the simulation because the network delay in information transfer (which, for modems, can be up to a quarter of a second or more) makes the client's view of the world perpetually out-of-sync with the server. Twitch FPS games, for which the V12 was initially designed, require instant control response in order to feel anything but sluggish. Also, fast moving objects can be difficult for highly latent players to hit. In order to solve these problems the V12 employs several strategies:
延迟也是一个问题,因为网络延迟(猫传输数据延迟1/4秒甚至更多)让客户端
1. Interpolation is used to smoothly move an object from where the client thinks it is to where the server says it is.客户端会在服务器指定移动路径上插值达到移动效果
2. Extrapolation is used to guess where the object is going based on its state and rules of movement.用推导来根据运动法则预测对象将要运动到什么位置。
3. Prediction is used to form an educated guess about where an object is going based on rules of movement and client input.根据客户端输入来预测对象将要移动的目标位置
The network architecture is layered: at the bottom is the OS/platform layer, above that the notify protocol layer, followed by the NetConnection object and event management layer. The following sections explain how each layer addresses some or all of the fundamental network simulation problems.
网络构架是分层的:最底层的是系统/平台层,上面是协议通告处理层,然后是NetConnection对象和事件管理层,接下来部分解释每个层完成的网络功能。
下面是网络模块层次图
Platform Networking Layer (TCP/UDP)
The platform library provides the interface between the game engine and the OS dependent network functionality. The platform library's Net interface contains functions for opening reliable and unreliable communication sockets, converting between string and numeric network addresses and sending and receiving data.
平台库在操作系统基础和游戏引擎之间提供了接口,网络平台库接口包含了打开可信赖链接和不可信赖链接的两种socket,转换字符串到数字的网络地址和发送接受数据。
The Net: penPort function opens an unreliable socket, of which only one is allowed per application instance. Net::sendto sends an unreliable datagram to the specified NetAddress. Net::openListenPort opens a reliable socket for incoming TCP connections. Net::openConnectTo begins the process of asynchronously connecting to a remote TCP socket. Net::sendtoSocket sends data over an established TCP connection. Net::process processes the platform network layer, possibly generating network related events that are then posted into the simulation via GameInterface::processEvent.
网络net::openPort函数打开不可信赖链接(服务端),一个程序只能打开一个实例。 Net::sendto() 发送不可信赖数据包到指定的NetAdress地址,NetopenListenPort 打开一个可信赖的TCP套接字开始监听,Net::openConnectto开始链接到远程的TCP套接字,Net::sendtoSocket通过建立好的链接发送数据。Net::process处理网络层数据,特定的网络消息通过GameInerface::processEvent来处理。
Connection Negotiation
The negotiation of a game network connection is not actually a part of the network class tree in the V12 - instead a set of functions, declared in engine/game/netDispatch.cc perform this service. The function V12Game::processPacketReceiveEvent is the main dispatch function for incoming network packets.
The first step of the connection process is the console function connect(), which initiates a connection attempt by sending a connect challenge request packet to the server from sendConnectChallengeRequest.
连接网络的第一步是调用函数connect(),它通过调用函数sendConnectChallengeRequest试图发送一个challege请求到服务器。
The server, in function handleConnectChallengeRequest, may issue the client a connect challenge response, which the client will process in handleConnectChallengeResponse. The client will in turn issue a connect request (sendConnectRequest) with the challenge information it received from the server. The server processes this message in handleConnectRequest. If the server decides to accept the request, it issues a sendConnectAccept back to the client and constructs a NetConnection object on the server to handle that client. The client, in handleConnectAccept creates a complementary NetConnection object to manage the client side of the connection. The dispatchCheckTimeouts function periodically checks if a connection request or challenge has been waiting too long and reissues the request if it has.
服务器端,在函数handleConnectChallengeRequest里面处理客户端的challenge请求。如果服务器通过了challenge请求,则客户端就会发送连接请求(通过sendConnectRequest函数)。这时服务器调用hanleConnectRequest函数来处理。如果服务器决定接受客户端的链接请求,就会通过调用sendConnectAccpet发送回客户端而且构造一个NetConnection对象来处理与客户端的数据通讯。在客户端的hanleConnectAccept函数创建一个同样的NetConnection对象来管理客户端与服务器的网络通讯。在这个过程中,dispatchCheckTimeouts函数周期性的检测链接请求和charlenge请求是否要等待很长时间回应。
ConnectionProtocol连接协议
Once a connection has been established, the function of the ConnectionProtocol class is to provide a common low-level mechanism for supporting the delivery of the four fundamental types of network data in the V12. The ConnectionProtocol abstract base class implements a sliding window connected message stream over an unreliable transport (UDP). Rather than supporting guaranteed messages directly, the ConnectionProtocol class implements a notify protocol. Each packet sent is prepended with a message header containing tracking information, including what
packets the other end of the connection has received or were dropped in transit. When a ConnectionProtocol instance determines that a packet it sent has been either received or dropped, it calls ConnectionProtocol::handleNotify. Notifies are always delivered in the order packets were sent - so for every packet sent through a ConnectionProtocol object, eventually a notification of successful (ack) or unsuccessful (nack) delivery will be executed.
当一个连接建立之后,ConnectionProtocol类的函数提供了一个公共的底层机制来支持4种类型的网络数据传输。ConnectionProtocol抽象基类使UDP实现了滑动窗口数据流的传输,与直接支持可信赖数据传输相比,ConnectionProtocole类实现的是通告协议。每个发送的数据都包装了一个信息头来跟踪传输情况,包含接受端是成功收到数据包还是丢失了数据包,当一个ConnectionProtocol实例得到一个发送过的包收到了或者是丢失了,它就会调用ConnectionProtocol::hanleNotity.通告总是按照发送的包的顺序被调用,所以对于每个通过ConnectionProtocol发送的数据报来说,顺序的ack和nack通告都会被收到。
Because the base network protocol exports the inherently unreliable nature of the network to the simulation, at a higher level the V12 can directly support different types of data guarantee: for unguaranteed data, if it is nacked, there is no need to resend it. For guaranteed data, if it is nacked, the engine queues it up for resend (NetConnection::eventPacketDropped). If the data is most recent state data and the packet is nacked and that object's state hasn't been subsequently changed and resent, queue the data up for resend (NetConnection::ghostPacketDropped). If the data is set for quickest possible delivery, continue sending the data with every packet until a packet containing the data is acked (GameConnection::readPacket).
NetConnection
The NetConnection class is derivative from both SimGroup and ConnectionProtocol, and is responsible for managing the data streaming between client and server. The NetEvent class encapsulates the guaranteed and unguaranteed message delivery types and the ghost management portion of the NetConnection class handles state updates of world objects from server to client. The V12 example game-specific sublclass of NetConnection is GameConnection and handles transmission of game specific data such as player moves.
The NetConnection class sends packets of a fixed size in a regular stream between the client and server. When a message is posted for transmission, it is aggregated with other messages and sent based on the packet rate and packet size settings for that connection.
The BitStream
The BitStream class is a utility class used to pack data for transmission. BitStream has methods for reading and writing variable-sized integers (BitStream::readInt, BitStream::writeInt), floats, vectors, Huffman-coded strings and bits.
BitStream类是一个工具类用来包装传输数据,它有读写各种数据类型的方法。
When a NetConnection instance determines it is ready to send a packet across the network (NetConnection::checkPacketSend), it allocates a BitStream and calls NetConnection::writePacket with the stream. When a packet is received it is processed through the corresponding NetConnection::readPacket function.
当一个NetConnection实例决定通过网络发送一个数据包时,函数NetConnection::checkPacketSend分配一个BitStream然后调用NetConnection::writePacket函数,当一个包收到后会通过调用NetConnection::readPacket函数来读取数据。
Network Events网络消息
The NetEvent class provides a foundation for guaranteed, guaranteed ordered and unguaranteed message transmission. NetEvent uses the same class instance creation mechanism as the console, but rather than instantiating by name, NetEvents use a class id, assigned when the console initializes.
NetEvent类提供了保证传输的基础,它保证了顺序和不保证的消息传输,NetEvent使用了和console同样的创建机理,但是它不是按照名字进行初始化,而是按照类id来进行,并当控制台初始化的时候得到赋值。
A simple network event:一个简单的网络事件的例子
class SimpleMessageEvent : public NetEvent
{
char *msg;
public:
SimpleMessageEvent(const char *message = NULL)
{
if(message)
msg = dStrdup(message);
else
msg = NULL;
}
~SimpleMessageEvent()
{ dFree(msg); }
virtual void pack(NetConnection* ps, BitStream *bstream)
{ bstream->writeString(msg); }
virtual void write(NetConnection*, BitStream *bstream)
{ bstream->writeString(msg); }
virtual void unpack(NetConnection* ps, BitStream *bstream)
{ char buf[256]; bstream->readString(buf); msg = dStrdup(buf); }
virtual void process(NetConnection *connection)
{ Con::printf("RMSG %d %s", connection->getId(), msg); }
DECLARE_CONOBJECT(SimpleMessageEvent);
};
IMPLEMENT_CO_NETEVENT_V1(SimpleMessageEvent);
ConsoleMethod(NetConnection, sendMsg, void, 3, 3, "con.sendMsg(messageString)")
{
(NetConnection *) object)->postNetEvent(new SimpleMessageEvent(argv[2]));
}
Some items to note - events have virtual methods to pack and unpack themselves into the network packet BitStream. If the read and write methods don't match in terms of what they read and write into the stream, serious network errors can occur. The client and server should gracefully disconnect in these cases, but the errors themselves can be very difficult to track down. If the DEBUG_NET macro is defined, a special key will be written into the packet stream after each event and object update, and the system will assert immediately when it detects that this problem has occurred.
需要注意的点:网络事件的打包和解包的虚函数把自己的数据打进网络数据流中,如果实际读取的数据没有按照实际写入的数据,那么严重的网络错误将会出现,这个时候客户端和服务器都能很好的把对方断开。但是错误本身却难以被跟踪,如果DEBUG_NET宏被定义,那么当包更新的时候,一些特定的校验位会被写入包数据中,这样系统就会发现问题出现并立即产生断言。
NetEvent instances may be unpacked out of order (if, for example there was a dropped packet), so, for guaranteed ordered events, the process function will not be called until the previous events have been processed. The NetEvent::write function is specifically for demo recording -all as yet unprocessed events on the client side of a connection are written using this method.
网络事件(NetEvent)实例可能不按照次序解包,(例如,发现了一个丢失的包),于是,对于保证次序的网络事件,处理包的函数会直到前面的事件处理之后才会被调用。NetEvent::write函数特别适合演示的录制-所有在一个连接下的客户端没有处理的网络事件使用这个方法来写入数据。
All network events must use the DECLARE_CONOBJECT macro in the class definition, and must use the IMPLEMENT_CO_NETEVENT_V1 macro outside the class definition. There are two special forms of IMPLEMENT_CO_NETEVENT: IMPLEMENT_CO_CLIENTEVENT_V1, which specifies an event that can only travel from the server to the client, and IMPLEMENT_CO_SERVEREVENT_V1 which specifies an event that can only travel to the server.
所有的网络事件必须在类声明的时侯使用DECLARE_CONOBJECT宏,而且在类定义的时候使用IMPLEMENT_CON_NETEVENT和IMPLEMENT_CO_CLIENTEVENT_V1,来指定这个事件只能被从服务器传送到客户端,另外一个宏IMPLEMENT_CO_SERVEREVENT_V1来指定事件只能传送到服务器。
|
|