我正在尝试设计服务器/客户端体系结构,我想让你们ping以确定代表和解析不同类型的数据包的最佳方法。每种数据包类型都需要进行不同的解析。以下是我会看到的数据包类型。
[*packet_type*][length][variable length data]
*packet_type* describes the type of packet we're sending (client login, server returning authentication, data, etc)
length describes how much data to read
variable length data contains the info to be sent. it will be specialized based on the packet_type. the data will be variable regardless of
我研究了tcphdr结构,并认为可以使用类似的 header 类型来表示* packet_type *和长度。然后,我将使用String表示数据。
public class Packet {
public enum PKT_TYPE {
CL_REGISTER,
CL_LOGIN,
SRV_AUTH,
SRV_GAME_INFO,
}
PKT_TYPE _packet_type;
int _length;
String _data;
}
现在有了一个共同的基础,我想到可以为每个* packet_type *实现类并发送/接收方法。但是,我觉得这不是很可扩展,很难维护。一个(粗略的,伪的)例子是
public class Packet {
...
public class Pkt_CL_LOGIN extends Packet {
String _loginname;
String _password;
public boolean send() {
//socket.write(CL_LOGIN, length, _loginname+_password);
}
public Pkt_CL_LOGIN parse(String data) {
//removed header already, so first byte will be data
//extract login + password
_loginname = login;
_password = password;
return this;
}
}
public Packet receive() {
//read from socket
//parse header for packet_type
switch (packet_type)
case CL_LOGIN:
return (new Pkt_CL_LOGIN()).parse(data);
}
}
谁能给我一些有关如何以不同方式实现此建议的建议?我不确定是否有一个,但是也许有更多经验的人可以给我一些见解(例如他们在多人游戏中的表现等)
谢谢!
最佳答案
我目前正在使用Protocol Buffers制作多线程C++聊天服务器,以实现实际的协议(protocol)。关键是,我认为您应该使用它们:它们为您需要的每个数据包提供了一个漂亮的接口(interface),它们可以用于多种语言(C++,Java和Python只是开始,我认为它们具有一些Ruby接口(interface),很好),并且它们使您无需花费太多精力即可创建通用协议(protocol),因为它们消除了序列化问题,并且无需为每个数据包编写自己的类。此外,移动设备还有一个特定的“精简版”版本(如果您使用的是Android编码,则可能会派上用场)。
关于数据包,我知道有两种方法来跟踪数据包何时结束:第一种方法具有固定长度的数据包,第二种方法在实际发送数据包之前先发送长度。包类型也一样。如果您没有很多数据包类型,则可以只使用一个unsigned char
(现在是C++,但是我想应该有某种方法可以在Java中执行相同的操作)来表示它,这将为您提供255数据包类型(如果您要求我,则超出所需的数量)。
就我的服务器而言,我实际上是通过发送一个固定长度的 header 来完成的,该 header 包含数据包的长度和类型(均为固定长度uint32),然后对其进行解析,然后再次读取套接字以检索信息,然后相应地对其进行解析,并将其发送到客户端处理程序中的适当处理程序。我认为这种方法非常不错,除了以下事实之外……我没有使用任何额外的内存(数据包类型太大)...
作为 Protocol Buffer 的示例,您的.proto文件可能看起来像这样:
message Header {
required fixed32 length = 1;
required fixed32 type = 2; // Note: don't use an enum here, as the values are serialized to varint, which simply kills your fixedness.
}
message Login {
required string nickname = 1;
required string password = 2;
}
enum ErrorType {
BAD_HEADER = 0;
WRONG_PASSWORD = 1;
}
message Error {
required ErrorType type = 1;
optional string message = 2;
}