Hi Peter (and others)

 

I eventually wrote my own poor man's BitTorrent to allow upload of large
files from the SL4 client to the WCF service. It turned out to be quite
straightforward and reasonably neat to code. In a nutshell it goes like
this:

 

Server Side

 

int ReceiveChunk(TransferChunk chunk)

 

The chunk contains the transfer Guid id, filename, offset, length and total
bytes. So if the client sends a 8MB file in 256KB chunks then it makes 32
calls with the same id and filename, changing the offset and length. The
service has a Dictionary<Guid, Torrent> to track the arrival of pieces and
when the total bytes have arrived in the full size receiving buffer it saves
the file and removes it from the dictionary. I put a timestamp on each entry
to know if it's gone stale and can be removed.

 

Client Side

 

It turns out the tricky bit was getting the client to send the chunks on the
right thread with a delay between each send. I knew that the client should
not send a flood of asynchronous calls for fear of a bottleneck or crash, so
I wanted a 0.5 second delay between each send. The delay was a guess and a
bit of a hack, but it works nicely. The SL control gets a file from an
OpenFileDialog and passes the selected FileInfo, name and length to the
controller. I've pasted below the skeleton of the controller method so you
can see how I read the file on a worker thread and send the chunks with
delays on the UI thread. I've stripped out UI callbacks and other
marshalling clutter.

 

Cheers,

Greg

 

public void UploadFile(Dispatcher dispatcher, FileInfo file, string name,
long length)

{

    ThreadPool.QueueUserWorkItem((o) =>

    {

        Guid key = Guid.NewGuid();

        using (Stream s = file.OpenRead())

        {

            using (BinaryReader br = new BinaryReader(s))

            {

                for (int offset = 0; ; offset += chunkLength)

                {

                    TransferChunk chunk = new TransferChunk();

                    chunk.Key = key;

                    chunk.TotalBytes = length;

                    byte[] buffer = new byte[chunkLength];

                    int readCount = br.Read(buffer, 0, chunkLength);

                    if (readCount == 0)

                    {

                        break;

                    }

                    chunk.Offset = offset;

                    chunk.Length = readCount;

                    chunk.Buffer = buffer;

                    chunk.Filename = name;

                    dispatcher.BeginInvoke(() =>

                    {

                        wcfproxy.SendChunk(chunk, (c, e, r) => { /* ignore
*/ });

                    });

                    Thread.Sleep(500);

                }

            }

        }

    }, marshalled parameters);

}

 

 

Reply via email to