NetworkDesign

来自gamedev
跳转到导航 跳转到搜索



请注意:此页面说明了底层网络通信。 如果您想使用Marauroa编写游戏,则无需理会这些实现细节。 无论如何,我们都会记录网络设计,以供Marauroa本身的贡献者使用。 这对于将Marauroa移植到其他编程语言的人们很有帮助。

Messages[编辑]

Marauroa使用消息在客户端和服务器之间进行通信。 从客户端发送到服务器的消息以C2S为前缀,从服务器发送到客户端的消息以S2C为前缀.

每个消息都在包marauroa.common.net.message中的Java类中实现。 您可以在 javadoc 中查找有关每条消息的详细信息. 如果要将Marauroa移植到另一种编程语言,则需要知道如何精确地序列化消息. 最简单的学习方法是查看 source code 中的 readObject() 和 writeObject() 方法.

游戏有不同的客户端状态:已连接,已登录,游戏中,已退出。 不同状态下的有效消息也不同:

已连接状态[编辑]

创建缩略图出错:文件丢失
Messages used to securely login.

由于安全性要求,登录过程有点复杂。 不要害怕,只要遵循以下的步骤即可.

一旦建立了TCP连接,客户端就会使用以下命令向服务器请求RSA公钥 C2SLoginRequestKey. 服务器检查每个消息中隐式包含的协议版本. 如果兼容,它会回复一个 S2CLoginSendKey 包含 RSA 公钥. 它由两个字节数组组成: 第一个包含 'n' 的值, 第二个包含 'e' 的值.

客户端计算 nonce (随机数) 并将其哈希值作为字节数组发送到服务器 C2SLoginPromise . 服务器会记住客户端的随机数,并在 S2CLoginSendNonce 回复服务器的nonce.

差不多可以了: 客户端现在拥有了实际发送消息所需的所有信息。 C2SLoginSendNonceNameAndPassword: Its nonce, the username and the value rsaCrypt(xor(xor(client nonce, server nonce), password)). The first field is a bytes array containing the client nonce, the second one a string containing the username and the third one a byte array containing the encrypted password. On reception, the server checks that the hash he received at first is the hash of the nonce he just received. It then decodes the password field, and having the value of the client nonce and its nonce, it gets the value of the password.

The S2CLoginNACK message is sent from the server to the client to tell the client that its login request was rejected because the username or password is wrong, the account was banned or the server is full. The included result object will tell which of the cases prevented the login.

If the username/password combination, however, is correct then the Server must send a S2CLoginACK message to tell the client that the message has been correctly processed. It contains information about the last login, so that the user is able to recognize unauthorized usage of his account. The client state is changed to "logged in" in this case.

State logged in[编辑]

创建缩略图出错:文件丢失
Selecting a character and transmitting world meta data.

The logging in stuff was complicated. But luckily things are getting much easier now. After the login completed successfully the server sends a S2CServerInfo message. It tells the client about what kind of server is running, and details on how to contact the server administrator (e.g. their email address). The message is composed of a List of strings of the form "attribute=value". In addition this message also contains the list of defined RPClasses.

Directly afterwards a S2CCharacterList message is sent from the server to the client. It offers a choice of character to play with. This feature models the behavior of having several characters associated with a single account. Each character name must be unique at server level, and it is assigned when the character is created.

Now the client picks one of the offered characters. Games that do not support multiple characters can pick the one that matches the account name. The choice is transmitted to the server using a C2SChooseCharacter message. The name of the character must be one of the names listed in the character list.

The server will check the selected character and reply for a S2CChooseCharacterNACK if the choice was invalid. This implies that the client should send another C2SChooseCharacter message.

If the selection, however, was successful, a ChooseCharacterACK message is sent and the client state changed to "in game".

游戏中[编辑]

创建缩略图出错:文件丢失
Messages sent while the game is active.

常规消息[编辑]

The S2CPerception message is a message sent from the server to the client to notify the client about changes to the objects that exist near it. The message is based on the idea explained in the Delta Perception document.

The message is composed of:

  • A type that can be DELTA or TOTAL
  • A string indicating the name of the zone the perception is related to.
  • A time stamp value that will be just one unit bigger than the previous perception
  • A List of RPObject that contains added object
  • A List of RPObject that contains modified added object attributes
  • A List of RPObject that contains modified deleted object attributes
  • A List of RPObject that contains deleted object
  • A RPObject that describes the things the rest of players don't see about OUR own object.

Read the Delta perception algorithm to understand what it is for.


The client sends C2SKeepAlive messages regularly. If there has been no keep alive message for some time, the server will timeout the client.


静态内容传输[编辑]

Perceptions are about dynamic content. But most games have some static data, too. Like maps, tilesets, sounds. The RPManager keeps track of situation in which the client might need some of this static data. A common case is the movement of the client from one zone to another.

If the RPManager detects such a situation, it will offer the new content to the client using a S2CTransferREQ message. The message is composed of an array of TransferContent objects containing the name of each resource, its timestamp (or checksum) and if the resource is cacheable or not.

The clients replies with a C2STransferACK acknowledging the offer. The message is composed of an array of TransferContent objects containing all the name of each resource and a flag indicating ack or not. Note: The C2STransferACK message is always sent, even if all flags indicate that no transfer should take place.

If the clients has acknowledged the need for content to be transfer, the server sends a S2CTransfer. The message contains a again an an array of TransferContent objects. This time, however, the actual data is included as well.

动作[编辑]

Actions are commands sent from the client to the server using a C2SAction message. Example for commands are "move to the right", "look at that object", "attack that rat". It is up to the server to decide whether to execute the action or reject it.

退出登录[编辑]

创建缩略图出错:文件丢失
The client sends a logout request and the server accepts or denies it.

If the player is in some kind of combat it is often desirable to prevent him from saving his live by logging out. Therefore the client sends a logout request to the server and the game server can decide whether to accept or reject it. Of course the user can close the client window or force a disconnect of his Internet connection. But in these cases he will simple stay in a game unattended until the timeout anyway.

The clients indicates that it wants to finish the session by sending a C2SLogout.

The server can reply with a S2CLogoutNACK to reject the logout request. Or it confirms the request with a S2CLogoutACK. In this case the client state is changed to logged out.

Transmitting Messages over TCP[编辑]

The idea behind Arianne's network protocol is to use a single TCP stream between the server and the clients. Different kinds of in-game actions create different types of messages that are then interpreted at the opposite side in to meaningful data. TCP takes care of sorting the packets and retransmitting lost ones.

Each message has a general header:

  • Size of message (4 bytes)
  • Protocol version (1 byte)
  • Type of message (1 byte)
  • Client ID (4 bytes)
  • Timestamp (4 bytes)

This header is followed by message specific data. Have a look at the source code of the methods readObject() and writeObject() of the message in question.

网络管理器[编辑]

The Network Manager is our router that sends and receives messages to and from the network. The manager exposes the interfaces that allow:

  • Reading a message from the network
  • Sending a message to the network
  • Finalizing the manager

The read operation is a blocking type operation so we have two options, either polling (i.e. continually checking if data is there) or blocking (i.e. only processing when data is actually available, otherwise sleeping).

We choose blocking because we don't want to waste CPU time polling the network for messages, we just want to sleep until messages are available. Hence we create a Thread to read from the Network, let's call it NetworkManagerRead.

将消息写入网络可以简单地编码为网络管理器的一种方法,因为写入是一种本质上不会阻塞的操作.

NetworkManager将打开一个套接字,它将从中接收来自网络的所有消息。 它还将从同一套接字将所有出站消息写入网络。 注意:写入和读取都使用相同的套接字.

为了封装所有这些内容,我们将Read和Write方法都创建为Network Manager的内部类。.

NetworkManager
  {
  socket
  messages
  pendingToSendMessages

  NetworkManagerRead isa Thread
    {
    read socket
    build message
    store in messages
    }

  NetworkManagerWrite isa Thread
    {
    get from pendingToSendMessages
    serialize message
    send socket
    }
  }

如您所见,收到消息后,消息将存储在列表中。 因此,对列表的访问必须同步.

现在,让我们回到暴露给其他对象的界面。 write方法是即时的,只需在发送消息时调用它即可,并确保已正确填写SourceAddress和ClientID。 然后,该消息将发送给客户端.

read方法处于阻塞状态,当您调用read方法时,它要么从队列中返回一条消息,要么如果队列为空,则线程阻塞(休眠)直到一个到达.

这是网络管理器的基本思想。 请注意,管理器仅发送一次数据包流,并且不确认是否接收到任何消息。 TCP负责确认. {{#breadcrumbs: Marauroa | Internals | Network Design }}