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.

Shayki Abramczyk
  • 36,824
  • 16
  • 89
  • 114
Teamol
  • 733
  • 1
  • 14
  • 42
  • 2
    You can´t use Span with async methods, that´s what memory 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. – C. Gonzalez Sep 06 '18 at 19:32
  • I have a C code which returns guint8 *data; gsize size; – Teamol Sep 07 '18 at 02:20
  • 2
    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](https://github.com/dotnet/corefx/pull/22988)), but that's not asynchronous -- and they can't be, due to the stack-based nature of `Span`. – Jeroen Mostert Sep 14 '18 at 16:41
  • 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. – Jeroen Mostert Sep 14 '18 at 16:47

3 Answers3

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
        }
    }
}
brichins
  • 3,825
  • 2
  • 39
  • 60
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?

shingo
  • 18,436
  • 5
  • 23
  • 42
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.

SammuelMiranda
  • 420
  • 4
  • 29