Posts Tagged ‘WCF’

ProgressStream: A Stream with Read and Write events

January 19th, 2010

In my experience with WCF I’ve implemented several solutions that leverage the message streaming capabilities present in the framework. Specifically in a client/server scenario when either side needs to transfer a file to the other side it is really convenient to be able to just pass a FileStream to a WCF service call and have the server read bytes from it and write them to disk as it pleases.

However, in this exact scenario, while the server can monitor and control how the bytes are read from the stream and track the progress, the client has no inherent way of determining how much of the file has been read across the network by the server. It would be really useful if the client could track this upload progress and display a progress bar or percentage to the user.

Well, I’ve created one of probably many possible solutions to this dilemma. I’ve written a class that inherits from Stream and exposes some events that are raised when bytes are read or written to/from the stream. These events provide information for how many bytes were read from the stream, what the current position is in the stream, and what the stream’s total length is.

The ProgressStream, as I call it, accepts any Stream object and encapsulates it, forwarding all regular stream calls to the underlying object. With this you can use the ProgressStream in conjunction with any other stream in .Net and leverage the added functionality.

Well enough explanation, here’s the code:

ProgressStream.cs (C#)
ProgressStream.vb (VB.Net)

And here’s a simple example in C# of how to use it:

using System;
using System.IO;
using CGS;
 
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var test = File.OpenRead(@"C:\Test\Test.doc");
 
            var pStream = new ProgressStream(test);
            pStream.BytesRead += 
                new ProgressStreamReportDelegate(pStream_BytesRead);
 
            int bSize = 4320;
            byte[] buffer = new byte[bSize];
            while (pStream.Read(buffer, 0, bSize) > 0) { }
 
            Console.ReadKey();
        }
 
        static void pStream_BytesRead(object sender, 
                                      ProgressStreamReportEventArgs args)
        {
            Console.WriteLine(
                string.Format("{2} bytes moved | {0} of {1} total", 
                    args.StreamPosition, 
                    args.StreamLength, 
                    args.BytesMoved
                )
           );
        }
    }
}

WCF: Net.Tcp Binding on all IP Addresses

April 1st, 2009

In my new adventures in WCF I’ve come across this need to have the Service Host listening on all available IP Addresses on the server. I fumbled around with this for about a day before discovering the answer, which is really quite simple.

My first attempt at doing this was to create the Service Host and add a new binding for each available address, all bindings listening on the same port. This caused an exception to be thrown whenever the Service Host attempted to bind to the second address.

 So here’s the incorrect code:

public static void StartHosts()
{
    try
    {
        // Create a new host
        ServiceHost host = new ServiceHost(typeof(ServerTasks));
 
        List<IPAddress> ips = new List<IPAddress>(
            Dns.GetHostAddresses(Dns.GetHostName()));
 
        if (IPAddress.Loopback != null)
            ips.Add(IPAddress.Loopback);
 
        ips.RemoveAll(
            i => i.AddressFamily != AddressFamily.InterNetwork
            );
 
        foreach (var ip in ips)
        {
            string uri = string.Empty;
 
            // Formulate the uri for this host
            uri = string.Format(
                "net.tcp://{0}:{1}/ServerTasks",
                ip.ToString(),
                ServerSettings.Instance.TCPListeningPort
            );
 
 
            // Add the endpoint binding
            host.AddServiceEndpoint(
                typeof(ServerTasks),
                new NetTcpBinding(SecurityMode.Transport) 
                { TransferMode = TransferMode.Streamed },
                uri
            );
 
        }
 
 
 
        // Add the meta data publishing
        var smb = 
            host.Description.Behaviors.Find<ServiceMetadataBehavior>();
        if (smb == null)
            smb = new ServiceMetadataBehavior();
 
        smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
        host.Description.Behaviors.Add(smb);
 
        host.AddServiceEndpoint(
            ServiceMetadataBehavior.MexContractName,
            MetadataExchangeBindings.CreateMexTcpBinding(),
            "net.tcp://localhost/ServerTasks/mex"
        );
 
        // Run the host
        host.Open();
    }
    catch (Exception exc)
    {
        DebugLogger.WriteException(exc);
    }
}

What I didn’t realize is that all I needed to do was tell the Service Host to bind to the IP of 0 or 0.0.0.0 (thanks to seakingii and Chris for pointing this out below) and WCF would automatically bind to all addresses on the machine (unless you tell it not to). This was such a wonderful and amazing discovery that I just had to share it online!

 Here’s the correct code:

public static void StartHosts()
{
    try
    {
        // Formulate the uri for this host
        string uri = string.Format(
            "net.tcp://0:{1}/ServerTasks",
            ServerSettings.Instance.TCPListeningPort
        );
 
        // Create a new host
        ServiceHost host = 
            new ServiceHost(typeof(ServerTasks), new Uri(uri));
 
        // Add the endpoint binding
        host.AddServiceEndpoint(
            typeof(ServerTasks),
            new NetTcpBinding(SecurityMode.Transport)
            {
                TransferMode = TransferMode.Streamed
            },
            uri
        );
 
        // Add the meta data publishing
        var smb = 
            host.Description.Behaviors.Find<ServiceMetadataBehavior>();
        if (smb == null)
            smb = new ServiceMetadataBehavior();
 
        smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
        host.Description.Behaviors.Add(smb);
 
        host.AddServiceEndpoint(
            ServiceMetadataBehavior.MexContractName,
            MetadataExchangeBindings.CreateMexTcpBinding(),
            "net.tcp://localhost/ServerTasks/mex"
        );
 
        // Run the host
        host.Open();
    }
    catch (Exception exc)
    {
        DebugLogger.WriteException(exc);
    }
}