我正在构建一个服务器/客户端应用程序,并且正在研究用于分离数据包的选项。我已经读过,最合适的方法是创建一个包含有效负载大小的信息的头,然后读取到它结束。
如何以编程方式工作?
也可以使用"\n“换行符分隔它们。举个恰当的例子就好了。
我以这种方式异步接收数据:
private void AsyncReceive(IAsyncResult result)
{
int bytesTransfered;
try
{
bytesTransfered = _handle.EndReceive(result);
if(bytesTransfered <= 0)
{
throw new Exception("No bytes transfered");
}
}
catch(NullReferenceException)
{
return;
}
catch(ObjectDisposedException)
{
return;
}
catch(Exception)
{
return;
}
byte[] received = new byte[bytesTransfered];
try
{
Array.Copy(_readBuffer, received, received.Length);
}
catch(Exception)
{
Disconnect();
return;
}
// How should I process the received data now?
try
{
_handle.BeginReceive(_readBuffer, 0, _readBuffer.Length, SocketFlags.None, AsyncReceive, null);
}
catch(ObjectDisposedException)
{
return;
}
catch(Exception)
{
}
}发布于 2016-05-10 20:28:02
首先,您需要区分不同类型的消息。您可以为此使用单个字节,这将允许最多255个不同的消息类型。为此创建一个枚举,并创建一个属性来标记您的消息(请参见下面):
enum MessageType : byte {
FirstMessage,
SecondMessage
}
class MessageAttribute : Attribute {
public MessageAttribute(MessageType type) {
Type = type;
}
public MessageType Type { get; private set; }
}其次,您需要对消息进行紧凑的序列化。一个很好的选择是protobuf --它非常紧凑(不序列化属性名称,只序列化值等等),但仍然很容易使用。
[Message(MessageType.FirstMessage)]
[ProtoContract]
class MyFirstMessage {
[ProtoMember(1)]
public string Value { get; set; }
[ProtoMember(2)]
public int AnotherValue { get; set; }
}
[Message(MessageType.SecondMessage)]
[ProtoContract]
class MySecondMessage {
[ProtoMember(1)]
public decimal Stuff { get; set; }
}第三,你需要知道信息的长度,就像来电者说的那样。为此使用2或4个字节( Int16和Int32类型的大小)。
所以我们的格式是:1字节消息类型。2-5字节-消息大小,5-5+大小字节-原型序列化消息.然后按以下三个步骤读取您的流:
class MessageReader {
static readonly Dictionary<byte, Type> _typesMap = new Dictionary<byte, Type>();
static MessageReader() {
// initialize your map
// this is executed only once per lifetime of your app
foreach (var type in Assembly.GetExecutingAssembly().GetTypes().Where(c => c.GetCustomAttribute<MessageAttribute>() != null)) {
var message = type.GetCustomAttribute<MessageAttribute>();
_typesMap.Add((byte)message.Type, type);
}
}
public async Task<object> Read(Stream stream) {
// this is your network or any other stream you have
// read first byte - that is message type
var firstBuf = new byte[1];
if (await stream.ReadAsync(firstBuf, 0, 1) != 1) {
// failed to read - end of stream
return null;
}
var type = firstBuf[0];
if (!_typesMap.ContainsKey(type)) {
// unknown message, handle somehow
return null;
}
// read next 4 bytes - length of a message
var lengthBuf = new byte[4];
if (await stream.ReadAsync(lengthBuf, 0, 4) != 4) {
// read less than expected - EOF
return null;
}
var length = BitConverter.ToInt32(lengthBuf, 0);
// check if length is not too big here! or use 2 bytes for length if your messages allow that
if (length > 1*1024*1024) {
// for example - adjust to your needs
return null;
}
var messageBuf = new byte[length];
if (await stream.ReadAsync(messageBuf, 0, length) != length) {
// didn't read full message - EOF
return null;
}
try {
return ProtoBuf.Serializer.NonGeneric.Deserialize(_typesMap[type], new MemoryStream(messageBuf));
}
catch {
// handle invalid message somehow
return null;
}
}
}在读取流中的一条消息之后--继续以相同的方式读取下一条消息。读取调用将被阻塞,直到新的数据到达。如果有任何违反协议下降连接。
发布于 2016-05-10 19:56:30
您没有考虑使用TCPClient和TCPListener,然后使用NetworkStream吗?套接字级别很低,在大多数情况下可能不需要。
见本文:How reading messages from server?(TCP)
此外,除非记录并重新抛出异常,否则不要捕获无法恢复的异常。当异常被默默吞没时,这将导致很难调试行为。
https://stackoverflow.com/questions/37147730
复制相似问题