如何正确使用单独的数据包套接字流? C# [英] How to properly separate packets using Sockets stream? C#

查看:121
本文介绍了如何正确使用单独的数据包套接字流? C#的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我建立一个服务器/客户端应用程序,我期待通过分离选项的数据包。我读过的最合适的人会创建一个包含有效载荷有多大的信息,然后读取直到它结束标题。



这是如何工作的编程?



此外分离那些通过使用\\\
换行。一个适当的例子是好的。



我异步接收数据是这样的:

 私人无效AsyncReceive(IAsyncResult的结果)
{
INT bytesTransfered;


{
bytesTransfered = _handle.EndReceive(结果);

如果(bytesTransfered< = 0)
{
抛出新的异常(无字节转移);
}
}
赶上(的NullReferenceException)
{
的回报;
}
赶上(的ObjectDisposedException)
{
的回报;
}
赶上(例外)
{
的回报;
}


字节[] =收到新的字节[bytesTransfered]


{
Array.Copy(_readBuffer,接收received.Length);
}
赶上(例外)
{
断开();
的回报;
}


//应该如何我现在处理接收到的数据?



{
_handle.BeginReceive(_readBuffer,0,_readBuffer.Length,SocketFlags.None,AsyncReceive,NULL);
}
赶上(的ObjectDisposedException)
{
的回报;
}
赶上(例外)
{

}
}


解决方案

首先,你需要不同类型的信息进行区分。可以使用该单个字节,这将允许多达255个不同的消息类型。使该枚举和您的邮件标记(见下文)的属性:

 枚举消息类型:字节{
FirstMessage,
SecondMessage
}

类MessageAttribute:属性{
公共MessageAttribute(消息类型类型){
类型=类型;
}

公共消息类型类型{搞定;私人集; }
}



其次,你需要为你的消息紧凑的串行器。一个很好的选择是protobuf的 - 它是非常紧凑的(不序列属性名称,只值等),同时还易于使用。



 <$ C。 $ C> [消息(MessageType.FirstMessage)
[ProtoContract]
类MyFirstMessage {
[ProtoMember(1)]
公共字符串值{获得;组; }
[ProtoMember(2)]
公众诠释AnotherValue {搞定;组; }
}

[消息(MessageType.SecondMessage)
[ProtoContract]
类MySecondMessage {
[ProtoMember(1)]
公共小数东西{搞定;组; }
}



第三,你需要知道消息的长度,主叫方说你。 。使用2个或4个字节为(Int16类型和类型的Int32分别大小)



因此​​,我们的格式是:1字节 - 消息类型。 2-5字节 - 邮件大小,5-5 +大小字节 - protobuf的序列化消息。然后分三步看了你的流,定义如下:

 类MessageReader {
静态只读字典<字节,类型和GT ; _typesMap =新词典<字节,类型和GT;();
静态MessageReader(){
//初始化地图
//这是根据您的应用程序
的foreach(在Assembly.GetExecutingAssembly VAR类型()的生命周期只执行一次。GetTypes ()。凡(C => c.GetCustomAttribute< MessageAttribute>()!= NULL)){
VAR消息= type.GetCustomAttribute< MessageAttribute>();
_typesMap.Add((字节)message.Type,类型);
}
}

公共异步任务<对象>阅读(流流){
//这是您的网络,或者你有
//读取第一个字节任何其他流 - 这是消息类型
变种firstBuf =新的字节[1];
如果(!等待stream.ReadAsync(firstBuf,0,1)= 1){
//无法读取 - 流
返回NULL结束;
}

变种类型= firstBuf [0];
如果{
//未知的消息,不知怎么处理
返回NULL(_typesMap.ContainsKey(型)!);
}
//读取接下来的4个字节 - 消息
变种lengthBuf =新的字节[4]的长度;
如果(等待stream.ReadAsync(lengthBuf,0,4)= 4!){
//读取小于预期 - EOF
返回NULL;
}
变种长度= BitConverter.ToInt32(lengthBuf,0);
//检查,如果长度是不是太大了!或者用2个字节长度为,如果你的邮件允许
如果(长度大于1 * 1024 * 1024){
// - 例如按需要调整
返回NULL;
}
变种messageBuf =新的字节[长度]
如果(等待stream.ReadAsync(messageBuf,0,长度)=长度!){
//没看过完整的邮件 - EOF
返回NULL;
}
尝试{
返回ProtoBuf.Serializer.NonGeneric.Deserialize(_typesMap【类型】,新的MemoryStream(messageBuf));
}
抓{
//处理无效的消息不知何故
返回NULL;
}
}
}

在你阅读一个消息流 - 继续以同样的方式来阅读下一条消息。读取调用将被阻塞,直到新的数据到达。如果有协议的违反 - 点连接


I'm building a server / client application and I'm looking through options for separating packets. I've read that the most proper one would be creating a header that contains information on how big the payload is and then read until it ends.

How does that programmatically works?

Also separating those by using "\n" newline. A proper example would be nice.

I'm async receiving data this way:

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)
        {

        }
    }

解决方案

First you need to distinguish between different types of messages. You can use single byte for that, which will allow for up to 255 different message types. Make an enum for that, and an attribute to mark your messages (see below):

enum MessageType : byte {
    FirstMessage,
    SecondMessage
}

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

    public MessageType Type { get; private set; }
}

Second, you need compact serializer for your messages. One good option is protobuf - it it's very compact (does not serialize property names, only values and so on) while still easy to use.

[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; }
}

Third you need to know the length of a message, as caller says you. Use 2 or 4 bytes for that (size of Int16 and Int32 types respectively).

So our format would be: 1 byte - message type. 2-5 bytes - message size, 5-5+size bytes - protobuf serialized message. Then read your stream in three steps, as defined below:

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;
        }
    }
}

After you read one message from stream - continue in the same way to read next message. Read calls will block until new data arrives. If there is any violation of protocol - drop connection.

这篇关于如何正确使用单独的数据包套接字流? C#的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆