0

I am trying to replace the stream i get from TcpClient.GetStream() by a stream I create from a string.

I am using the following method to create said Stream:

public Stream GenerateStreamFromString(string s)
{
    MemoryStream stream = new MemoryStream();
    StreamWriter writer = new StreamWriter(stream);
    writer.Write(s);
    writer.Flush();
    stream.Position = 0;
    return stream;
}

However this stream is read by using Stream.Read() somewhere in a library i do not WANT to change. The problem is I create this stream with an empty string because the object needs a stream to start up, normally when using the TcpClient stream it would stop at Stream.Read() until it would have things to read but not with the stream i created from my string.

So my question, how do I create an empty stream to which i can later add data from a string?

Vincent
  • 1,497
  • 1
  • 21
  • 44
  • 1
    How is this not just a duplicate of [your previous question](http://stackoverflow.com/questions/30376297/create-a-stream-from-a-string-which-could-replace-the-tcpclient-stream)? – Damien_The_Unbeliever May 22 '15 at 07:36
  • 2
    @Damien_The_Unbeliever Because now the other question has been deleted :-) – xanatos May 22 '15 at 07:48
  • @Damien_The_Unbeliever this question is different since it it better worded, more compact and it actually did what i wanted: generate proper answers as opposed to my previous question which I have deleted because i do realize it is a dupe. – Vincent May 22 '15 at 08:27
  • But that's what the [edit] option is for - if you're not getting the answers you want, you should edit your question to improve it - not post a new one. – Damien_The_Unbeliever May 22 '15 at 08:28
  • Yes @Damien_The_Unbeliever I have tried that with different question but because of the shear amount of questions the old questions just get drowned in new ones so i wanted to try this and it worked like a charm. Is this something that is not allowed? – Vincent May 22 '15 at 08:31
  • It's not recommended - when you make an edit to your question, it should be moved back up the lists - (but you shouldn't *abuse* that mechanic). Posting duplicate questions (even if you delete the older ones) adds more noise. And, had you not already deleted your question I would have posted my answer to your older one and already have closed this as a dupe. – Damien_The_Unbeliever May 22 '15 at 08:33
  • See also [What should I do if no one answers my question?](http://stackoverflow.com/help/no-one-answers) – Damien_The_Unbeliever May 22 '15 at 08:37
  • I will refrain from doing this in the future @Damien_The_Unbeliever sorry. – Vincent May 22 '15 at 08:39

1 Answers1

2

Using internally a BlockingCollection<> as a queue, you could write something like:

public class WatitingStream : Stream
{
    private BlockingCollection<byte[]> Packets = new BlockingCollection<byte[]>();

    private byte[] IncompletePacket;
    private int IncompletePacketOffset;

    public WatitingStream()
    {
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            Packets.CompleteAdding();
        }

        base.Dispose(disposing);
    }

    public override bool CanRead
    {
        get { return Packets.IsCompleted; }
    }

    public override bool CanSeek
    {
        get { return false; }
    }

    public override bool CanWrite
    {
        get { return Packets.IsAddingCompleted; }
    }

    public override void Flush()
    {
    }

    public override long Length
    {
        get
        {
            throw new NotSupportedException();
        }
    }

    public override long Position
    {
        get
        {
            throw new NotSupportedException();
        }
        set
        {
            throw new NotSupportedException();
        }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        if (count == 0)
        {
            return 0;
        }

        byte[] packet;
        int packetOffset;

        if (IncompletePacket != null)
        {
            packet = IncompletePacket;
            packetOffset = IncompletePacketOffset;
        }
        else
        {
            if (Packets.IsCompleted)
            {
                return 0;
            }

            packet = Packets.Take();
            packetOffset = 0;
        }

        int read = Math.Min(packet.Length - packetOffset, count);
        Buffer.BlockCopy(packet, packetOffset, buffer, offset, read);

        packetOffset += read;

        if (packetOffset < packet.Length)
        {
            IncompletePacket = packet;
            IncompletePacketOffset = packetOffset;
        }
        else
        {
            IncompletePacket = null;
            IncompletePacketOffset = 0;
        }

        return read;
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotSupportedException();
    }

    public override void SetLength(long value)
    {
        throw new NotSupportedException();
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        if (count == 0)
        {
            return;
        }

        byte[] packet = new byte[count];
        Buffer.BlockCopy(buffer, offset, packet, 0, count);
        Packets.Add(packet);
    }
}

You use it as a normal stream. The Write doesn't block. The Read blocks.

Some decisions had to be taken: this Stream is "packet" based. It won't Write zero-length packets, and a Read will return the data of one packet. The Read won't continue on the next packet. If there is data remaining in a packet after a Read, that data is saved for the next Read. The Dispose() will stop the Write (so if the "client" does a Dispose() before the "server", the server will get an exception if it tries to do a Write). If the "server" does the Dispose() first, the "client" can finish reading the packets still present. Clearly it is possible (easy) to split this class in two classes (one Server and one Client), where the Server keeps the BlockingCollection<> and the Client has a reference to the "server". This would solve the "Dispose()" anomaly/problem (but would double the code size :-) )

xanatos
  • 109,618
  • 12
  • 197
  • 280