Protobuf.ProtoException: 未找到无参数构造函数...从客户端接收特定消息时
Protobuf.ProtoException: No parameterless constructor found... when receiving specific message from client
我正在尝试使用 C# 编写异步服务器程序。我正在使用 proto-buf.net 在客户端和服务器之间发送对象。在大多数情况下,它工作正常,直到我开始使用连接到我的服务器的另一个用户测试该程序。
尝试以下操作时会出现此特定问题:
按下按钮时,将调用向所有连接到服务器的客户端发送 BattleMessage
的代码。
// in server
var message = new BattleMessage
{
Players = players, // List<string>
Enemies = enemies, // List<Enemy objects>
Current = currentPlayer, // string, the current player's name
Boss = isBoss, // boolean
Turns = turns // string
};
收到此消息后,客户端将更改 GUI 以显示 "battle screen",其中向用户显示 BattleMessage 发送的所有信息。这样做时,它将 运行 检查显示信息的客户端名称是否匹配 "Current."
如果是,它将发回此消息:
// still in client
if(current.Name.Equals(Current))
{
var message = new CurrentPlayerBattleMessage
{
Name = current.Name,
Defense = current.GetDefense(),
HP = current.Hp,
MP = current.Mp,
skills = D,
IsPhysical = current.IsPhysical,
};
message.Weaknesses = weaknesses;
Client.SendMessage(message); // send the message back to the server
}
服务器随后将接收消息,对其进行处理并采取相应行动:
// back in the server
// in Receive(IAsyncResult r)
Socket client = r.AsyncState as Socket;
client.EndReceive(r);
// NetMessage is abstract, CurrentPlayerBattleMessage extends it
NetMessage message = PacketManager.ProcessMessage(buffer);
buffer = new byte[BUFFERSIZE]; // buffer: private byte[] buffer, BUFFERLENGTH = 2048
client.BeginReceive(buffer, 0, BUFFERSIZE, SocketFlags.None, new AsyncCallback(Receive), client);
// process after being identified
if(message.GetType().Equals(typeof(CurrentPlayerBattleMessage)))
{
var current = message as CurrentPlayerBattleMessage;
BattlePlayer bp = new BattlePlayer
{
Name = current.Name,
Defense = current.Defense,
HP = current.HP,
MP = current.MP,
Skills = current.Skills,
IsPhysical = current.IsPhysical,
Weaknesses = current.Weaknesses
};
//update server GUI
server.AddCurrentBattlePlayer(bp);
}
当只有一个人连接到服务器时,当前玩家的技能和数据将被成功加载。当两个人连接到服务器时,会抛出问题中列出的异常。
我查看buffer是否为空的时候,听说会出现这个错误,结果竟然是这样!我不完全确定为什么,或者如何去阻止它。作为参考,在向其发送登录消息后,缓冲区将被重置。这会阻止它接收消息吗?
此外,对于上下文,这里是用于处理消息的方法:
// both methods are used by the Client as well
public static byte[] CreateMessage(NetMessage message)
{
using (var stream = new MemoryStream())
{
Serializer.SerializeWithLengthPrefix(stream, message, PrefixStyle.Base128);
return stream.ToArray();
}
}
public static NetMessage ProcessMessage(byte[] buffer)
{
using (var stream = new MemoryStream(buffer))
{
return Serializer.DeserializeWithLengthPrefix<NetMessage>(stream, PrefixStyle.Base128);
}
}
编辑:我被要求提供一些 class 信息,所以给你:
// the message causing me the issue (SERVER)
[ProtoContract]
public class CurrentPlayerBattleMessage : NetMessage
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public int Defense { get; set; }
[ProtoMember(3)]
public int HP { get; set; }
[ProtoMember(4)]
public int MP { get; set; }
[ProtoMember(5, AsReference = true)]
public Dictionary<string, int> Skills { get; set; }
[ProtoMember(6)]
public bool IsPhysical { get; set; }
[ProtoMember(7, AsReference = true)]
public List<int> Weaknesses { get; set; }
public CurrentPlayerBattleMessage() { }
}
// (CLIENT)
[ProtoContract]
public class CurrentPlayerBattleMessage : NetMessage
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public int Defense { get; set; }
[ProtoMember(3)]
public int HP { get; set; }
[ProtoMember(4)]
public int MP { get; set; }
[ProtoMember(5, AsReference = true)]
public Dictionary<string, int> skills { get; set; }
[ProtoMember(6)]
public bool IsPhysical { get; set; }
[ProtoMember(7, AsReference = true)]
public List<int> Weaknesses { get; set; }
public CurrentPlayerBattleMessage() { }
}
// the parent class
[ProtoContract]
[ProtoInclude(500, typeof(LoginMessage))]
[ProtoInclude(501, typeof(InventoryMessage))]
[ProtoInclude(502, typeof(BattleMessage))]
[ProtoInclude(503, typeof(CurrentPlayerBattleMessage))]
[ProtoInclude(504, typeof(UpdateBattleMessage))]
[ProtoInclude(505, typeof(RewardMessage))]
[ProtoInclude(506, typeof(RetreatMessage))]
public abstract class NetMessage
{
public NetMessage() { }
}
到目前为止 solutions/ideas 已尝试:
让我自己和帮助我测试的人单独连接到服务器,这样我会尝试查看如果我是唯一连接的人是否可以显示我的统计数据,那么我会重新启动服务器,最后另一个人也会这样做。导致客户端收到消息,技能也收到了。
正在检查客户端是否在发送消息。是的,服务器只是没有正确处理它。
- 检查缓冲区是否为空,原来是!
我通过跟踪发送的最后一条消息(一直触发错误的消息)并重新发送来解决了这个问题。每当抛出 Protobuf 异常时,程序都会假定它是由于空缓冲区造成的。
这不是最好的解决方案,事实上它非常"hacky,"但它解决了我遇到的眼前问题。
我正在尝试使用 C# 编写异步服务器程序。我正在使用 proto-buf.net 在客户端和服务器之间发送对象。在大多数情况下,它工作正常,直到我开始使用连接到我的服务器的另一个用户测试该程序。
尝试以下操作时会出现此特定问题:
按下按钮时,将调用向所有连接到服务器的客户端发送 BattleMessage
的代码。
// in server
var message = new BattleMessage
{
Players = players, // List<string>
Enemies = enemies, // List<Enemy objects>
Current = currentPlayer, // string, the current player's name
Boss = isBoss, // boolean
Turns = turns // string
};
收到此消息后,客户端将更改 GUI 以显示 "battle screen",其中向用户显示 BattleMessage 发送的所有信息。这样做时,它将 运行 检查显示信息的客户端名称是否匹配 "Current."
如果是,它将发回此消息:
// still in client
if(current.Name.Equals(Current))
{
var message = new CurrentPlayerBattleMessage
{
Name = current.Name,
Defense = current.GetDefense(),
HP = current.Hp,
MP = current.Mp,
skills = D,
IsPhysical = current.IsPhysical,
};
message.Weaknesses = weaknesses;
Client.SendMessage(message); // send the message back to the server
}
服务器随后将接收消息,对其进行处理并采取相应行动:
// back in the server
// in Receive(IAsyncResult r)
Socket client = r.AsyncState as Socket;
client.EndReceive(r);
// NetMessage is abstract, CurrentPlayerBattleMessage extends it
NetMessage message = PacketManager.ProcessMessage(buffer);
buffer = new byte[BUFFERSIZE]; // buffer: private byte[] buffer, BUFFERLENGTH = 2048
client.BeginReceive(buffer, 0, BUFFERSIZE, SocketFlags.None, new AsyncCallback(Receive), client);
// process after being identified
if(message.GetType().Equals(typeof(CurrentPlayerBattleMessage)))
{
var current = message as CurrentPlayerBattleMessage;
BattlePlayer bp = new BattlePlayer
{
Name = current.Name,
Defense = current.Defense,
HP = current.HP,
MP = current.MP,
Skills = current.Skills,
IsPhysical = current.IsPhysical,
Weaknesses = current.Weaknesses
};
//update server GUI
server.AddCurrentBattlePlayer(bp);
}
当只有一个人连接到服务器时,当前玩家的技能和数据将被成功加载。当两个人连接到服务器时,会抛出问题中列出的异常。
我查看buffer是否为空的时候,听说会出现这个错误,结果竟然是这样!我不完全确定为什么,或者如何去阻止它。作为参考,在向其发送登录消息后,缓冲区将被重置。这会阻止它接收消息吗?
此外,对于上下文,这里是用于处理消息的方法:
// both methods are used by the Client as well
public static byte[] CreateMessage(NetMessage message)
{
using (var stream = new MemoryStream())
{
Serializer.SerializeWithLengthPrefix(stream, message, PrefixStyle.Base128);
return stream.ToArray();
}
}
public static NetMessage ProcessMessage(byte[] buffer)
{
using (var stream = new MemoryStream(buffer))
{
return Serializer.DeserializeWithLengthPrefix<NetMessage>(stream, PrefixStyle.Base128);
}
}
编辑:我被要求提供一些 class 信息,所以给你:
// the message causing me the issue (SERVER)
[ProtoContract]
public class CurrentPlayerBattleMessage : NetMessage
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public int Defense { get; set; }
[ProtoMember(3)]
public int HP { get; set; }
[ProtoMember(4)]
public int MP { get; set; }
[ProtoMember(5, AsReference = true)]
public Dictionary<string, int> Skills { get; set; }
[ProtoMember(6)]
public bool IsPhysical { get; set; }
[ProtoMember(7, AsReference = true)]
public List<int> Weaknesses { get; set; }
public CurrentPlayerBattleMessage() { }
}
// (CLIENT)
[ProtoContract]
public class CurrentPlayerBattleMessage : NetMessage
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public int Defense { get; set; }
[ProtoMember(3)]
public int HP { get; set; }
[ProtoMember(4)]
public int MP { get; set; }
[ProtoMember(5, AsReference = true)]
public Dictionary<string, int> skills { get; set; }
[ProtoMember(6)]
public bool IsPhysical { get; set; }
[ProtoMember(7, AsReference = true)]
public List<int> Weaknesses { get; set; }
public CurrentPlayerBattleMessage() { }
}
// the parent class
[ProtoContract]
[ProtoInclude(500, typeof(LoginMessage))]
[ProtoInclude(501, typeof(InventoryMessage))]
[ProtoInclude(502, typeof(BattleMessage))]
[ProtoInclude(503, typeof(CurrentPlayerBattleMessage))]
[ProtoInclude(504, typeof(UpdateBattleMessage))]
[ProtoInclude(505, typeof(RewardMessage))]
[ProtoInclude(506, typeof(RetreatMessage))]
public abstract class NetMessage
{
public NetMessage() { }
}
到目前为止 solutions/ideas 已尝试:
让我自己和帮助我测试的人单独连接到服务器,这样我会尝试查看如果我是唯一连接的人是否可以显示我的统计数据,那么我会重新启动服务器,最后另一个人也会这样做。导致客户端收到消息,技能也收到了。
正在检查客户端是否在发送消息。是的,服务器只是没有正确处理它。
- 检查缓冲区是否为空,原来是!
我通过跟踪发送的最后一条消息(一直触发错误的消息)并重新发送来解决了这个问题。每当抛出 Protobuf 异常时,程序都会假定它是由于空缓冲区造成的。
这不是最好的解决方案,事实上它非常"hacky,"但它解决了我遇到的眼前问题。