首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用套接字流正确地分离数据包?C#

如何使用套接字流正确地分离数据包?C#
EN

Stack Overflow用户
提问于 2016-05-10 19:52:11
回答 2查看 1.7K关注 0票数 4

我正在构建一个服务器/客户端应用程序,并且正在研究用于分离数据包的选项。我已经读过,最合适的方法是创建一个包含有效负载大小的信息的头,然后读取到它结束。

如何以编程方式工作?

也可以使用"\n“换行符分隔它们。举个恰当的例子就好了。

我以这种方式异步接收数据:

代码语言:javascript
复制
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)
        {

        }
    }
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-05-10 20:28:02

首先,您需要区分不同类型的消息。您可以为此使用单个字节,这将允许最多255个不同的消息类型。为此创建一个枚举,并创建一个属性来标记您的消息(请参见下面):

代码语言:javascript
复制
enum MessageType : byte {
    FirstMessage,
    SecondMessage
}

class MessageAttribute : Attribute {
    public MessageAttribute(MessageType type) {
        Type = type;
    }

    public MessageType Type { get; private set; }
}

其次,您需要对消息进行紧凑的序列化。一个很好的选择是protobuf --它非常紧凑(不序列化属性名称,只序列化值等等),但仍然很容易使用。

代码语言:javascript
复制
[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+大小字节-原型序列化消息.然后按以下三个步骤读取您的流:

代码语言:javascript
复制
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;
        }
    }
}

在读取流中的一条消息之后--继续以相同的方式读取下一条消息。读取调用将被阻塞,直到新的数据到达。如果有任何违反协议下降连接。

票数 2
EN

Stack Overflow用户

发布于 2016-05-10 19:56:30

您没有考虑使用TCPClient和TCPListener,然后使用NetworkStream吗?套接字级别很低,在大多数情况下可能不需要。

见本文:How reading messages from server?(TCP)

此外,除非记录并重新抛出异常,否则不要捕获无法恢复的异常。当异常被默默吞没时,这将导致很难调试行为。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37147730

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档