C# Using span with SocketAsyncEventArgs
Asked Answered
D

3

11

I would like to use new Span to send unmanaged data straight to the socket using SocketAsyncEventArgs but it seems that SocketAsyncEventArgs can only accept Memory<byte> which cannot be initialized with byte * or IntPtr.

So please is there a way to do use span with SocketAsyncEventArgs?

Thank you for your help.

Dowel answered 6/9, 2018 at 18:53 Comment(4)
You can´t use Span with async methods, that´s what memory<T> is for. You don´t say how you hrt you unmsnsgrd data so it´s hard to help... There are Marshal functions that return a Memory<> that you could use.Baynebridge
I have a C code which returns guint8 *data; gsize size;Dowel
If you want to send unmanaged data "directly", then you don't want to use SocketAsyncEventArgs. The whole point of that class is to serve as a unit in a managed object pool, to cut down on allocations. It tries for the same goal as Span, but in a different way (at a time when Span did not yet exist). To blast unmanaged data directly to a socket, you'd need Span-enabled versions of the regular methods (and that's worked on here), but that's not asynchronous -- and they can't be, due to the stack-based nature of Span.Kester
It is, in theory, possible to build a new kind of SocketAsyncEventArgs infrastructure that can work with unmanaged memory -- you know, just because we don't have enough ways to interact with sockets yet. :-P I don't see that interoperating very well with the current SocketAsyncEventArgs, though.Kester
A
4

As already mentioned in the comments, Span is the wrong tool here - have you looked at using Memory instead? As you stated, the SetBuffer method does accept that as a parameter - is there a reason you can't use it?

See also this article for a good explanation on how stack vs heap allocation applies to Span and Memory. It includes this example, using a readonly Memory<Foo> buffer:

public struct Enumerable : IEnumerable<Foo>
{
    readonly Stream stream;

    public Enumerable(Stream stream)
    {
        this.stream = stream;
    }

    public IEnumerator<Foo> GetEnumerator() => new Enumerator(this);

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    public struct Enumerator : IEnumerator<Foo>
    {
        static readonly int ItemSize = Unsafe.SizeOf<Foo>();

        readonly Stream stream;
        readonly Memory<Foo> buffer;
        bool lastBuffer;
        long loadedItems;
        int currentItem;

        public Enumerator(Enumerable enumerable)
        {
            stream = enumerable.stream;
            buffer = new Foo[100]; // alloc items buffer
            lastBuffer = false;
            loadedItems = 0;
            currentItem = -1;
        }

        public Foo Current => buffer.Span[currentItem];

        object IEnumerator.Current => Current;

        public bool MoveNext()
        {
            if (++currentItem != loadedItems) // increment current position and check if reached end of buffer
                return true;
            if (lastBuffer) // check if it was the last buffer
                return false;

            // get next buffer
            var rawBuffer = MemoryMarshal.Cast<Foo, byte>(buffer);
            var bytesRead = stream.Read(rawBuffer);
            lastBuffer = bytesRead < rawBuffer.Length;
            currentItem = 0;
            loadedItems = bytesRead / ItemSize;
            return loadedItems != 0;
        }

        public void Reset() => throw new NotImplementedException();

        public void Dispose()
        {
            // nothing to do
        }
    }
}
Agio answered 18/9, 2018 at 16:29 Comment(0)
L
0

You should copy the data to managed memory first, use Marshal or Buffer class.

If not think about when the C code delete the returned pointer what will happen to the send data?

Lew answered 17/9, 2018 at 3:34 Comment(0)
P
0

There's a complete example (and implementation of the class) on the MSDN page for the SocketAsyncEventArgs (just follow the link). It shows the proper use of the class and may give you the guidence you're looking for.

Also, as Shingo said, it should all be in managed code, not in pointers.

Polygenesis answered 21/9, 2018 at 11:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.